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