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