Fixed HTTP-client query-string handling bug.
[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     if((curpos = transfer->localpos - socktqueuesize(transfer->datapipe)) < 0)
146         curpos = 0;
147     if(curpos != transfer->curpos) {
148         transfer->curpos = curpos;
149         CBCHAINDOCB(transfer, trans_p, transfer);
150     }
151 }
152
153 static void dataread(struct socket *sk, struct transfer *transfer)
154 {
155     void *buf;
156     size_t blen;
157     
158     if((transfer->localend != NULL) && (sockqueueleft(transfer->localend) > 0)) {
159         buf = sockgetinbuf(sk, &blen);
160         if((transfer->endpos >= 0) && (transfer->curpos + blen > transfer->endpos))
161             blen = transfer->endpos - transfer->curpos;
162         sockqueue(transfer->localend, buf, blen);
163         free(buf);
164         transfer->curpos += blen;
165         bytesdownload += blen;
166         CBCHAINDOCB(transfer, trans_p, transfer);
167     }
168 }
169
170 static void localwrite(struct socket *sk, struct transfer *transfer)
171 {
172     if(transfer->datapipe != NULL)
173         dataread(transfer->datapipe, transfer);
174 }
175
176 static void datawrite(struct socket *sk, struct transfer *transfer)
177 {
178     if(transfer->localend != NULL)
179         localread(transfer->localend, transfer);
180 }
181
182 static void localerr(struct socket *sk, int errno, struct transfer *transfer)
183 {
184     if(transfer->datapipe != NULL)
185         closesock(transfer->datapipe);
186 }
187
188 static void dataerr(struct socket *sk, int errno, struct transfer *transfer)
189 {
190     if(transfer->dir == TRNSD_DOWN) {
191         if(transfer->curpos >= transfer->size) {
192             transfersetstate(transfer, TRNS_DONE);
193             if(transfer->localend != NULL) {
194                 closesock(transfer->localend);
195                 quitsock(transfer->localend);
196                 transfer->localend = NULL;
197             }
198         } else {
199             resettransfer(transfer);
200         }
201     } else if(transfer->dir == TRNSD_UP) {
202         transfer->close = 1;
203     }
204 }
205
206 void transferattach(struct transfer *transfer, struct socket *dpipe)
207 {
208     transferdetach(transfer);
209     getsock(transfer->datapipe = dpipe);
210     dpipe->readcb = (void (*)(struct socket *, void *))dataread;
211     dpipe->writecb = (void (*)(struct socket *, void *))datawrite;
212     dpipe->errcb = (void (*)(struct socket *, int, void *))dataerr;
213     dpipe->data = transfer;
214 }
215
216 void transferdetach(struct transfer *transfer)
217 {
218     if(transfer->datapipe != NULL) {
219         closesock(transfer->datapipe);
220         quitsock(transfer->datapipe);
221     }
222     transfer->datapipe = NULL;
223 }
224
225 struct transfer *finddownload(wchar_t *peerid)
226 {
227     struct transfer *transfer;
228
229     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
230     {
231         if((transfer->dir == TRNSD_DOWN) && (transfer->datapipe == NULL) && !wcscmp(peerid, transfer->peerid))
232             break;
233     }
234     return(transfer);
235 }
236
237 struct transfer *hasupload(struct fnet *fnet, wchar_t *peerid)
238 {
239     struct transfer *transfer;
240     
241     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
242     {
243         if((transfer->dir == TRNSD_UP) && (transfer->fnet == fnet) && !wcscmp(transfer->peerid, peerid))
244             break;
245     }
246     return(transfer);
247 }
248
249 struct transfer *newupload(struct fnetnode *fn, struct fnet *fnet, wchar_t *nickid, struct socket *dpipe)
250 {
251     struct transfer *transfer;
252     
253     transfer = newtransfer();
254     if(fnet != NULL)
255         transfer->fnet = fnet;
256     else
257         transfer->fnet = fn->fnet;
258     transfer->peerid = swcsdup(nickid);
259     transfer->state = TRNS_HS;
260     transfer->dir = TRNSD_UP;
261     if(fn != NULL)
262         getfnetnode(transfer->fn = fn);
263     transferattach(transfer, dpipe);
264     linktransfer(transfer);
265     bumptransfer(transfer);
266     return(transfer);
267 }
268
269 void linktransfer(struct transfer *transfer)
270 {
271     transfer->next = transfers;
272     transfer->prev = NULL;
273     if(transfers != NULL)
274         transfers->prev = transfer;
275     transfers = transfer;
276     GCBCHAINDOCB(newtransfercb, transfer);
277 }
278
279 void resettransfer(struct transfer *transfer)
280 {
281     if(transfer->dir == TRNSD_DOWN)
282     {
283         transferdetach(transfer);
284         killfilter(transfer);
285         transfersetstate(transfer, TRNS_WAITING);
286         transfersetactivity(transfer, L"reset");
287         return;
288     }
289 }
290
291 struct transfer *findtransfer(int id)
292 {
293     struct transfer *transfer;
294     
295     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
296     {
297         if(transfer->id == id)
298             break;
299     }
300     return(transfer);
301 }
302
303 static void transexpire(int cancelled, struct transfer *transfer)
304 {
305     transfer->etimer = NULL;
306     if(!cancelled)
307         bumptransfer(transfer);
308     else
309         transfer->timeout = 0;
310 }
311
312 void transferprepul(struct transfer *transfer, off_t size, off_t start, off_t end, struct socket *lesk)
313 {
314     transfersetsize(transfer, size);
315     transfer->curpos = transfer->localpos = start;
316     transfer->endpos = end;
317     transfersetlocalend(transfer, lesk);
318 }
319
320 void transferstartdl(struct transfer *transfer, struct socket *sk)
321 {
322     transfersetstate(transfer, TRNS_MAIN);
323     socksettos(sk, confgetint("transfer", "dltos"));
324 }
325
326 void transferstartul(struct transfer *transfer, struct socket *sk)
327 {
328     transfersetstate(transfer, TRNS_MAIN);
329     socksettos(sk, confgetint("transfer", "ultos"));
330     if(transfer->localend != NULL)
331         localread(transfer->localend, transfer);
332 }
333
334 void transfersetlocalend(struct transfer *transfer, struct socket *sk)
335 {
336     if(transfer->localend != NULL)
337         putsock(transfer->localend);
338     getsock(transfer->localend = sk);
339     sk->data = transfer;
340     sk->readcb = (void (*)(struct socket *, void *))localread;
341     sk->writecb = (void (*)(struct socket *, void *))localwrite;
342     sk->errcb = (void (*)(struct socket *, int, void *))localerr;
343 }
344
345 static int tryreq(struct transfer *transfer)
346 {
347     struct fnetnode *fn;
348     struct fnetpeer *peer;
349     
350     if((fn = transfer->fn) != NULL)
351     {
352         if(fn->state != FNN_EST)
353         {
354             transfer->close = 1;
355             return(1);
356         }
357         peer = fnetfindpeer(fn, transfer->peerid);
358     } else {
359         peer = NULL;
360         for(fn = fnetnodes; fn != NULL; fn = fn->next)
361         {
362             if((fn->state == FNN_EST) && (fn->fnet == transfer->fnet) && ((peer = fnetfindpeer(fn, transfer->peerid)) != NULL))
363                 break;
364         }
365     }
366     if(peer != NULL)
367     {
368         time(&transfer->lastreq);
369         return(fn->fnet->reqconn(peer));
370     }
371     return(1);
372 }
373
374 void trytransferbypeer(struct fnet *fnet, wchar_t *peerid)
375 {
376     struct transfer *transfer;
377     
378     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
379     {
380         if((transfer->dir == TRNSD_DOWN) && (transfer->state == TRNS_WAITING))
381         {
382             if((transfer->fnet == fnet) && !wcscmp(transfer->peerid, peerid))
383             {
384                 if(!tryreq(transfer))
385                     return;
386             }
387         }
388     }
389 }
390
391 void bumptransfer(struct transfer *transfer)
392 {
393     time_t now;
394     
395     if((now = time(NULL)) < transfer->timeout)
396     {
397
398         if(transfer->etimer == NULL)
399             transfer->etimer = timercallback(transfer->timeout, (void (*)(int, void *))transexpire, transfer);
400         return;
401     }
402     if(transfer->etimer != NULL)
403         canceltimer(transfer->etimer);
404     switch(transfer->state)
405     {
406     case TRNS_WAITING:
407         transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 30), (void (*)(int, void *))transexpire, transfer);
408         if(now - transfer->lastreq > 30)
409             tryreq(transfer);
410         break;
411     case TRNS_HS:
412         if(transfer->dir == TRNSD_UP)
413         {
414             if(now - transfer->activity < 60)
415                 transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 60), (void (*)(int, void *))transexpire, transfer);
416             else
417                 transfer->close = 1;
418         } else if(transfer->dir == TRNSD_DOWN) {
419             if(now - transfer->activity < 60)
420                 transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 60), (void (*)(int, void *))transexpire, transfer);
421             else
422                 resettransfer(transfer);
423         }
424         break;
425     case TRNS_MAIN:
426         if(transfer->dir == TRNSD_UP)
427         {
428             if(now - transfer->activity < 300)
429                 transfer->etimer = timercallback(transfer->timeout = (time(NULL) + 300), (void (*)(int, void *))transexpire, transfer);
430             else
431                 transfer->close = 1;
432         }
433         break;
434     }
435 }
436
437 void transfersetactivity(struct transfer *transfer, wchar_t *desc)
438 {
439     time(&transfer->activity);
440     if(desc != NULL)
441     {
442         if(transfer->actdesc != NULL)
443             free(transfer->actdesc);
444         transfer->actdesc = swcsdup(desc);
445     }
446     bumptransfer(transfer);
447     CBCHAINDOCB(transfer, trans_act, transfer);
448 }
449
450 void transfersetstate(struct transfer *transfer, int newstate)
451 {
452     transfer->state = newstate;
453     if(transfer->etimer != NULL)
454         canceltimer(transfer->etimer);
455     transfersetactivity(transfer, NULL);
456     CBCHAINDOCB(transfer, trans_ac, transfer, L"state");
457 }
458
459 void transfersetnick(struct transfer *transfer, wchar_t *newnick)
460 {
461     if(transfer->peernick != NULL)
462         free(transfer->peernick);
463     transfer->peernick = swcsdup(newnick);
464     CBCHAINDOCB(transfer, trans_ac, transfer, L"nick");
465 }
466
467 void transfersetsize(struct transfer *transfer, off_t newsize)
468 {
469     transfer->size = newsize;
470     CBCHAINDOCB(transfer, trans_ac, transfer, L"size");
471 }
472
473 void transferseterror(struct transfer *transfer, int error)
474 {
475     transfer->error = error;
476     CBCHAINDOCB(transfer, trans_ac, transfer, L"error");
477 }
478
479 void transfersetpath(struct transfer *transfer, wchar_t *path)
480 {
481     if(transfer->path != NULL)
482         free(transfer->path);
483     transfer->path = swcsdup(path);
484     CBCHAINDOCB(transfer, trans_ac, transfer, L"path");
485 }
486
487 void transfersethash(struct transfer *transfer, struct hash *hash)
488 {
489     if(transfer->hash != NULL)
490         freehash(transfer->hash);
491     transfer->hash = hash;
492     CBCHAINDOCB(transfer, trans_ac, transfer, L"hash");
493 }
494
495 int slotsleft(void)
496 {
497     struct transfer *transfer;
498     int slots;
499     
500     slots = confgetint("transfer", "slots");
501     for(transfer = transfers; (transfer != NULL) && (slots > 0); transfer = transfer->next)
502     {
503         if((transfer->dir == TRNSD_UP) && (transfer->state == TRNS_MAIN) && !transfer->flags.b.minislot)
504             slots--;
505     }
506     return(slots);
507 }
508
509 static void killfilter(struct transfer *transfer)
510 {
511     if(transfer->filter != -1)
512     {
513         kill(-transfer->filter, SIGHUP);
514         transfer->filter = -1;
515     }
516     if(transfer->localend)
517     {
518         transfer->localend->readcb = NULL;
519         transfer->localend->writecb = NULL;
520         transfer->localend->errcb = NULL;
521         putsock(transfer->localend);
522         transfer->localend = NULL;
523     }
524     if(transfer->filterout)
525     {
526         transfer->filterout->readcb = NULL;
527         putsock(transfer->filterout);
528         transfer->filterout = NULL;
529     }
530     if(transfer->filterbuf)
531     {
532         free(transfer->filterbuf);
533         transfer->filterbuf = NULL;
534     }
535     transfer->filterbufsize = transfer->filterbufdata = 0;
536 }
537
538 static void handletranscmd(struct transfer *transfer, wchar_t *cmd, wchar_t *arg)
539 {
540     if(!wcscmp(cmd, L"status")) {
541         if(arg == NULL)
542             arg = L"";
543         if(transfer->exitstatus != NULL)
544             free(transfer->exitstatus);
545         transfer->exitstatus = swcsdup(arg);
546     }
547 }
548
549 static void filterread(struct socket *sk, struct transfer *transfer)
550 {
551     char *buf, *p, *p2;
552     size_t bufsize;
553     wchar_t *cmd, *arg;
554     
555     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
556         return;
557     bufcat(transfer->filterbuf, buf, bufsize);
558     free(buf);
559     while((p = memchr(transfer->filterbuf, '\n', transfer->filterbufdata)) != NULL)
560     {
561         *(p++) = 0;
562         if((p2 = strchr(transfer->filterbuf, ' ')) != NULL)
563             *(p2++) = 0;
564         if((cmd = icmbstowcs(transfer->filterbuf, NULL)) != NULL)
565         {
566             arg = NULL;
567             if(p2 != NULL)
568             {
569                 if((arg = icmbstowcs(p2, NULL)) == NULL)
570                     flog(LOG_WARNING, "filter sent a string which could not be converted into the local charset: %s: %s", p2, strerror(errno));
571             }
572             handletranscmd(transfer, cmd, arg);
573             CBCHAINDOCB(transfer, trans_filterout, transfer, cmd, arg);
574             if(arg != NULL)
575                 free(arg);
576             free(cmd);
577         } else {
578             flog(LOG_WARNING, "filter sent a string which could not be converted into the local charset: %s: %s", transfer->filterbuf, strerror(errno));
579         }
580         memmove(transfer->filterbuf, p, transfer->filterbufdata -= (p - transfer->filterbuf));
581     }
582 }
583
584 static void filterexit(pid_t pid, int status, void *data)
585 {
586     struct transfer *transfer;
587     struct fnet *fnet;
588     wchar_t *peerid;
589     
590     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
591     {
592         if(transfer->filter == pid)
593         {
594             transfer->filter = -1;
595             killfilter(transfer);
596             fnet = transfer->fnet;
597             peerid = swcsdup(transfer->peerid);
598             if(WEXITSTATUS(status))
599                 resettransfer(transfer);
600             else
601                 freetransfer(transfer);
602             trytransferbypeer(fnet, peerid);
603             free(peerid);
604             break;
605         }
606     }
607 }
608
609 int forkfilter(struct transfer *transfer)
610 {
611     char *filtername, *filename, *peerid, *buf, *p;
612     wchar_t *wfilename;
613     struct passwd *pwent;
614     pid_t pid;
615     int inpipe, outpipe;
616     char **argv;
617     size_t argvsize, argvdata;
618     struct socket *insock, *outsock;
619     struct wcspair *ta;
620     char *rec, *val;
621
622     wfilename = fnfilebasename(transfer->path);
623     if(transfer->auth == NULL)
624     {
625         flog(LOG_WARNING, "tried to fork filter for transfer with NULL authhandle (tranfer %i)", transfer->id);
626         errno = EACCES;
627         return(-1);
628     }
629     if((pwent = getpwuid(transfer->owner)) == NULL)
630     {
631         flog(LOG_WARNING, "no passwd entry for uid %i (found in transfer %i)", transfer->owner, transfer->id);
632         errno = EACCES;
633         return(-1);
634     }
635     filtername = findfile("dc-filter", pwent->pw_dir, 0);
636     if(filtername == NULL)
637         filtername = findfile(icswcstombs(confgetstr("transfer", "filter"), NULL, NULL), NULL, 0);
638     if(filtername == NULL)
639     {
640         flog(LOG_WARNING, "could not find filter for user %s", pwent->pw_name);
641         errno = ENOENT;
642         return(-1);
643     }
644     if((filename = icwcstombs(wfilename, NULL)) == NULL)
645     {
646         if((buf = icwcstombs(wfilename, "UTF-8")) == NULL)
647         {
648             flog(LOG_WARNING, "could convert transfer filename to neither local charset nor UTF-8: %s", strerror(errno));
649             return(-1);
650         }
651         filename = sprintf2("utf8-%s", buf);
652         free(buf);
653     }
654     if((peerid = icwcstombs(transfer->peerid, NULL)) == NULL)
655     {
656         if((buf = icwcstombs(transfer->peerid, "UTF-8")) == NULL)
657         {
658             flog(LOG_WARNING, "could convert transfer peerid to neither local charset nor UTF-8: %s", strerror(errno));
659             free(filename);
660             return(-1);
661         }
662         peerid = sprintf2("utf8-%s", buf);
663         free(buf);
664     }
665     for(p = filename; *p; p++) {
666         if(*p == '/')
667             *p = '_';
668         else if((p == filename) && (*p == '.'))
669             *p = '_';
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("%ji", (intmax_t)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->key, 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 /*     sockblock(insock, 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     /*
740     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
741     {
742         if((transfer->endpos >= 0) && (transfer->state == TRNS_MAIN) && (transfer->localend != NULL) && (transfer->localend->state == SOCK_EST) && (transfer->curpos >= transfer->endpos))
743         {
744             if((transfer->iface != NULL) && (transfer->iface->endofdata != NULL))
745                 transfer->iface->endofdata(transfer, transfer->ifacedata);
746             closesock(transfer->localend);
747         }
748     }
749     */
750     for(transfer = transfers; transfer != NULL; transfer = next)
751     {
752         next = transfer->next;
753         if(transfer->close)
754         {
755             transferdetach(transfer);
756             freetransfer(transfer);
757             continue;
758         }
759     }
760     return(0);
761 }
762
763 static struct configvar myvars[] =
764 {
765     /** The maximum number of simultaneously permitted uploads. A
766      * common hub rule is that you will need at least as many slots as
767      * the number of hubs to which you are connected. */
768     {CONF_VAR_INT, "slots", {.num = 3}},
769     /** The TOS value to use for upload connections (see the TOS
770      * VALUES section). */
771     {CONF_VAR_INT, "ultos", {.num = SOCK_TOS_MAXTP}},
772     /** The TOS value to use for download connections (see the TOS
773      * VALUES section). */
774     {CONF_VAR_INT, "dltos", {.num = SOCK_TOS_MAXTP}},
775     /** The name of the filter script (see the FILES section for
776      * lookup information). */
777     {CONF_VAR_STRING, "filter", {.str = L"dc-filter"}},
778     /** If true, only one upload is allowed per remote peer. This
779      * option is still experimental, so it is recommended to leave it
780      * off. */
781     {CONF_VAR_BOOL, "ulquota", {.num = 0}},
782     {CONF_VAR_END}
783 };
784
785 static struct module me =
786 {
787     .conf =
788     {
789         .vars = myvars
790     },
791     .name = "transfer",
792     .run = run
793 };
794
795 MODULE(me);