Send hash to filter.
[doldaconnect.git] / daemon / transfer.c
1 /*
2  *  Dolda Connect - Modular multiuser Direct Connect-style client
3  *  Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4  *  
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *  
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *  
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 #include <stdlib.h>
20 #include <string.h>
21 #include <time.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <errno.h>
28 #include <sys/wait.h>
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 #include "log.h"
34 #include "utils.h"
35 #include "sysevents.h"
36 #include "auth.h"
37 #include "transfer.h"
38 #include "module.h"
39 #include "client.h"
40
41 static void killfilter(struct transfer *transfer);
42
43 struct transfer *transfers = NULL;
44 int numtransfers = 0;
45 GCBCHAIN(newtransfercb, struct transfer *);
46
47 void freetransfer(struct transfer *transfer)
48 {
49     struct transarg *ta;
50     
51     if(transfer == transfers)
52         transfers = transfer->next;
53     if(transfer->next != NULL)
54         transfer->next->prev = transfer->prev;
55     if(transfer->prev != NULL)
56         transfer->prev->next = transfer->next;
57     CBCHAINDOCB(transfer, trans_destroy, transfer);
58     CBCHAINFREE(transfer, trans_ac);
59     CBCHAINFREE(transfer, trans_act);
60     CBCHAINFREE(transfer, trans_p);
61     CBCHAINFREE(transfer, trans_destroy);
62     CBCHAINFREE(transfer, trans_filterout);
63     while((ta = transfer->args) != NULL)
64     {
65         transfer->args = ta->next;
66         free(ta->rec);
67         free(ta->val);
68         free(ta);
69     }
70     if(transfer->filter != -1)
71         killfilter(transfer);
72     if(transfer->etimer != NULL)
73         canceltimer(transfer->etimer);
74     if(transfer->auth != NULL)
75         authputhandle(transfer->auth);
76     if(transfer->peerid != NULL)
77         free(transfer->peerid);
78     if(transfer->peernick != NULL)
79         free(transfer->peernick);
80     if(transfer->path != NULL)
81         free(transfer->path);
82     if(transfer->actdesc != NULL)
83         free(transfer->actdesc);
84     if(transfer->filterbuf != NULL)
85         free(transfer->filterbuf);
86     if(transfer->hash != NULL)
87         freehash(transfer->hash);
88     if(transfer->localend != NULL)
89     {
90         transfer->localend->readcb = NULL;
91         transfer->localend->writecb = NULL;
92         transfer->localend->errcb = NULL;
93         putsock(transfer->localend);
94     }
95     if(transfer->filterout != NULL)
96     {
97         transfer->filterout->readcb = NULL;
98         transfer->filterout->writecb = NULL;
99         transfer->filterout->errcb = NULL;
100         putsock(transfer->filterout);
101     }
102     if(transfer->fn != NULL)
103         putfnetnode(transfer->fn);
104     free(transfer);
105     numtransfers--;
106 }
107
108 struct transfer *newtransfer(void)
109 {
110     struct transfer *new;
111     static int curid = 0;
112     
113     new = smalloc(sizeof(*new));
114     memset(new, 0, sizeof(*new));
115     new->id = curid++;
116     new->size = -1;
117     new->endpos = -1;
118     new->filter = -1;
119     CBCHAININIT(new, trans_ac);
120     CBCHAININIT(new, trans_act);
121     CBCHAININIT(new, trans_p);
122     CBCHAININIT(new, trans_destroy);
123     CBCHAININIT(new, trans_filterout);
124     new->next = NULL;
125     new->prev = NULL;
126     time(&new->activity);
127     numtransfers++;
128     return(new);
129 }
130
131 void transferaddarg(struct transfer *transfer, wchar_t *rec, wchar_t *val)
132 {
133     struct transarg *ta;
134     
135     ta = smalloc(sizeof(*ta));
136     ta->rec = swcsdup(rec);
137     ta->val = swcsdup(val);
138     ta->next = transfer->args;
139     transfer->args = ta;
140 }
141
142 void transferattach(struct transfer *transfer, struct transferiface *iface, void *data)
143 {
144     if(transfer->iface != NULL)
145         transferdetach(transfer);
146     transfer->iface = iface;
147     transfer->ifacedata = data;
148 }
149
150 void transferdetach(struct transfer *transfer)
151 {
152     if(transfer->iface != NULL)
153     {
154         transfer->iface->detach(transfer, transfer->ifacedata);
155         transfer->iface = NULL;
156         transfer->ifacedata = NULL;
157     }
158 }
159
160 struct transfer *finddownload(wchar_t *peerid)
161 {
162     struct transfer *transfer;
163
164     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
165     {
166         if((transfer->dir == TRNSD_DOWN) && (transfer->iface == NULL) && !wcscmp(peerid, transfer->peerid))
167             break;
168     }
169     return(transfer);
170 }
171
172 struct transfer *newupload(struct fnetnode *fn, struct fnet *fnet, wchar_t *nickid, struct transferiface *iface, void *data)
173 {
174     struct transfer *transfer;
175     
176     transfer = newtransfer();
177     if(fnet != NULL)
178         transfer->fnet = fnet;
179     else
180         transfer->fnet = fn->fnet;
181     transfer->peerid = swcsdup(nickid);
182     transfer->state = TRNS_HS;
183     transfer->dir = TRNSD_UP;
184     if(fn != NULL)
185         getfnetnode(transfer->fn = fn);
186     transferattach(transfer, iface, data);
187     linktransfer(transfer);
188     bumptransfer(transfer);
189     return(transfer);
190 }
191
192 void linktransfer(struct transfer *transfer)
193 {
194     transfer->next = transfers;
195     transfer->prev = NULL;
196     if(transfers != NULL)
197         transfers->prev = transfer;
198     transfers = transfer;
199     GCBCHAINDOCB(newtransfercb, transfer);
200 }
201
202 void resettransfer(struct transfer *transfer)
203 {
204     if(transfer->dir == TRNSD_DOWN)
205     {
206         if(transfer->iface != NULL)
207             transferdetach(transfer);
208         killfilter(transfer);
209         transfersetstate(transfer, TRNS_WAITING);
210         transfersetactivity(transfer, L"reset");
211         return;
212     }
213 }
214
215 struct transfer *findtransfer(int id)
216 {
217     struct transfer *transfer;
218     
219     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
220     {
221         if(transfer->id == id)
222             break;
223     }
224     return(transfer);
225 }
226
227 static void transexpire(int cancelled, struct transfer *transfer)
228 {
229     transfer->etimer = NULL;
230     if(!cancelled)
231         bumptransfer(transfer);
232     else
233         transfer->timeout = 0;
234 }
235
236 static void transferread(struct socket *sk, struct transfer *transfer)
237 {
238     if(sockgetdatalen(sk) >= 65536)
239         sk->ignread = 1;
240     if((transfer->iface != NULL) && (transfer->iface->gotdata != NULL))
241         transfer->iface->gotdata(transfer, transfer->ifacedata);
242 }
243
244 static void transferwrite(struct socket *sk, struct transfer *transfer)
245 {
246     if((transfer->iface != NULL) && (transfer->iface->wantdata != NULL))
247         transfer->iface->wantdata(transfer, transfer->ifacedata);
248 }
249
250 static void transfererr(struct socket *sk, int errno, struct transfer *transfer)
251 {
252     if((transfer->iface != NULL) && (transfer->iface->endofdata != NULL))
253         transfer->iface->endofdata(transfer, transfer->ifacedata);
254 }
255
256 void transferputdata(struct transfer *transfer, void *buf, size_t size)
257 {
258     time(&transfer->activity);
259     sockqueue(transfer->localend, buf, size);
260     transfer->curpos += size;
261     CBCHAINDOCB(transfer, trans_p, transfer);
262 }
263
264 void transferendofdata(struct transfer *transfer)
265 {
266     if(transfer->curpos >= transfer->size)
267     {
268         transfersetstate(transfer, TRNS_DONE);
269         transfer->localend->readcb = NULL;
270         transfer->localend->writecb = NULL;
271         transfer->localend->errcb = NULL;
272         putsock(transfer->localend);
273         transfer->localend = NULL;
274     } else {
275         resettransfer(transfer);
276     }
277 }
278
279 size_t transferdatasize(struct transfer *transfer)
280 {
281     return(sockqueuesize(transfer->localend));
282 }
283
284 void *transfergetdata(struct transfer *transfer, size_t *size)
285 {
286     void *buf;
287     
288     if(transfer->localend == NULL)
289         return(NULL);
290     transfer->localend->ignread = 0;
291     time(&transfer->activity);
292     if((buf = sockgetinbuf(transfer->localend, size)) == NULL)
293         return(NULL);
294     if((transfer->endpos >= 0) && (transfer->curpos + *size >= transfer->endpos))
295     {
296         *size = transfer->endpos - transfer->curpos;
297         buf = srealloc(buf, *size);
298     }
299     transfer->curpos += *size;
300     CBCHAINDOCB(transfer, trans_p, transfer);
301     return(buf);
302 }
303
304 void transferprepul(struct transfer *transfer, size_t size, size_t start, size_t end, struct socket *lesk)
305 {
306     transfersetsize(transfer, size);
307     transfer->curpos = start;
308     transfer->endpos = end;
309     lesk->ignread = 1;
310     transfersetlocalend(transfer, lesk);
311 }
312
313 void transferstartdl(struct transfer *transfer, struct socket *sk)
314 {
315     transfersetstate(transfer, TRNS_MAIN);
316     socksettos(sk, confgetint("transfer", "dltos"));
317 }
318
319 void transferstartul(struct transfer *transfer, struct socket *sk)
320 {
321     transfersetstate(transfer, TRNS_MAIN);
322     socksettos(sk, confgetint("transfer", "ultos"));
323     if(transfer->localend != NULL)
324         transfer->localend->ignread = 0;
325 }
326
327 void transfersetlocalend(struct transfer *transfer, struct socket *sk)
328 {
329     if(transfer->localend != NULL)
330         putsock(transfer->localend);
331     getsock(transfer->localend = sk);
332     sk->data = transfer;
333     sk->readcb = (void (*)(struct socket *, void *))transferread;
334     sk->writecb = (void (*)(struct socket *, void *))transferwrite;
335     sk->errcb = (void (*)(struct socket *, int, void *))transfererr;
336 }
337
338 void bumptransfer(struct transfer *transfer)
339 {
340     struct fnetnode *fn;
341     struct fnetpeer *peer;
342     time_t now;
343     
344     if((now = time(NULL)) < transfer->timeout)
345     {
346         if(transfer->etimer == NULL)
347             transfer->etimer = timercallback(transfer->timeout, (void (*)(int, void *))transexpire, transfer);
348         return;
349     }
350     if(transfer->etimer != NULL)
351         canceltimer(transfer->etimer);
352     switch(transfer->state)
353     {
354     case TRNS_WAITING:
355         if(transfer->fn != NULL)
356         {
357             fn = transfer->fn;
358             if(fn->state != FNN_EST)
359             {
360                 transfer->close = 1;
361                 return;
362             }
363             peer = fnetfindpeer(fn, transfer->peerid);
364         } else {
365             peer = NULL;
366             for(fn = fnetnodes; fn != NULL; fn = fn->next)
367             {
368                 if((fn->state == FNN_EST) && (fn->fnet == transfer->fnet) && ((peer = fnetfindpeer(fn, transfer->peerid)) != NULL))
369                     break;
370             }
371         }
372         transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 30), (void (*)(int, void *))transexpire, transfer);
373         if(now - transfer->lastreq > 30)
374         {
375             if(peer != NULL)
376             {
377                 fn->fnet->reqconn(peer);
378                 time(&transfer->lastreq);
379             }
380         }
381         break;
382     case TRNS_HS:
383         if(transfer->dir == TRNSD_UP)
384         {
385             if(now - transfer->activity < 60)
386                 transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 60), (void (*)(int, void *))transexpire, transfer);
387             else
388                 transfer->close = 1;
389         } else if(transfer->dir == TRNSD_DOWN) {
390             if(now - transfer->activity < 60)
391                 transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 60), (void (*)(int, void *))transexpire, transfer);
392             else
393                 resettransfer(transfer);
394         }
395         break;
396     case TRNS_MAIN:
397         if(transfer->dir == TRNSD_UP)
398         {
399             if(now - transfer->activity < 300)
400                 transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 300), (void (*)(int, void *))transexpire, transfer);
401             else
402                 transfer->close = 1;
403         }
404         break;
405     }
406 }
407
408 void transfersetactivity(struct transfer *transfer, wchar_t *desc)
409 {
410     time(&transfer->activity);
411     if(desc != NULL)
412     {
413         if(transfer->actdesc != NULL)
414             free(transfer->actdesc);
415         transfer->actdesc = swcsdup(desc);
416     }
417     bumptransfer(transfer);
418     CBCHAINDOCB(transfer, trans_act, transfer);
419 }
420
421 void transfersetstate(struct transfer *transfer, int newstate)
422 {
423     transfer->state = newstate;
424     if(transfer->etimer != NULL)
425         canceltimer(transfer->etimer);
426     transfersetactivity(transfer, NULL);
427     CBCHAINDOCB(transfer, trans_ac, transfer, L"state");
428 }
429
430 void transfersetnick(struct transfer *transfer, wchar_t *newnick)
431 {
432     if(transfer->peernick != NULL)
433         free(transfer->peernick);
434     transfer->peernick = swcsdup(newnick);
435     CBCHAINDOCB(transfer, trans_ac, transfer, L"nick");
436 }
437
438 void transfersetsize(struct transfer *transfer, int newsize)
439 {
440     transfer->size = newsize;
441     CBCHAINDOCB(transfer, trans_ac, transfer, L"size");
442 }
443
444 void transferseterror(struct transfer *transfer, int error)
445 {
446     transfer->error = error;
447     CBCHAINDOCB(transfer, trans_ac, transfer, L"error");
448 }
449
450 void transfersetpath(struct transfer *transfer, wchar_t *path)
451 {
452     if(transfer->path != NULL)
453         free(transfer->path);
454     transfer->path = swcsdup(path);
455     CBCHAINDOCB(transfer, trans_ac, transfer, L"path");
456 }
457
458 void transfersethash(struct transfer *transfer, struct hash *hash)
459 {
460     if(transfer->hash != NULL)
461         freehash(transfer->hash);
462     transfer->hash = hash;
463     CBCHAINDOCB(transfer, trans_ac, transfer, L"hash");
464 }
465
466 int slotsleft(void)
467 {
468     struct transfer *transfer;
469     int slots;
470     
471     slots = confgetint("transfer", "slots");
472     for(transfer = transfers; (transfer != NULL) && (slots > 0); transfer = transfer->next)
473     {
474         if((transfer->dir == TRNSD_UP) && (transfer->state == TRNS_MAIN) && !transfer->flags.b.minislot)
475             slots--;
476     }
477     return(slots);
478 }
479
480 static void killfilter(struct transfer *transfer)
481 {
482     if(transfer->filter != -1)
483     {
484         kill(-transfer->filter, SIGHUP);
485         transfer->filter = -1;
486     }
487     if(transfer->localend)
488     {
489         transfer->localend->readcb = NULL;
490         transfer->localend->writecb = NULL;
491         transfer->localend->errcb = NULL;
492         putsock(transfer->localend);
493         transfer->localend = NULL;
494     }
495     if(transfer->filterout)
496     {
497         transfer->filterout->readcb = NULL;
498         putsock(transfer->filterout);
499         transfer->filterout = NULL;
500     }
501     if(transfer->filterbuf)
502     {
503         free(transfer->filterbuf);
504         transfer->filterbuf = NULL;
505     }
506     transfer->filterbufsize = transfer->filterbufdata = 0;
507 }
508
509 static char *findfilter(struct passwd *pwd)
510 {
511     char *path, *filtername;
512
513     if((path = sprintf2("%s/.dcdl-filter", pwd->pw_dir)) != NULL)
514     {
515         if(!access(path, X_OK))
516             return(path);
517         free(path);
518     }
519     if((filtername = icwcstombs(confgetstr("transfer", "filter"), NULL)) == NULL)
520     {
521         flog(LOG_WARNING, "could not convert filter name into local charset: %s", strerror(errno));
522     } else {
523         if(strchr(filtername, '/') == NULL)
524         {
525             if((path = sprintf2("/etc/%s", filtername)) != NULL)
526             {
527                 if(!access(path, X_OK))
528                 {
529                     free(filtername);
530                     return(path);
531                 }
532                 free(path);
533             }
534             if((path = sprintf2("/usr/etc/%s", filtername)) != NULL)
535             {
536                 if(!access(path, X_OK))
537                 {
538                     free(filtername);
539                     return(path);
540                 }
541                 free(path);
542             }
543             if((path = sprintf2("/usr/local/etc/%s", filtername)) != NULL)
544             {
545                 if(!access(path, X_OK))
546                 {
547                     free(filtername);
548                     return(path);
549                 }
550                 free(path);
551             }
552         } else {
553             if(!access(filtername, X_OK))
554                 return(filtername);
555         }
556         free(filtername);
557     }
558     return(NULL);
559 }
560
561 static void filterread(struct socket *sk, struct transfer *transfer)
562 {
563     char *buf, *p, *p2;
564     size_t bufsize;
565     wchar_t *cmd, *arg;
566     
567     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
568         return;
569     bufcat(transfer->filterbuf, buf, bufsize);
570     free(buf);
571     if((p = memchr(transfer->filterbuf, '\n', transfer->filterbufdata)) != NULL)
572     {
573         *(p++) = 0;
574         if((p2 = strchr(transfer->filterbuf, ' ')) != NULL)
575             *(p2++) = 0;
576         if((cmd = icmbstowcs(transfer->filterbuf, NULL)) != NULL)
577         {
578             arg = NULL;
579             if(p2 != NULL)
580             {
581                 if((arg = icmbstowcs(p2, NULL)) == NULL)
582                     flog(LOG_WARNING, "filter sent a string which could not be converted into the local charset: %s: %s", transfer->filterbuf, strerror(errno));
583             }
584             CBCHAINDOCB(transfer, trans_filterout, transfer, cmd, arg);
585             if(arg != NULL)
586                 free(arg);
587             free(cmd);
588         } else {
589             flog(LOG_WARNING, "filter sent a string which could not be converted into the local charset: %s: %s", transfer->filterbuf, strerror(errno));
590         }
591         memmove(transfer->filterbuf, p, transfer->filterbufdata -= (p - transfer->filterbuf));
592     }
593 }
594
595 static void filterexit(pid_t pid, int status, void *data)
596 {
597     struct transfer *transfer;
598     
599     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
600     {
601         if(transfer->filter == pid)
602         {
603             transfer->filter = -1;
604             killfilter(transfer);
605             if(WEXITSTATUS(status))
606             {
607                 resettransfer(transfer);
608             } else {
609                 freetransfer(transfer);
610             }
611             break;
612         }
613     }
614 }
615
616 int forkfilter(struct transfer *transfer)
617 {
618     char *filtername, *filename, *peerid, *buf;
619     wchar_t *wfilename;
620     struct passwd *pwent;
621     pid_t pid;
622     int inpipe, outpipe;
623     char **argv;
624     size_t argvsize, argvdata;
625     struct socket *insock, *outsock;
626     struct transarg *ta;
627     char *rec, *val;
628
629     wfilename = transfer->path;
630     if(transfer->fnet->filebasename != NULL)
631         wfilename = transfer->fnet->filebasename(wfilename);
632     if(transfer->auth == NULL)
633     {
634         flog(LOG_WARNING, "tried to fork filter for transfer with NULL authhandle (tranfer %i)", transfer->id);
635         errno = EACCES;
636         return(-1);
637     }
638     if((pwent = getpwuid(transfer->owner)) == NULL)
639     {
640         flog(LOG_WARNING, "no passwd entry for uid %i (found in transfer %i)", transfer->owner, transfer->id);
641         errno = EACCES;
642         return(-1);
643     }
644     if((filtername = findfilter(pwent)) == NULL)
645     {
646         flog(LOG_WARNING, "could not find filter for user %s", pwent->pw_name);
647         errno = ENOENT;
648         return(-1);
649     }
650     if((filename = icwcstombs(wfilename, NULL)) == NULL)
651     {
652         if((buf = icwcstombs(wfilename, "UTF-8")) == NULL)
653         {
654             flog(LOG_WARNING, "could convert transfer filename to neither local charset nor UTF-8: %s", strerror(errno));
655             return(-1);
656         }
657         filename = sprintf2("utf8-%s", buf);
658         free(buf);
659     }
660     if((peerid = icwcstombs(transfer->peerid, NULL)) == NULL)
661     {
662         if((buf = icwcstombs(transfer->peerid, "UTF-8")) == NULL)
663         {
664             flog(LOG_WARNING, "could convert transfer peerid to neither local charset nor UTF-8: %s", strerror(errno));
665             free(filename);
666             return(-1);
667         }
668         peerid = sprintf2("utf8-%s", buf);
669         free(buf);
670     }
671     if((pid = forksess(transfer->owner, transfer->auth, filterexit, NULL, FD_PIPE, 0, O_WRONLY, &inpipe, FD_PIPE, 1, O_RDONLY, &outpipe, FD_FILE, 2, O_RDWR, "/dev/null", FD_END)) < 0)
672     {
673         flog(LOG_WARNING, "could not fork session for filter for transfer %i: %s", transfer->id, strerror(errno));
674         return(-1);
675     }
676     if(pid == 0)
677     {
678         argv = NULL;
679         argvsize = argvdata = 0;
680         buf = sprintf2("%i", transfer->size);
681         addtobuf(argv, filtername);
682         addtobuf(argv, filename);
683         addtobuf(argv, buf);
684         addtobuf(argv, peerid);
685         if(transfer->hash)
686         {
687             if((buf = icwcstombs(unparsehash(transfer->hash), NULL)) != NULL)
688             {
689                 /* XXX: I am very doubtful of this, but it can just as
690                  * well be argued that all data should be presented as
691                  * key-value pairs. */
692                 addtobuf(argv, "hash");
693                 addtobuf(argv, buf);
694             } else {
695                 flog(LOG_WARNING, "could not convert hash to local charset");
696             }
697         }
698         for(ta = transfer->args; ta != NULL; ta = ta->next)
699         {
700             if((rec = icwcstombs(ta->rec, NULL)) == NULL)
701                 continue;
702             if((val = icwcstombs(ta->val, NULL)) == NULL)
703                 continue;
704             addtobuf(argv, rec);
705             addtobuf(argv, val);
706         }
707         addtobuf(argv, NULL);
708         execv(filtername, argv);
709         flog(LOG_WARNING, "could not exec filter %s: %s", filtername, strerror(errno));
710         exit(127);
711     }
712     insock = wrapsock(inpipe);
713     outsock = wrapsock(outpipe);
714     /* Really, really strange thing here - sometimes the kernel would
715      * return POLLIN on insock, even though it's a write-side
716      * pipe. The corresponding read on the pipe naturally returns
717      * EBADF, causing doldacond to think there's something wrong with
718      * the fd, and thus it closes it. Until I can find out whyever the
719      * kernel gives a POLLIN on the fd (if I can at all...), I'll just
720      * set ignread on insock for now. */
721     insock->ignread = 1;
722     transfer->filter = pid;
723     transfersetlocalend(transfer, insock);
724     getsock(transfer->filterout = outsock);
725     outsock->data = transfer;
726     outsock->readcb = (void (*)(struct socket *, void *))filterread;
727     putsock(insock);
728     putsock(outsock);
729     free(filtername);
730     free(filename);
731     free(peerid);
732     return(0);
733 }
734
735 static int run(void)
736 {
737     struct transfer *transfer, *next;
738     
739     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
740     {
741         if((transfer->endpos >= 0) && (transfer->state == TRNS_MAIN) && (transfer->localend != NULL) && (transfer->localend->state == SOCK_EST) && (transfer->curpos >= transfer->endpos))
742         {
743             if((transfer->iface != NULL) && (transfer->iface->endofdata != NULL))
744                 transfer->iface->endofdata(transfer, transfer->ifacedata);
745             closesock(transfer->localend);
746         }
747     }
748     for(transfer = transfers; transfer != NULL; transfer = next)
749     {
750         next = transfer->next;
751         if(transfer->close)
752         {
753             transferdetach(transfer);
754             freetransfer(transfer);
755             continue;
756         }
757     }
758     return(0);
759 }
760
761 static struct configvar myvars[] =
762 {
763     {CONF_VAR_INT, "slots", {.num = 3}},
764     {CONF_VAR_INT, "ultos", {.num = SOCK_TOS_MAXTP}},
765     {CONF_VAR_INT, "dltos", {.num = SOCK_TOS_MAXTP}},
766     {CONF_VAR_STRING, "filter", {.str = L"dc-filter"}},
767     {CONF_VAR_END}
768 };
769
770 static struct module me =
771 {
772     .conf =
773     {
774         .vars = myvars
775     },
776     .name = "transfer",
777     .run = run
778 };
779
780 MODULE(me);