Display speed for uploads as well.
[doldaconnect.git] / daemon / fnet-dc.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 <stdio.h>
21 #include <wchar.h>
22 #include <malloc.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <alloca.h>
29 #include <wctype.h>
30 #include <time.h>
31 #include <errno.h>
32 #include <bzlib.h>
33 #include <zlib.h>
34 #include <sys/stat.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39 #include "filenet.h"
40 #include "log.h"
41 #include "module.h"
42 #include "utils.h"
43 #include "client.h"
44 #include "transfer.h"
45 #include "sysevents.h"
46 #include "net.h"
47 #include "tiger.h"
48
49 /*
50  * The Direct Connect protocol is extremely ugly. Thus, this code must
51  * also be a bit ugly in certain places. Please forgive me. =/
52  *
53  * This also means that there might be some pieces of the code that
54  * look completely illogical to you, and you think that you may make
55  * them much neater/better. However, in many cases it might just be
56  * that it's required to cope with some oddity of the protocol, such
57  * as the amazingly ugly delayed key response in the peer protocol.
58  */
59
60 /* I assume this is the correct character set to use for DC,
61  * considering it was developed without i18n support under Windows */
62 #define DCCHARSET "windows-1252"
63
64 #ifdef DCPP_MASQUERADE
65 /*
66  * I honestly don't want to pretend being a client that I'm not, but
67  * there are so many hubs that simply do not accept any clients
68  * outside their whitelists, for no obvious reasons, so I feel that I
69  * am left with little choice. Anyhow, as long as I actually support
70  * all the features that my faked DC++ version does, there should be
71  * very little harm done.
72  */
73 #define DCIDTAG "++"
74 #define DCIDTAGV "0.674"
75 #define DCIDFULL "DC++ 0.674"
76 #else
77 #define DCIDTAG "Dolda"
78 #define DCIDTAGV VERSION
79 #define DCIDFULL "DoldaConnect " VERSION
80 #endif
81
82 #define PEER_CMD 0
83 #define PEER_STOP 1
84 #define PEER_TRNS 2
85 #define PEER_SYNC 3
86 #define PEER_TTHL 4
87
88 #define CPRS_NONE 0
89 #define CPRS_ZLIB 1
90
91 struct command
92 {
93     char *name;
94     void (*handler)(struct socket *sk, void *data, char *cmd, char *args);
95     int stop;
96 };
97
98 struct qcommand
99 {
100     struct qcommand *next;
101     char *string;
102 };
103
104 struct dchub
105 {
106     char *inbuf;
107     size_t inbufdata, inbufsize;
108     struct qcommand *queue;
109     int extended, dcppemu;
110     char *nativename;
111     char *nativenick;
112 };
113
114 struct dcexppeer
115 {
116     struct dcexppeer *next, *prev;
117     char *nick;
118     struct fnetnode *fn;
119     struct timer *expire;
120 };
121
122 struct dcpeer
123 {
124     struct dcpeer *next, *prev;
125     struct socket *sk;
126     struct fnetnode *fn;
127     char *inbuf;
128     size_t inbufdata, inbufsize;
129     size_t curread, totalsize;
130     int freeing;
131     struct timer *timeout;
132     struct qcommand *queue;
133     struct transfer *transfer;
134     int state;
135     int ptclose;      /* Close after transfer is complete */
136     int accepted;     /* If false, we connected, otherwise, we accepted */
137     int extended, dcppemu;
138     int direction;    /* Using the constants from transfer.h */
139     int compress;
140     int hascurpos, fetchingtthl, notthl;
141     struct tigertreehash tth;
142     void *cprsdata;
143     char *key;
144     char *nativename;
145     char **supports;
146     wchar_t *wcsname;
147 };
148
149 static struct fnet dcnet;
150 static struct transferiface dctransfer;
151 static struct socket *udpsock = NULL;
152 static struct socket *tcpsock = NULL;
153 static struct dcpeer *peers = NULL;
154 int numdcpeers = 0;
155 static struct dcexppeer *expected = NULL;
156 static char *hmlistname = NULL;
157 static char *xmllistname = NULL;
158 static char *xmlbz2listname = NULL;
159
160 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn);
161 static void freedcpeer(struct dcpeer *peer);
162 static void transread(struct socket *sk, struct dcpeer *peer);
163 static void transerr(struct socket *sk, int err, struct dcpeer *peer);
164 static void transwrite(struct socket *sk, struct dcpeer *peer);
165 static void updatehmlist(void);
166 static void updatexmllist(void);
167 static void updatexmlbz2list(void);
168 static void requestfile(struct dcpeer *peer);
169
170 static int reservedchar(unsigned char c)
171 {
172     return((c == 0) || (c == 5) || (c == 124) || (c == 96) || (c == 126) || (c == 36));
173 }
174
175 /* Oh, how I despise having to do this... */
176 static char *dcmakekey(char *lock)
177 {
178     int i, len, offset;
179     char *buf, *key;
180     char save;
181     
182     buf = smalloc(strlen(lock));
183     save = 5;
184     len = 0;
185     for(i = 0; lock[i]; i++)
186     {
187         buf[i] = lock[i] ^ save;
188         buf[i] = ((buf[i] & 0x0F) << 4) | ((buf[i] & 0xF0) >> 4);
189         save = lock[i];
190         if((i != 0) && reservedchar(buf[i]))
191             len += 10;
192         else
193             len++;
194     }
195     buf[0] ^= buf[i - 1];
196     if(reservedchar(buf[0]))
197         len += 10;
198     else
199         len++;
200     key = smalloc(len + 1);
201     offset = 0;
202     for(i = 0; lock[i] != 0; i++)
203     {
204         if(reservedchar(buf[i]))
205             offset += sprintf(key + offset, "/%%DCN%03i%%/", buf[i]);
206         else
207             key[offset++] = buf[i];
208     }
209     key[offset] = 0;
210     free(buf);
211     return(key);
212 }
213
214 static char *pathnmdc2adc(char *path)
215 {
216     char *ret;
217     size_t retsize, retdata;
218     
219     if(!strcmp(path, "files.xml") || !strcmp(path, "files.xml.bz2") || !strcmp(path, "MyList.DcLst"))
220         return(sstrdup(path));
221     ret = NULL;
222     retsize = retdata = 0;
223     addtobuf(ret, '/');
224     for(; *path; path++)
225     {
226         if(*path == '\\')
227             addtobuf(ret, '/');
228         else
229             addtobuf(ret, *path);
230     }
231     addtobuf(ret, 0);
232     return(ret);
233 }
234
235 static int isdchash(struct hash *hash)
236 {
237     if(wcscmp(hash->algo, L"TTH"))
238         return(0);
239     if(hash->len != 24)
240         return(0);
241     return(1);
242 }
243
244 static int supports(struct dcpeer *peer, char *cap)
245 {
246     char **p;
247     
248     if(peer->supports == NULL)
249         return(0);
250     for(p = peer->supports; *p != NULL; p++)
251     {
252         if(!strcasecmp(*p, cap))
253             return(1);
254     }
255     return(0);
256 }
257
258 static void endcompress(struct dcpeer *peer)
259 {
260     if(peer->compress == CPRS_ZLIB)
261     {
262         deflateEnd(peer->cprsdata);
263         free(peer->cprsdata);
264     }
265     peer->compress = CPRS_NONE;
266 }
267
268 static void initcompress(struct dcpeer *peer, int algo)
269 {
270     int ret;
271     
272     endcompress(peer);
273     peer->compress = algo;
274     if(algo == CPRS_ZLIB)
275     {
276         peer->cprsdata = smalloc(sizeof(z_stream));
277         memset(peer->cprsdata, 0, sizeof(z_stream));
278         if((ret = deflateInit(peer->cprsdata, 3)) != Z_OK)
279         {
280             flog(LOG_CRIT, "Aiya! zlib refuses to init (%i)!", ret);
281             abort();
282         }
283     }
284 }
285
286 static void unquote(wchar_t *in)
287 {
288     wchar_t *p, *p2, nc;
289     
290     for(p = in; *p != L'\0'; p++)
291     {
292         if(*p == L'&')
293         {
294             for(p2 = p + 1; (*p2 != L'\0') && (*p2 != L';') && (*p2 != L'&'); p2++);
295             if(*p2 == L'&')
296                 continue;
297             if(*p2 == L'\0')
298                 return;
299             *p2 = L'\0';
300             nc = L'\0';
301             if(!wcscmp(p + 1, L"amp"))
302             {
303                 nc = L'&';
304             } else if(p[1] == L'#') {
305                 nc = ucptowc(wcstol(p + 2, NULL, 10));
306             }
307             if(nc == L'\0')
308             {
309                 *p2 = L';';
310                 p = p2;
311                 continue;
312             }
313             *p = nc;
314             memmove(p + 1, p2 + 1, (wcslen(p2 + 1) + 1) * sizeof(wchar_t));
315         }
316     }
317 }
318
319 static void freeexppeer(struct dcexppeer *ep)
320 {
321     if(ep->next != NULL)
322         ep->next->prev = ep->prev;
323     if(ep->prev != NULL)
324         ep->prev->next = ep->next;
325     if(ep == expected)
326         expected = ep->next;
327     free(ep->nick);
328     putfnetnode(ep->fn);
329     if(ep->expire != NULL)
330         canceltimer(ep->expire);
331     free(ep);
332 }
333
334 static void exppeerexpire(int cancelled, struct dcexppeer *ep)
335 {
336     ep->expire = NULL;
337     if(!cancelled)
338         freeexppeer(ep);
339 }
340
341 static struct dcexppeer *expectpeer(char *nick, struct fnetnode *fn)
342 {
343     struct dcexppeer *ep;
344     
345     ep = smalloc(sizeof(*ep));
346     ep->nick = sstrdup(nick);
347     getfnetnode(ep->fn = fn);
348     ep->expire = timercallback(ntime() + 300, (void (*)(int, void *))exppeerexpire, ep);
349     ep->next = expected;
350     ep->prev = NULL;
351     if(expected != NULL)
352         expected->prev = ep;
353     expected = ep;
354     return(ep);
355 }
356
357 static struct qcommand *newqcmd(struct qcommand **queue, char *string)
358 {
359     struct qcommand *new;
360     
361     while(*queue != NULL)
362         queue = &(*queue)->next;
363     new = smalloc(sizeof(*new));
364     new->string = sstrdup(string);
365     new->next = *queue;
366     *queue = new;
367     return(new);
368 }
369
370 static struct qcommand *ulqcmd(struct qcommand **queue)
371 {
372     struct qcommand *qcmd;
373     
374     if((qcmd = *queue) == NULL)
375         return(NULL);
376     *queue = qcmd->next;
377     return(qcmd);
378 }
379
380 static void freeqcmd(struct qcommand *qcmd)
381 {
382     free(qcmd->string);
383     free(qcmd);
384 }
385
386 static void hubrecvchat(struct socket *sk, struct fnetnode *fn, char *from, char *string)
387 {
388     wchar_t *chat, *wfrom, *wpeer;
389     char *p, *end;
390     struct fnetpeer *peer;
391     
392     end = string + strlen(string);
393     while((p = strchr(string, 13)) != NULL)
394         memmove(p, p + 1, (end-- - p));
395     if(from != NULL)
396     {
397         if((strlen(string) > strlen(from) + 2) && (*string == '<') && !memcmp(string + 1, from, strlen(from)) && (*(string + strlen(from) + 1) == '>'))
398             string += strlen(from) + 2;
399         if((wfrom = icmbstowcs(from, DCCHARSET)) == NULL)
400             return;
401         wpeer = swcsdup(wfrom);
402     } else {
403         wfrom = NULL;
404         wpeer = NULL;
405         if(*string == '<')
406         {
407             for(p = string + 1; *p; p++)
408             {
409                 if((*p == ' ') || (*p == '>'))
410                     break;
411             }
412             if(*p == '>')
413             {
414                 *(p++) = 0;
415                 if(*p == ' ')
416                     p++;
417                 if((wpeer = icmbstowcs(string + 1, DCCHARSET)) == NULL)
418                     return;
419                 string = p;
420             }
421         }
422         if(wpeer == NULL)
423             wpeer = swcsdup(L"");
424     }
425     if((chat = icmbstowcs(string, DCCHARSET)) == NULL)
426     {
427         if(wfrom != NULL)
428             free(wfrom);
429         free(wpeer);
430         return;
431     }
432     unquote(chat);
433     if(wfrom != NULL)
434     {
435         if((peer = fnetfindpeer(fn, wfrom)) == NULL) /* Assume public chat */
436             fnethandlechat(fn, 1, wfrom, wpeer, chat);
437         else
438             fnethandlechat(fn, 0, wfrom, wpeer, chat);
439     } else {
440         fnethandlechat(fn, 1, L"", wpeer, chat);
441     }
442     if(wfrom != NULL)
443         free(wfrom);
444     free(wpeer);
445     free(chat);
446 }
447
448 static void peertimeout(int cancelled, struct dcpeer *peer)
449 {
450     peer->timeout = NULL;
451     if(cancelled)
452         return;
453     freedcpeer(peer);
454 }
455
456 static void sendadc(struct socket *sk, char *arg)
457 {
458     char *buf;
459     size_t bufsize, bufdata;
460     
461     buf = NULL;
462     bufsize = bufdata = 0;
463     addtobuf(buf, ' ');
464     for(; *arg; arg++)
465     {
466         if(*arg == ' ')
467         {
468             bufcat(buf, "\\s", 2);
469         } else if(*arg == '\n') {
470             bufcat(buf, "\\n", 2);
471         } else if(*arg == '\\') {
472             bufcat(buf, "\\\\", 2);
473         } else {
474             addtobuf(buf, *arg);
475         }
476     }
477     sockqueue(sk, buf, bufdata);
478     free(buf);
479 }
480
481 static void sendadcf(struct socket *sk, char *arg, ...)
482 {
483     char *buf;
484     va_list args;
485     
486     va_start(args, arg);
487     buf = vsprintf2(arg, args);
488     va_end(args);
489     if(buf == NULL)
490         return;
491     sendadc(sk, buf);
492     free(buf);
493 }
494
495 static char **parseadc(char *args)
496 {
497     char **retbuf;
498     size_t retbufsize, retbufdata;
499     char *buf;
500     size_t bufsize, bufdata;
501     int state;
502     
503     retbuf = NULL;
504     buf = NULL;
505     retbufsize = retbufdata = bufsize = bufdata = 0;
506     state = 0;
507     while(state != 3)
508     {
509         switch(state)
510         {
511         case 0:
512             if(*args == 0)
513                 state = 3;
514             else if(*args != ' ')
515                 state = 1;
516             break;
517         case 1:
518             if((*args == ' ') || (*args == 0))
519             {
520                 addtobuf(buf, 0);
521                 addtobuf(retbuf, buf);
522                 buf = NULL;
523                 bufsize = bufdata = 0;
524                 if(*args == 0)
525                     state = 3;
526             } else if(*args == '\\') {
527                 state = 2;
528             } else {
529                 addtobuf(buf, *args);
530             }
531             args++;
532             break;
533         case 2:
534             if(*args == 0)
535             {
536                 if(buf != NULL)
537                     free(buf);
538                 addtobuf(retbuf, NULL);
539                 freeparr(retbuf);
540                 return(NULL);
541             } else if((*args == 's') || (*args == ' ')) {
542                 addtobuf(buf, ' ');
543             } else if(*args == 'n') {
544                 addtobuf(buf, '\n');
545             } else if(*args == '\\') {
546                 addtobuf(buf, '\\');
547             }
548             args++;
549             state = 1;
550             break;
551         }
552     }
553     if(buf != NULL)
554         free(buf);
555     addtobuf(retbuf, NULL);
556     return(retbuf);
557 }
558
559 /* Macros useful in command handlers */
560 #define skipspace(s) ({if(((s) = strchr((s), ' ')) == NULL) return; else (s)++;})
561 #define qstr(sk, str) sockqueue(sk, str, strlen(str))
562 #define qstrf(sk, strandargs...) \
563 do { \
564     char *__buf__; \
565     if((__buf__ = sprintf2(strandargs)) != NULL) { \
566         sockqueue(sk, __buf__, strlen(__buf__)); \
567         free(__buf__); \
568     } \
569 } while(0)
570
571 static char *tr(char *str, char *trans)
572 {
573     char *p;
574     
575     for(; *trans; trans += 2)
576     {
577         for(p = strchr(str, trans[0]); p != NULL; p = strchr(p, trans[0]))
578             *p = trans[1];
579     }
580     return(str);
581 }
582
583 static char *getadcid(struct dcpeer *peer)
584 {
585     char *buf;
586     char *ret;
587     int isfilelist;
588     
589     if(!wcscmp(peer->transfer->path, L"files.xml") || !wcscmp(peer->transfer->path, L"files.xml.bz2") || !wcscmp(peer->transfer->path, L"MyList.DcLst"))
590         isfilelist = 1;
591     if(!isfilelist && (peer->transfer->hash != NULL) && isdchash(peer->transfer->hash) && supports(peer, "tthf"))
592     {
593         buf = base32encode(peer->transfer->hash->buf, 24);
594         ret = sprintf2("TTH/%.39s", buf);
595         free(buf);
596     } else {
597         if((buf = icwcstombs(peer->transfer->path, "UTF-8")) == NULL)
598             return(NULL);
599         ret = pathnmdc2adc(buf);
600         free(buf);
601     }
602     return(ret);
603 }
604
605
606 static int trresumecb(struct transfer *transfer, wchar_t *cmd, wchar_t *arg, struct dcpeer *peer)
607 {
608     if(!wcscmp(cmd, L"resume"))
609     {
610         if(arg == NULL)
611         {
612             flog(LOG_WARNING, "filter returned no position for \"resume\" on transfer %i", transfer->id);
613             freedcpeer(peer);
614         } else {
615             transfer->curpos = wcstol(arg, NULL, 10);
616             peer->hascurpos = 1;
617             requestfile(peer);
618         }
619         return(1);
620     }
621     return(0);
622 }
623
624 static void sendmynick(struct dcpeer *peer)
625 {
626     struct dchub *hub;
627     
628     hub = (peer->fn == NULL)?NULL:(peer->fn->data);
629     if(hub == NULL)
630         qstrf(peer->sk, "$MyNick %s|", icswcstombs(confgetstr("cli", "defnick"), DCCHARSET, "DoldaConnectUser-IN"));
631     else
632         qstrf(peer->sk, "$MyNick %s|", hub->nativenick);
633 }
634
635 static void sendpeerlock(struct dcpeer *peer)
636 {
637     if(peer->dcppemu)
638         qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DCPLUSPLUS0.674ABCABC|");
639     else
640         qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DOLDA%sABCABCABC|", VERSION);
641 }
642
643 static void sendsupports(struct dcpeer *peer)
644 {
645     if(peer->dcppemu)
646         qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG |");
647     else
648         qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG|");
649 }
650
651 static void requestfile(struct dcpeer *peer)
652 {
653     char *buf;
654     
655     if(peer->transfer->size == -1)
656     {
657         if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
658         {
659             transferseterror(peer->transfer, TRNSE_NOTFOUND);
660             freedcpeer(peer);
661             return;
662         }
663         /* The transfer will be restarted later from
664          * cmd_filelength when it detects that the sizes
665          * don't match. */
666         qstrf(peer->sk, "$Get %s$1|", buf);
667         return;
668     }
669     if((peer->transfer->hash == NULL) && !peer->notthl)
670     {
671         if(supports(peer, "adcget") && supports(peer, "tthl"))
672         {
673             qstr(peer->sk, "$ADCGET");
674             sendadc(peer->sk, "tthl");
675             if((buf = getadcid(peer)) == NULL)
676             {
677                 transferseterror(peer->transfer, TRNSE_NOTFOUND);
678                 freedcpeer(peer);
679                 return;
680             }
681             sendadc(peer->sk, buf);
682             free(buf);
683             sendadc(peer->sk, "0");
684             sendadc(peer->sk, "-1");
685             qstr(peer->sk, "|");
686             peer->fetchingtthl = 1;
687             return;
688         }
689     }
690     if(!peer->hascurpos)
691     {
692         if(forkfilter(peer->transfer))
693         {
694             flog(LOG_WARNING, "could not fork filter for transfer %i: %s", peer->transfer->id, strerror(errno));
695             freedcpeer(peer);
696             return;
697         }
698         CBREG(peer->transfer, trans_filterout, (int (*)(struct transfer *, wchar_t *, wchar_t *, void *))trresumecb, NULL, peer);
699         return;
700     }
701     if(supports(peer, "adcget"))
702     {
703         qstr(peer->sk, "$ADCGET");
704         sendadc(peer->sk, "file");
705         if((buf = getadcid(peer)) == NULL)
706         {
707             transferseterror(peer->transfer, TRNSE_NOTFOUND);
708             freedcpeer(peer);
709             return;
710         }
711         sendadc(peer->sk, buf);
712         free(buf);
713         sendadcf(peer->sk, "%i", peer->transfer->curpos);
714         sendadcf(peer->sk, "%i", peer->transfer->size - peer->transfer->curpos);
715         qstr(peer->sk, "|");
716     } else if(supports(peer, "xmlbzlist")) {
717         if((buf = icswcstombs(peer->transfer->path, "UTF-8", NULL)) == NULL)
718         {
719             transferseterror(peer->transfer, TRNSE_NOTFOUND);
720             freedcpeer(peer);
721             return;
722         }
723         qstrf(peer->sk, "$UGetBlock %i %i %s|", peer->transfer->curpos, peer->transfer->size - peer->transfer->curpos, buf);
724     } else {
725         if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
726         {
727             transferseterror(peer->transfer, TRNSE_NOTFOUND);
728             freedcpeer(peer);
729             return;
730         }
731         qstrf(peer->sk, "$Get %s$%i|", buf, peer->transfer->curpos + 1);
732     }
733 }
734
735 static void sendmyinfo(struct socket *sk, struct fnetnode *fn)
736 {
737     struct dchub *hub;
738     char *buf;
739     struct fnetnode *cfn;
740     int hn1, hn2, hn3;
741     
742     hub = fn->data;
743     qstrf(sk, "$MyINFO $ALL %s ", hub->nativenick);
744     buf = tr(icswcstombs(confgetstr("dc", "desc"), DCCHARSET, "Charset_conv_failure"), "$_|_");
745     qstrf(sk, "%s", buf);
746     hn1 = hn2 = hn3 = 0;
747     for(cfn = fnetnodes; cfn != NULL; cfn = cfn->next)
748     {
749         if((cfn->state == FNN_EST) || (cfn->state == FNN_HS))
750         {
751             if(cfn->regstatus == FNNS_OP)
752                 hn3++;
753             else if(cfn->regstatus == FNNS_REG)
754                 hn2++;
755             else
756                 hn1++;
757         }
758     }
759     qstrf(sk, "<%s V:%s,M:%c,H:%i/%i/%i,S:%i>",
760           (hub->dcppemu)?"++":"Dolda",
761           (hub->dcppemu)?"0.674":VERSION,
762           (tcpsock == NULL)?'P':'A',
763           hn1, hn2, hn3,
764           confgetint("transfer", "slots")
765           );
766     qstrf(sk, "$ $");
767     buf = tr(icswcstombs(confgetstr("dc", "speedstring"), DCCHARSET, "Charset_conv_failure"), "$_|_");
768     qstrf(sk, "%s\x01$", buf);
769     buf = tr(icswcstombs(confgetstr("dc", "email"), DCCHARSET, "Charset_conv_failure"), "$_|_");
770     qstrf(sk, "%s$", buf);
771     qstrf(sk, "%llu$|", sharesize);
772 }
773
774 static void hubhandleaction(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
775 {
776     struct dchub *hub;
777     
778     hub = fn->data;
779     if(!strcmp(cmd, "$Lock"))
780     {
781         qstrf(sk, "$ValidateNick %s|", hub->nativenick);
782     } else if(!strcmp(cmd, "$Hello")) {
783         if(fn->state == FNN_HS)
784         {
785             qstrf(sk, "$Version 1,0091|");
786             qstrf(sk, "$GetNickList|");
787             sendmyinfo(sk, fn);
788             fnetsetstate(fn, FNN_EST);
789         } else {
790             qstrf(sk, "$GetINFO %s %s|", args, hub->nativenick);
791         }
792     }
793 }
794
795 static void cmd_lock(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
796 {
797     struct dchub *hub;
798     char *key;
799     char *p;
800     
801     hub = fn->data;
802     if(!strncmp(args, "EXTENDEDPROTOCOL", 16))
803         hub->extended = 1;
804     if((p = strchr(args, ' ')) != NULL)
805         *(p++) = 0;
806     if(hub->extended)
807     {
808         if(hub->dcppemu)
809             qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock |");
810         else
811             qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock|");
812     }
813     key = dcmakekey(args);
814     qstrf(sk, "$Key %s|", key);
815     free(key);
816     hubhandleaction(sk, fn, cmd, args);
817 }
818
819 static void cmd_hubname(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
820 {
821     wchar_t *buf;
822     struct dchub *hub;
823     
824     hub = fn->data;
825     if(hub->nativename == NULL)
826         free(hub->nativename);
827     hub->nativename = sstrdup(args);
828     buf = icmbstowcs(args, DCCHARSET);
829     fnetsetname(fn, (buf == NULL)?L"Hubname conv error":buf);
830     if(buf != NULL)
831         free(buf);
832     hubhandleaction(sk, fn, cmd, args);
833 }
834
835 static void cmd_hello(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
836 {
837     wchar_t *nick;
838     struct dchub *hub;
839     
840     hub = fn->data;
841     if((nick = icmbstowcs(args, DCCHARSET)) == NULL)
842         return;
843     if(strcmp(args, hub->nativenick) && (fnetfindpeer(fn, nick) == NULL))
844         fnetaddpeer(fn, nick, nick);
845     free(nick);
846     hubhandleaction(sk, fn, cmd, args);
847 }
848
849 static void cmd_quit(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
850 {
851     wchar_t *nick;
852     struct fnetpeer *peer;
853     struct dchub *hub;
854     
855     hub = fn->data;
856     if((nick = icmbstowcs(args, DCCHARSET)) == NULL)
857         return;
858     if((peer = fnetfindpeer(fn, nick)) != NULL)
859         fnetdelpeer(peer);
860     free(nick);
861     hubhandleaction(sk, fn, cmd, args);
862 }
863
864 static void cmd_nicklist(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
865 {
866     struct dchub *hub;
867     char *p;
868     wchar_t *buf;
869     struct fnetpeer *peer, *npeer;
870     
871     hub = fn->data;
872     for(peer = fn->peers; peer != NULL; peer = peer->next)
873         peer->flags.b.delete = 1;
874     while((p = strstr(args, "$$")) != NULL)
875     {
876         *p = 0;
877         if((buf = icmbstowcs(args, DCCHARSET)) != NULL)
878         {
879             if((peer = fnetfindpeer(fn, buf)) == NULL)
880                 peer = fnetaddpeer(fn, buf, buf);
881             else
882                 peer->flags.b.delete = 0;
883             free(buf);
884             qstrf(sk, "$GetINFO %s %s|", args, hub->nativenick);
885         }
886         args = p + 2;
887     }
888     for(peer = fn->peers; peer != NULL; peer = npeer)
889     {
890         npeer = peer->next;
891         if(peer->flags.b.delete)
892             fnetdelpeer(peer);
893     }
894     hubhandleaction(sk, fn, cmd, args);
895 }
896
897 static void cmd_oplist(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
898 {
899     struct dchub *hub;
900     char *p;
901     wchar_t *buf;
902     struct fnetpeer *peer;
903     
904     hub = fn->data;
905     for(peer = fn->peers; peer != NULL; peer = peer->next)
906         peer->flags.b.op = 0;
907     while((p = strstr(args, "$$")) != NULL)
908     {
909         *p = 0;
910         if((buf = icmbstowcs(args, DCCHARSET)) != NULL)
911         {
912             if((peer = fnetfindpeer(fn, buf)) != NULL)
913                 peer->flags.b.op = 1;
914             free(buf);
915         }
916         args = p + 2;
917     }
918     hubhandleaction(sk, fn, cmd, args);
919 }
920
921 static void cmd_myinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
922 {
923     char *p, *p2;
924     wchar_t *buf, *wp, *wp2;
925     wchar_t abuf[10];
926     struct fnetpeer *peer;
927     struct dchub *hub;
928     
929     hub = fn->data;
930     p = args;
931     if(strncmp(p, "$ALL ", 5))
932         return;
933     p += 5;
934     if((p2 = strchr(p, ' ')) == NULL)
935         return;
936     *p2 = 0;
937     if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
938         return;
939     if((peer = fnetfindpeer(fn, buf)) == NULL)
940         peer = fnetaddpeer(fn, buf, buf);
941     free(buf);
942     p = p2 + 1;
943     if((p2 = strstr(p, "$ $")) == NULL)
944         return;
945     *p2 = 0;
946     if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
947         return;
948     if((wcslen(buf) > 0) && (buf[wcslen(buf) - 1] == L'>') && ((wp = wcschr(buf, L'<')) != NULL))
949     {
950         buf[wcslen(buf) - 1] = L'\0';
951         *(wp++) = L'\0';
952         if((wp2 = wcschr(wp, L' ')) != NULL)
953         {
954             *(wp2++) = L'\0';
955             fnetpeersetstr(peer, L"dc-client", wp);
956             wp = wp2;
957             do
958             {
959                 if((wp2 = wcschr(wp, L',')) != NULL)
960                     *(wp2++) = L'\0';
961                 if(wp[1] != L':')
962                     continue;
963                 swprintf(abuf, 10, L"dc-tag-%lc", wp[0]);
964                 fnetpeersetstr(peer, abuf, wp + 2);
965                 wp = wp2;
966             } while(wp2 != NULL);
967         }
968     }
969     fnetpeersetstr(peer, L"descr", buf);
970     free(buf);
971     p = p2 + 3;
972     if((p2 = strchr(p, '$')) == NULL)
973         return;
974     *(p2 - 1) = 0;
975     if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
976         return;
977     fnetpeersetstr(peer, L"dc-speed", buf);
978     free(buf);
979     p = p2 + 1;
980     if((p2 = strchr(p, '$')) == NULL)
981         return;
982     *p2 = 0;
983     if((buf = icmbstowcs(p, DCCHARSET)) == NULL)
984         return;
985     fnetpeersetstr(peer, L"email", buf);
986     free(buf);
987     p = p2 + 1;
988     if(strlen(p) < 1)
989         return;
990     fnetpeersetlnum(peer, L"share", strtoll(p, NULL, 10));
991     hubhandleaction(sk, fn, cmd, args);
992 }
993
994 /* I do not implement the fully in that I do not disconnect from the
995  * old hub. I do this since I believe that if the hub owner really
996  * wants to get rid of us, then it is his responsibility to disconnect
997  * us. */
998 static void cmd_forcemove(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
999 {
1000     struct dchub *hub;
1001     struct fnetnode *newfn;
1002     int freeargs;
1003     
1004     hub = fn->data;
1005     if(strchr(args, ':') == NULL)
1006     {
1007         args = strcpy(smalloc(strlen(args) + 5), args);
1008         strcat(args, ":411");
1009         freeargs = 1;
1010     } else {
1011         freeargs = 0;
1012     }
1013     if((newfn = fnetinitconnect(L"dc", args, NULL)) != NULL)
1014     {
1015         linkfnetnode(newfn);
1016         putfnetnode(newfn);
1017     }
1018     hubhandleaction(sk, fn, cmd, args);
1019     if(freeargs)
1020         free(args);
1021 }
1022
1023 static char *getdcpath(struct sharecache *node, size_t *retlen)
1024 {
1025     char *buf, *buf2;
1026     size_t len, len2;
1027     
1028     if(node->parent == NULL)
1029         return(NULL);
1030     if(node->parent == shareroot)
1031     {
1032         if((buf = icwcstombs(node->name, DCCHARSET)) == NULL)
1033             return(NULL);
1034         if(retlen != NULL)
1035             *retlen = strlen(buf);
1036         return(buf);
1037     } else {
1038         if((buf2 = icwcstombs(node->name, DCCHARSET)) == NULL)
1039             return(NULL);
1040         if((buf = getdcpath(node->parent, &len)) == NULL)
1041         {
1042             free(buf2);
1043             return(NULL);
1044         }
1045         len2 = strlen(buf2);
1046         buf = srealloc(buf, len + 1 + len2 + 1);
1047         buf[len++] = '\\';
1048         strcpy(buf + len, buf2);
1049         buf[len + len2] = 0;
1050         free(buf2);
1051         if(retlen != NULL)
1052             *retlen = len + len2;
1053         return(buf);
1054     }
1055 }
1056
1057 /*
1058  * This is the main share searching function for Direct Connect
1059  * peers. Feel free to optimize it if you feel the need for it. I
1060  * haven't ever seen it take any noticable CPU time anyway, so I
1061  * haven't felt that need.
1062  *
1063  * It may feel dubious to just return the directory when all terms are
1064  * satisfied rather than all the files in it, but it really benefits
1065  * everyone: Less cycles used for us, less UDP squelching for active
1066  * searchers, and less bandwidth waste for hubs when serving passive
1067  * searchers.
1068  */
1069 static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1070 {
1071     int i, done;
1072     struct dchub *hub;
1073     char *p, *p2;
1074     char *prefix, *infix, *postfix, *buf, *buf2;
1075     struct socket *dsk;
1076     struct sockaddr_in addr;
1077     struct sharecache *node;
1078     int minsize, maxsize;
1079     int dotth, buflen;
1080     int termnum, satisfied, skipcheck;
1081     int level, tersat[32];
1082     wchar_t *terms[32];
1083     char hashtth[24];
1084     
1085     hub = fn->data;
1086     if((p = strchr(args, ' ')) == NULL)
1087         return;
1088     *(p++) = 0;
1089     
1090     memset(terms, 0, sizeof(terms));
1091     prefix = infix = postfix = NULL;
1092     dsk = NULL;
1093     dotth = 0;
1094     
1095     if(!strncmp(args, "Hub:", 4))
1096     {
1097         if(!strcmp(cmd, "$MultiSearch"))
1098             goto out;
1099         if(!strcmp(args + 4, hub->nativenick))
1100             goto out;
1101         prefix = sprintf2("$SR %s ", hub->nativenick);
1102         infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
1103         postfix = sprintf2(" (%s)\005%s|", formataddress(fn->sk->remote, fn->sk->remotelen), args + 4);
1104         dsk = sk;
1105         getsock(dsk);
1106     } else {
1107         if((p2 = strchr(args, ':')) == NULL)
1108             goto out;
1109         *(p2++) = 0;
1110         addr.sin_family = AF_INET;
1111         if(!inet_aton(args, &addr.sin_addr))
1112             goto out;
1113         addr.sin_port = htons(atoi(p2));
1114         prefix = sprintf2("$SR %s ", hub->nativenick);
1115         infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
1116         postfix = sprintf2(" (%s)|", formataddress(fn->sk->remote, fn->sk->remotelen));
1117         netdgramconn(dsk = netdupsock(udpsock), (struct sockaddr *)&addr, sizeof(addr));
1118     }
1119     
1120     minsize = maxsize = -1;
1121     if(*p == 0)
1122         goto out;
1123     if(*(p++) == 'T')
1124         minsize = 0;
1125     if(*(p++) != '?')
1126         goto out;
1127     if(*p == 0)
1128         goto out;
1129     if((*(p++) == 'T') && (minsize == 0))
1130     {
1131         maxsize = 0;
1132         minsize = -1;
1133     }
1134     if(*(p++) != '?')
1135         goto out;
1136     if((p2 = strchr(p, '?')) == NULL)
1137         goto out;
1138     *(p2++) = 0;
1139     if(minsize == 0)
1140         minsize = atoi(p);
1141     if(maxsize == 0)
1142         maxsize = atoi(p);
1143     p = p2 + 1;
1144     if(*(p++) != '?')
1145         goto out;
1146     termnum = 0;
1147     p2 = p;
1148     done = 0;
1149     while(!done)
1150     {
1151         if((*p2 == 0) || (*p2 == '$'))
1152         {
1153             if(*p2 == 0)
1154                 done = 1;
1155             else
1156                 *p2 = 0;
1157             if(*p)
1158             {
1159                 if(!dotth && !strncmp(p, "TTH:", 4))
1160                 {
1161                     dotth = 1;
1162                     if((buf = base32decode(p + 4, &buflen)) == NULL)
1163                         goto out;
1164                     if(buflen != 24)
1165                         goto out;
1166                     memcpy(hashtth, buf, 24);
1167                     free(buf);
1168                 } else {
1169                     if((terms[termnum] = icmbstowcs(p, DCCHARSET)) != NULL)
1170                         termnum++;
1171                 }
1172             }
1173             p = p2 + 1;
1174             if(termnum == 32)
1175                 break;
1176         }
1177         p2++;
1178     }
1179     
1180     node = shareroot->child;
1181     level = 0;
1182     for(i = 0; i < termnum; i++)
1183         tersat[i] = -1;
1184     satisfied = 0;
1185     while(1)
1186     {
1187         skipcheck = 0;
1188         if(node->f.b.type == FILE_REG)
1189         {
1190             if((minsize >= 0) && (node->size < minsize))
1191                 skipcheck = 1;
1192             if((maxsize >= 0) && (node->size > maxsize))
1193                 skipcheck = 1;
1194         }
1195         if(!skipcheck && dotth)
1196         {
1197             if((node->f.b.type != FILE_REG) || (node->f.b.hastth && memcmp(hashtth, node->hashtth, 24)))
1198                 skipcheck = 1;
1199         }
1200         if(!skipcheck)
1201         {
1202             for(i = 0; i < termnum; i++)
1203             {
1204                 if(tersat[i] >= 0)
1205                     continue;
1206                 if(wcsexists(node->name, terms[i]))
1207                 {
1208                     tersat[i] = level;
1209                     satisfied++;
1210                 } else if(node->child == NULL) {
1211                     break;
1212                 }
1213             }
1214         }
1215         if(!skipcheck && (satisfied == termnum))
1216         {
1217             if((buf = getdcpath(node, NULL)) != NULL)
1218             {
1219                 if(node->f.b.hastth)
1220                 {
1221                     buf2 = base32encode(node->hashtth, 24);
1222                     qstrf(dsk, "%s%s\005%i%sTTH:%.39s%s", prefix, buf, node->size, infix, buf2, postfix);
1223                     free(buf2);
1224                 } else {
1225                     qstrf(dsk, "%s%s\005%i%s%s%s", prefix, buf, node->size, infix, hub->nativename, postfix);
1226                 }
1227                 free(buf);
1228             }
1229         }
1230         if((!skipcheck && (satisfied == termnum)) || (node->child == NULL))
1231         {
1232             while(node->next == NULL)
1233             {
1234                 if((node = node->parent) == shareroot)
1235                     break;
1236                 level--;
1237             }
1238             if(node == shareroot)
1239                 break;
1240             for(i = 0; i < termnum; i++)
1241             {
1242                 if(tersat[i] >= level)
1243                 {
1244                     tersat[i] = -1;
1245                     satisfied--;
1246                 }
1247             }
1248             node = node->next;
1249         } else {
1250             node = node->child;
1251             level++;
1252         }
1253     }
1254
1255     hubhandleaction(sk, fn, cmd, args);
1256     
1257  out:
1258     if(dsk != NULL)
1259         putsock(dsk);
1260     if(prefix != NULL)
1261         free(prefix);
1262     if(infix != NULL)
1263         free(infix);
1264     if(postfix != NULL)
1265         free(postfix);
1266     for(i = 0; (i < 32) && (terms[i] != NULL); i++)
1267         free(terms[i]);
1268 }
1269
1270 static void cmd_connecttome(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1271 {
1272     char *p;
1273     struct dchub *hub;
1274     struct socket *newsk;
1275     struct sockaddr_in addr;
1276     
1277     hub = fn->data;
1278     if((p = strchr(args, ' ')) == NULL)
1279         return;
1280     *(p++) = 0;
1281     if(strcmp(args, hub->nativenick))
1282         return;
1283     addr.sin_family = AF_INET;
1284     args = p;
1285     if((p = strchr(args, ':')) == NULL)
1286         return;
1287     *(p++) = 0;
1288     addr.sin_port = htons(atoi(p));
1289     if(!inet_aton(args, &addr.sin_addr))
1290         return;
1291     newsk = netcsconn((struct sockaddr *)&addr, sizeof(addr), (void (*)(struct socket *, int, void *))peerconnect, fn);
1292     getfnetnode(fn);
1293     hubhandleaction(sk, fn, cmd, args);
1294 }
1295
1296 static void sendctm(struct socket *sk, char *nick)
1297 {
1298     struct sockaddr *addr;
1299     socklen_t addrlen;
1300     
1301     if(tcpsock == NULL)
1302         return;
1303     if(sockgetremotename(tcpsock, &addr, &addrlen) < 0)
1304         return;
1305     if(addr->sa_family == AF_INET)
1306         qstrf(sk, "$ConnectToMe %s %s|", nick, formataddress(addr, addrlen));
1307     else
1308         flog(LOG_WARNING, "Direct Connect TCP socket is suddenly not AF_INET, but %i", addr->sa_family);
1309     free(addr);
1310 }
1311
1312 static void cmd_revconnecttome(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1313 {
1314     struct dchub *hub;
1315     char *p;
1316     
1317     hub = fn->data;
1318     if((p = strchr(args, ' ')) == NULL)
1319         return;
1320     *(p++) = 0;
1321     if(strcmp(p, hub->nativenick))
1322         return;
1323     sendctm(sk, args);
1324     expectpeer(args, fn);
1325     hubhandleaction(sk, fn, cmd, args);
1326 }
1327
1328 static void cmd_getnetinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1329 {
1330     struct dchub *hub;
1331     struct fnetnode *node;
1332     int numhubs;
1333     
1334     hub = fn->data;
1335     numhubs = 0;
1336     for(node = fnetnodes; node != NULL; node = node->next)
1337     {
1338         if(node->state == FNN_EST)
1339             numhubs++;
1340     }
1341     qstrf(sk, "$NetInfo %i$%i$%c$0|", confgetint("transfer", "slots"), numhubs, (tcpsock == NULL)?'P':'A');
1342     hubhandleaction(sk, fn, cmd, args);
1343 }
1344
1345 static void cmd_to(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1346 {
1347     struct dchub *hub;
1348     char *p, *p2;
1349     
1350     hub = fn->data;
1351     p = args;
1352     if((p2 = strchr(p, ' ')) == NULL)
1353         return;
1354     *(p2++) = 0;
1355     p = p2;
1356     if((p2 = strchr(p, ' ')) == NULL)
1357         return;
1358     *(p2++) = 0;
1359     if(strcmp(p, "From:"))
1360         return;
1361     p = p2;
1362     if((p2 = strstr(p, " $")) == NULL)
1363         return;
1364     *p2 = 0;
1365     p2 += 2;
1366     hubrecvchat(fn->sk, fn, p, p2);
1367     hubhandleaction(sk, fn, cmd, args);
1368 }
1369
1370 static void cmd_sr(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1371 {
1372     struct dchub *hub;
1373     char *p, *p2, *buf;
1374     char *nick, *filename, *hubname;
1375     int size, slots;
1376     size_t buflen;
1377     struct srchres *sr;
1378     wchar_t *wnick, *wfile;
1379     
1380     hub = fn->data;
1381     nick = p = args;
1382     if((p2 = strchr(p, ' ')) == NULL)
1383         return;
1384     *p2 = 0;
1385     p = p2 + 1;
1386     filename = p;
1387     if((p2 = strchr(p, 5)) == NULL)
1388         return;
1389     *p2 = 0;
1390     p = p2 + 1;
1391     if((p2 = strchr(p, ' ')) == NULL)
1392         return;
1393     *p2 = 0;
1394     size = atoi(p);
1395     p = p2 + 1;
1396     if((p2 = strchr(p, '/')) == NULL)
1397         return;
1398     *p2 = 0;
1399     slots = atoi(p);
1400     p = p2 + 1;
1401     if((p2 = strchr(p, 5)) == NULL)
1402         return;
1403     p = p2 + 1;
1404     hubname = p;
1405     if((p2 = strstr(p, " (")) == NULL)
1406         return;
1407     *p2 = 0;
1408     if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
1409         return;
1410     if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
1411     {
1412         free(wnick);
1413         return;
1414     }
1415     sr = newsrchres(&dcnet, wfile, wnick);
1416     if(sr->peernick != NULL)
1417         free(sr->peernick);
1418     sr->peernick = swcsdup(wnick);
1419     sr->size = size;
1420     sr->slots = slots;
1421     free(wfile);
1422     free(wnick);
1423     if(!strncmp(hubname, "TTH:", 4))
1424     {
1425         if((buf = base32decode(hubname + 4, &buflen)) != NULL)
1426         {
1427             if(buflen == 24)
1428                 sr->hash = newhash(L"TTH", 24, buf);
1429             free(buf);
1430         }
1431     }
1432     getfnetnode(sr->fn = fn);
1433     submitsrchres(sr);
1434     freesrchres(sr);
1435     hubhandleaction(sk, fn, cmd, args);
1436 }
1437
1438 static void cmd_usercommand(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1439 {
1440     /* Do nothing for now. */
1441 }
1442
1443 static void cmd_getpass(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1444 {
1445     struct dchub *hub;
1446     wchar_t *pw;
1447     char *mbspw;
1448     
1449     hub = fn->data;
1450     pw = wpfind(fn->args, L"password");
1451     if((pw == NULL) || ((mbspw = icwcstombs(pw, DCCHARSET)) == NULL))
1452     {
1453         killfnetnode(fn);
1454         return;
1455     }
1456     qstrf(sk, "$MyPass %s|", mbspw);
1457     free(mbspw);
1458     fn->regstatus = FNNS_REG;
1459     hubhandleaction(sk, fn, cmd, args);
1460 }
1461
1462 static void cmd_logedin(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1463 {
1464     struct dchub *hub;
1465     
1466     hub = fn->data;
1467     fn->regstatus = FNNS_OP;
1468     hubhandleaction(sk, fn, cmd, args);
1469 }
1470
1471 static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1472 {
1473     struct dcexppeer *expect;
1474     struct dchub *hub;
1475
1476     if(peer->nativename != NULL)
1477         free(peer->nativename);
1478     peer->nativename = sstrdup(args);
1479     if(peer->wcsname != NULL)
1480         free(peer->wcsname);
1481     if((peer->wcsname = icmbstowcs(peer->nativename, DCCHARSET)) == NULL)
1482     {
1483         freedcpeer(peer);
1484         return;
1485     }
1486     if(peer->accepted)
1487     {
1488         for(expect = expected; expect != NULL; expect = expect->next)
1489         {
1490             if(!strcmp(expect->nick, args))
1491                 break;
1492         }
1493         if(expect == NULL)
1494         {
1495             peer->fn = NULL;
1496         } else {
1497             hub = expect->fn->data;
1498             peer->fn = expect->fn;
1499             getfnetnode(peer->fn);
1500             peer->dcppemu = hub->dcppemu;
1501             freeexppeer(expect);
1502         }
1503     }
1504 }
1505
1506 static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1507 {
1508     char *p;
1509     int mydir;
1510     struct transfer *transfer;
1511     
1512     if((p = strchr(args, ' ')) == NULL)
1513         return;
1514     *p = 0;
1515     if(!strcmp(args, "Upload"))
1516         mydir = TRNSD_DOWN;
1517     if(!strcmp(args, "Download"))
1518         mydir = TRNSD_UP;
1519     if(peer->accepted)
1520     {
1521         if((peer->transfer == NULL) || (mydir != peer->direction))
1522         {
1523             freedcpeer(peer);
1524             return;
1525         }
1526         if(peer->direction == TRNSD_DOWN)
1527             requestfile(peer);
1528     } else {
1529         if(peer->wcsname == NULL)
1530         {
1531             freedcpeer(peer);
1532             return;
1533         }
1534         peer->direction = mydir;
1535         if(peer->direction == TRNSD_UP)
1536         {
1537             transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
1538         } else {
1539             if((transfer = finddownload(peer->wcsname)) == NULL)
1540             {
1541                 freedcpeer(peer);
1542                 return;
1543             }
1544             transferattach(transfer, &dctransfer, peer);
1545             transfersetstate(transfer, TRNS_HS);
1546         }
1547         transfersetnick(transfer, peer->wcsname);
1548         peer->transfer = transfer;
1549         if(peer->extended)
1550             sendsupports(peer);
1551         qstrf(sk, "$Direction %s %i|", (peer->direction == TRNSD_UP)?"Upload":"Download", rand() % 10000);
1552         if(peer->key != NULL)
1553             qstrf(sk, "$Key %s|", peer->key);
1554         if(peer->direction == TRNSD_DOWN)
1555             requestfile(peer);
1556     }
1557 }
1558
1559 static void cmd_peerlock(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1560 {
1561     char *p, *key;
1562     struct transfer *transfer;
1563     
1564     if((p = strchr(args, ' ')) == NULL)
1565         return;
1566     *p = 0;
1567     if(!strncmp(args, "EXTENDEDPROTOCOL", 16))
1568         peer->extended = 1;
1569     key = dcmakekey(args);
1570     if(peer->accepted)
1571     {
1572         if(peer->wcsname == NULL)
1573         {
1574             freedcpeer(peer);
1575             return;
1576         }
1577         sendmynick(peer);
1578         sendpeerlock(peer);
1579         if(peer->extended)
1580             sendsupports(peer);
1581         if((transfer = finddownload(peer->wcsname)) == NULL)
1582         {
1583             peer->direction = TRNSD_UP;
1584             transfer = newupload(peer->fn, &dcnet, peer->wcsname, &dctransfer, peer);
1585         } else {
1586             peer->direction = TRNSD_DOWN;
1587             transferattach(transfer, &dctransfer, peer);
1588             transfersetstate(transfer, TRNS_HS);
1589         }
1590         transfersetnick(transfer, peer->wcsname);
1591         peer->transfer = transfer;
1592         qstrf(sk, "$Direction %s %i|", (peer->direction == TRNSD_UP)?"Upload":"Download", rand() % 10000);
1593         qstrf(sk, "$Key %s|", key);
1594     } else {
1595         if(peer->key != NULL)
1596             free(peer->key);
1597         peer->key = key;
1598     }
1599 }
1600
1601 static void cmd_key(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1602 {
1603     /* NOP */
1604 }
1605
1606 static void startdl(struct dcpeer *peer)
1607 {
1608     if(peer->timeout != NULL)
1609         canceltimer(peer->timeout);
1610     peer->state = PEER_TRNS;
1611     transferstartdl(peer->transfer, peer->sk);
1612     peer->sk->readcb = (void (*)(struct socket *, void *))transread;
1613     peer->sk->errcb = (void (*)(struct socket *, int, void *))transerr;
1614 }
1615
1616 static void startul(struct dcpeer *peer)
1617 {
1618     if(peer->timeout != NULL)
1619         canceltimer(peer->timeout);
1620     peer->state = PEER_TRNS;
1621     transferstartul(peer->transfer, peer->sk);
1622     peer->sk->writecb = (void (*)(struct socket *, void *))transwrite;
1623 }
1624
1625 static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1626 {
1627     int size;
1628     struct transfer *transfer;
1629     
1630     if(peer->transfer == NULL)
1631     {
1632         freedcpeer(peer);
1633         return;
1634     }
1635     size = atoi(args);
1636     if(peer->transfer->size != size)
1637     {
1638         transfersetsize(peer->transfer, size);
1639         transfer = peer->transfer;
1640         freedcpeer(peer);
1641         trytransferbypeer(transfer->fnet, transfer->peerid);
1642         return;
1643     }
1644     startdl(peer);
1645     qstr(peer->sk, "$Send|");
1646 }
1647
1648 static void cmd_error(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1649 {
1650     if(peer->fetchingtthl)
1651     {
1652         peer->fetchingtthl = 0;
1653         peer->notthl = 1;
1654         requestfile(peer);
1655         return;
1656     }
1657     if((peer->transfer != NULL) && (peer->transfer->dir == TRNSD_DOWN))
1658     {
1659         transferseterror(peer->transfer, TRNSE_NOTFOUND);
1660         resettransfer(peer->transfer);
1661         return;
1662     }
1663     freedcpeer(peer);
1664 }
1665
1666 static void cmd_maxedout(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1667 {
1668     if((peer->transfer != NULL) && (peer->transfer->dir == TRNSD_DOWN))
1669     {
1670         transferseterror(peer->transfer, TRNSE_NOSLOTS);
1671         resettransfer(peer->transfer);
1672         return;
1673     }
1674     freedcpeer(peer);
1675 }
1676
1677 static struct
1678 {
1679     char *name;
1680     char **file;
1681     void (*func)(void);
1682 } lists[] =
1683 {
1684     {"MyList.DcLst", &hmlistname, updatehmlist},
1685     {"files.xml", &xmllistname, updatexmllist},
1686     {"files.xml.bz2", &xmlbz2listname, updatexmlbz2list},
1687     {NULL, NULL}
1688 };
1689
1690 static int openfilelist(char *name)
1691 {
1692     int i, fd;
1693     int errnobak;
1694     
1695     for(i = 0; lists[i].name != NULL; i++)
1696     {
1697         if(!strcmp(name, lists[i].name))
1698             break;
1699     }
1700     errno = 0;
1701     if(lists[i].name == NULL)
1702         return(-1);
1703     fd = -1;
1704     if((*lists[i].file == NULL) || ((fd = open(*lists[i].file, O_RDONLY)) < 0))
1705         lists[i].func();
1706     if((fd < 0) && ((*lists[i].file == NULL) || ((fd = open(*lists[i].file, O_RDONLY)) < 0)))
1707     {
1708         errnobak = errno;
1709         flog(LOG_ERR, "could not open filelist tempfile: %s", strerror(errno));
1710         errno = errnobak;
1711         return(-1);
1712     }
1713     return(fd);
1714 }
1715
1716 static struct sharecache *findbytth(char *tth32)
1717 {
1718     char *buf;
1719     size_t buflen;
1720     struct sharecache *node;
1721     
1722     if((buf = base32decode(tth32, &buflen)) == NULL)
1723         return(NULL);
1724     if(buflen != 24)
1725     {
1726         free(buf);
1727         return(NULL);
1728     }
1729     for(node = shareroot->child; node != NULL; node = nextscnode(node))
1730     {
1731         if(node->f.b.hastth && !memcmp(node->hashtth, buf, 24))
1732             break;
1733     }
1734     free(buf);
1735     return(node);
1736 }
1737
1738 static struct sharecache *resdcpath(char *path, char *charset, char sep)
1739 {
1740     struct sharecache *node;
1741     char *p, *p2;
1742
1743     node = shareroot;
1744     p = path;
1745     while(p != NULL)
1746     {
1747         if((p2 = strchr(p, sep)) != NULL)
1748             *(p2++) = 0;
1749         if((node = findcache(node, icsmbstowcs(p, charset, L""))) == NULL)
1750             return(NULL);
1751         p = p2;
1752     }
1753     return(node);
1754 }
1755
1756 static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1757 {
1758     int offset;
1759     char *p, *buf;
1760     wchar_t *buf2;
1761     struct sharecache *node;
1762     struct socket *lesk;
1763     int fd;
1764     struct stat sb;
1765     
1766     if(peer->transfer == NULL)
1767     {
1768         freedcpeer(peer);
1769         return;
1770     }
1771     if((p = strchr(args, '$')) == NULL)
1772     {
1773         freedcpeer(peer);
1774         return;
1775     }
1776     *(p++) = 0;
1777     if((offset = (atoi(p) - 1)) < 0)
1778     {
1779         freedcpeer(peer);
1780         return;
1781     }
1782     if(((fd = openfilelist(args)) < 0) && (errno != 0))
1783     {
1784         qstr(sk, "$Error Could not send file list|");
1785         freedcpeer(peer);
1786         return;
1787     } else if(fd >= 0) {
1788         if((buf2 = icsmbstowcs(args, DCCHARSET, NULL)) != NULL)
1789             transfersetpath(peer->transfer, buf2);
1790         peer->transfer->flags.b.minislot = 1;
1791     }
1792     if(fd < 0)
1793     {
1794         if((node = resdcpath(args, DCCHARSET, '\\')) == NULL)
1795         {
1796             qstrf(sk, "$Error File not in share|");
1797             freedcpeer(peer);
1798             return;
1799         }
1800         if((fd = opensharecache(node)) < 0)
1801         {
1802             qstrf(sk, "$Error %s|", strerror(errno));
1803             freedcpeer(peer);
1804             return;
1805         }
1806         buf = getfspath(node);
1807         if((buf2 = icsmbstowcs(buf, NULL, NULL)) == NULL)
1808             flog(LOG_WARNING, "could not convert native path into a wcs (%s): %s", buf, strerror(errno));
1809         else
1810             transfersetpath(peer->transfer, buf2);
1811         free(buf);
1812     }
1813     if(fstat(fd, &sb) < 0)
1814     {
1815         close(fd);
1816         flog(LOG_WARNING, "could not stat file %ls: %s", node->name, strerror(errno));
1817         qstrf(sk, "$Error|");
1818         freedcpeer(peer);
1819         return;
1820     }
1821     if(sb.st_size < 65536)
1822         peer->transfer->flags.b.minislot = 1;
1823     if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
1824         close(fd);
1825         qstr(sk, "$MaxedOut|");
1826         freedcpeer(peer);
1827         return;
1828     }
1829     if((offset != 0) && (lseek(fd, offset, SEEK_SET) < 0))
1830     {
1831         close(fd);
1832         qstrf(sk, "$Error Offset out of range|");
1833         freedcpeer(peer);
1834         return;
1835     }
1836     lesk = wrapsock(fd);
1837     transferprepul(peer->transfer, sb.st_size, offset, -1, lesk);
1838     putsock(lesk);
1839     qstrf(sk, "$FileLength %i|", peer->transfer->size);
1840 }
1841
1842 static void cmd_send(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1843 {
1844     if(peer->transfer == NULL)
1845     {
1846         freedcpeer(peer);
1847         return;
1848     }
1849     if(peer->transfer->localend == NULL)
1850     {
1851         freedcpeer(peer);
1852         return;
1853     }
1854     peer->ptclose = 1;
1855     startul(peer);
1856 }
1857
1858 static void cmd_supports(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1859 {
1860     int i;
1861     char *p, *p2;
1862     char **arr;
1863     size_t arrsize, arrdata;
1864     
1865     if(peer->supports != NULL)
1866     {
1867         for(i = 0; peer->supports[i] != NULL; i++)
1868             free(peer->supports[i]);
1869         free(peer->supports);
1870     }
1871     arr = NULL;
1872     arrsize = arrdata = 0;
1873     p = args;
1874     do
1875     {
1876         if((p2 = strchr(p, ' ')) != NULL)
1877             *(p2++) = 0;
1878         if(*p == 0)
1879             continue;
1880         addtobuf(arr, sstrdup(p));
1881     } while((p = p2) != NULL);
1882     addtobuf(arr, NULL);
1883     peer->supports = arr;
1884 }
1885
1886 static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1887 {
1888     int fd;
1889     char *p, *p2;
1890     int start, numbytes;
1891     char *charset, *buf;
1892     wchar_t *buf2;
1893     struct sharecache *node;
1894     struct stat sb;
1895     struct socket *lesk;
1896     
1897     if(peer->transfer == NULL)
1898     {
1899         freedcpeer(peer);
1900         return;
1901     }
1902     p = args;
1903     if((p2 = strchr(p, ' ')) == NULL)
1904     {
1905         freedcpeer(peer);
1906         return;
1907     }
1908     *(p2++) = 0;
1909     start = atoi(p);
1910     p = p2;
1911     if((p2 = strchr(p, ' ')) == NULL)
1912     {
1913         freedcpeer(peer);
1914         return;
1915     }
1916     *(p2++) = 0;
1917     numbytes = atoi(p);
1918     p = p2;
1919     if(!strcmp(cmd, "$UGetBlock") || !strcmp(cmd, "$UGetZBlock"))
1920         charset = "UTF-8";
1921     else
1922         charset = DCCHARSET;
1923     if(!strcmp(cmd, "$GetZBlock") || !strcmp(cmd, "$UGetZBlock"))
1924         initcompress(peer, CPRS_ZLIB);
1925     if(((fd = openfilelist(p)) < 0) && (errno != 0))
1926     {
1927         qstr(sk, "$Error Could not send file list|");
1928         return;
1929     } else if(fd >= 0) {
1930         if((buf2 = icsmbstowcs(args, charset, NULL)) != NULL)
1931             transfersetpath(peer->transfer, buf2);
1932         peer->transfer->flags.b.minislot = 1;
1933     }
1934     if(fd < 0)
1935     {
1936         if((node = resdcpath(p, charset, '\\')) == NULL)
1937         {
1938             qstr(sk, "$Error File not in cache|");
1939             return;
1940         }
1941         if((fd = opensharecache(node)) < 0)
1942         {
1943             qstrf(sk, "$Error %s|", strerror(errno));
1944             return;
1945         }
1946         buf = getfspath(node);
1947         if((buf2 = icsmbstowcs(buf, NULL, NULL)) == NULL)
1948             flog(LOG_WARNING, "could not convert native path into a wcs (%s): %s", buf, strerror(errno));
1949         else
1950             transfersetpath(peer->transfer, buf2);
1951         free(buf);
1952     }
1953     if(fstat(fd, &sb) < 0)
1954     {
1955         close(fd);
1956         flog(LOG_WARNING, "could not stat file %ls: %s", node->name, strerror(errno));
1957         qstr(sk, "$Error|");
1958         return;
1959     }
1960     if(sb.st_size < 65536)
1961         peer->transfer->flags.b.minislot = 1;
1962     if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
1963         close(fd);
1964         qstr(sk, "$MaxedOut|");
1965         return;
1966     }
1967     if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
1968     {
1969         close(fd);
1970         qstr(sk, "$Error Offset out of range|");
1971         return;
1972     }
1973     if((numbytes < 0) || (start + numbytes > sb.st_size))
1974         numbytes = sb.st_size - start;
1975     lesk = wrapsock(fd);
1976     transferprepul(peer->transfer, sb.st_size, start, start + numbytes, lesk);
1977     putsock(lesk);
1978     qstrf(sk, "$Sending %i|", numbytes);
1979     startul(peer);
1980 }
1981
1982 static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1983 {
1984     int i;
1985     char **argv, *buf;
1986     int start, numbytes;
1987     struct sharecache *node;
1988     struct stat sb;
1989     struct socket *lesk;
1990     wchar_t *wbuf;
1991     int fd;
1992     
1993     if(peer->transfer == NULL)
1994     {
1995         freedcpeer(peer);
1996         return;
1997     }
1998     if((argv = parseadc(args)) == NULL)
1999     {
2000         freedcpeer(peer);
2001         return;
2002     }
2003     if(parrlen(argv) < 4)
2004     {
2005         freedcpeer(peer);
2006         goto out;
2007     }
2008     start = atoi(argv[2]);
2009     numbytes = atoi(argv[3]);
2010     node = NULL;
2011     fd = -1;
2012     if(((fd = openfilelist(argv[1])) < 0) && (errno != 0))
2013     {
2014         qstr(sk, "$Error Could not send file list|");
2015         goto out;
2016     } else if(fd >= 0) {
2017         if((wbuf = icsmbstowcs(argv[1], "UTF-8", NULL)) != NULL)
2018             transfersetpath(peer->transfer, wbuf);
2019         peer->transfer->flags.b.minislot = 1;
2020     }
2021     if(fd < 0)
2022     {
2023         if(!strncmp(argv[1], "TTH/", 4))
2024         {
2025             if((node = findbytth(argv[1] + 4)) == NULL)
2026             {
2027                 qstr(sk, "$Error File not in cache|");
2028                 goto out;
2029             }
2030         } else {
2031             if((node = resdcpath((argv[1][0] == '/')?(argv[1] + 1):(argv[1]), "UTF-8", '/')) == NULL)
2032             {
2033                 qstr(sk, "$Error File not in cache|");
2034                 goto out;
2035             }
2036         }
2037         if((fd = opensharecache(node)) < 0)
2038         {
2039             qstrf(sk, "$Error %s|", strerror(errno));
2040             goto out;
2041         }
2042         buf = getfspath(node);
2043         if((wbuf = icsmbstowcs(buf, NULL, NULL)) == NULL)
2044             flog(LOG_WARNING, "could not convert native path into a wcs (%s): %s", buf, strerror(errno));
2045         else
2046             transfersetpath(peer->transfer, wbuf);
2047         free(buf);
2048     }
2049     if(!strcmp(argv[0], "file"))
2050     {
2051         for(i = 4; argv[i] != NULL; i++)
2052         {
2053             if(!strcmp(argv[i], "ZL1"))
2054                 initcompress(peer, CPRS_ZLIB);
2055         }
2056         if(fstat(fd, &sb) < 0)
2057         {
2058             flog(LOG_WARNING, "could not stat file %ls: %s", node->name, strerror(errno));
2059             qstr(sk, "$Error|");
2060             goto out;
2061         }
2062         if(sb.st_size < 65536)
2063             peer->transfer->flags.b.minislot = 1;
2064         if(!peer->transfer->flags.b.minislot && (slotsleft() < 1)) {
2065             qstr(sk, "$MaxedOut|");
2066             goto out;
2067         }
2068         if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
2069         {
2070             qstr(sk, "$Error Offset out of range|");
2071             goto out;
2072         }
2073         if((numbytes < 0) || (start + numbytes > sb.st_size))
2074             numbytes = sb.st_size - start;
2075         lesk = wrapsock(fd);
2076         transferprepul(peer->transfer, sb.st_size, start, start + numbytes, lesk);
2077         putsock(lesk);
2078         fd = -1;
2079         qstr(sk, "$ADCSND");
2080         sendadc(sk, "file");
2081         sendadc(sk, argv[1]);
2082         sendadcf(sk, "%i", start);
2083         sendadcf(sk, "%i", numbytes);
2084         if(peer->compress == CPRS_ZLIB)
2085             sendadc(sk, "ZL1");
2086         qstr(sk, "|");
2087         startul(peer);
2088     } else if(!strcmp(argv[0], "tthl")) {
2089         /*
2090          * XXX: Implement full TTHL support.
2091          *
2092          * In the meantime, this is better than nothing, since it at
2093          * least allows fetching of the TTH root if it isn't already
2094          * known.
2095          */
2096         if(node == NULL)
2097         {
2098             qstr(sk, "$Error no TTHL data for virtual files|");
2099             goto out;
2100         }
2101         qstr(sk, "$ADCSND");
2102         sendadc(sk, "tthl");
2103         sendadc(sk, argv[1]);
2104         sendadc(sk, "0");
2105         sendadc(sk, "24");
2106         qstr(sk, "|");
2107         sockqueue(sk, node->hashtth, 24);
2108     } else {
2109         qstr(sk, "$Error Namespace not implemented|");
2110         goto out;
2111     }
2112     
2113  out:
2114     if(fd >= 0)
2115         close(fd);
2116     freeparr(argv);
2117 }
2118
2119 static void handletthl(struct dcpeer *peer)
2120 {
2121     char buf[24];
2122     
2123     while(peer->inbufdata >= 24)
2124     {
2125         pushtigertree(&peer->tth, peer->inbuf);
2126         memmove(peer->inbuf, peer->inbuf + 24, peer->inbufdata -= 24);
2127         peer->curread += 24;
2128     }
2129     if(peer->curread >= peer->totalsize)
2130     {
2131         if(peer->timeout == NULL)
2132             peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
2133         peer->state = PEER_CMD;
2134         synctigertree(&peer->tth);
2135         restigertree(&peer->tth, buf);
2136         transfersethash(peer->transfer, newhash(L"TTH", 24, buf));
2137         requestfile(peer);
2138     }
2139 }
2140
2141 static void cmd_adcsnd(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
2142 {
2143     char **argv;
2144     int start, numbytes;
2145     
2146     if(peer->transfer == NULL)
2147     {
2148         freedcpeer(peer);
2149         return;
2150     }
2151     if((argv = parseadc(args)) == NULL)
2152     {
2153         freedcpeer(peer);
2154         return;
2155     }
2156     if(parrlen(argv) < 4)
2157     {
2158         freedcpeer(peer);
2159         goto out;
2160     }
2161     start = atoi(argv[2]);
2162     numbytes = atoi(argv[3]);
2163     if(!strcmp(argv[0], "tthl"))
2164     {
2165         if((start != 0) || (numbytes % 24 != 0))
2166         {
2167             /* Weird. Bail out. */
2168             freedcpeer(peer);
2169             goto out;
2170         }
2171         if(peer->timeout != NULL)
2172             canceltimer(peer->timeout);
2173         peer->state = PEER_TTHL;
2174         peer->totalsize = numbytes;
2175         peer->curread = 0;
2176         peer->fetchingtthl = 0;
2177         inittigertree(&peer->tth);
2178         handletthl(peer);
2179     } else if(!strcmp(argv[0], "file")) {
2180         if(start != peer->transfer->curpos)
2181         {
2182             freedcpeer(peer);
2183             goto out;
2184         }
2185         if(start + numbytes != peer->transfer->size)
2186         {
2187             transfersetsize(peer->transfer, start + numbytes);
2188             freedcpeer(peer);
2189             goto out;
2190         }
2191         startdl(peer);
2192         if(peer->inbufdata > 0)
2193         {
2194             sockpushdata(sk, peer->inbuf, peer->inbufdata);
2195             peer->inbufdata = 0;
2196             transread(sk, peer);
2197         }
2198     } else {
2199         /* We certainly didn't request this...*/
2200         freedcpeer(peer);
2201         goto out;
2202     }
2203     
2204  out:
2205     freeparr(argv);
2206 }
2207
2208 static void cmd_sending(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
2209 {
2210     int numbytes;
2211     
2212     if(peer->transfer == NULL)
2213     {
2214         freedcpeer(peer);
2215         return;
2216     }
2217     numbytes = atoi(args);
2218     if(peer->transfer->size - peer->transfer->curpos != numbytes)
2219     {
2220         transfersetsize(peer->transfer, peer->transfer->curpos + numbytes);
2221         freedcpeer(peer);
2222         return;
2223     }
2224     startdl(peer);
2225     if(peer->inbufdata > 0)
2226     {
2227         sockpushdata(sk, peer->inbuf, peer->inbufdata);
2228         peer->inbufdata = 0;
2229         transread(sk, peer);
2230     }
2231 }
2232
2233 /*
2234 Hub skeleton:
2235 static void cmd_(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
2236 {
2237     struct dchub *hub;
2238     
2239     hub = fn->data;
2240     hubhandleaction(sk, fn, cmd, args);
2241 }
2242
2243 Peer skeleton:
2244 static void cmd_(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
2245 {
2246 }
2247
2248 */
2249
2250 static int hubreqconn(struct fnetpeer *peer)
2251 {
2252     struct dchub *hub;
2253     char *mbsnick;
2254     
2255     if((peer->fn->state != FNN_EST) || (peer->fn->fnet != &dcnet))
2256     {
2257         errno = EINVAL;
2258         return(1);
2259     }
2260     if((hub = peer->fn->data) == NULL)
2261     {
2262         errno = EFAULT;
2263         return(1);
2264     }
2265     if((mbsnick = icwcstombs(peer->id, DCCHARSET)) == NULL)
2266         return(1); /* Shouldn't happen, of course, but who knows... */
2267     if(tcpsock != NULL)
2268     {
2269         sendctm(peer->fn->sk, mbsnick);
2270         expectpeer(mbsnick, peer->fn);
2271     } else {
2272         qstrf(peer->fn->sk, "$RevConnectToMe %s %s|", hub->nativenick, mbsnick);
2273     }
2274     free(mbsnick);
2275     return(0);
2276 }
2277
2278 static int hubsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
2279 {
2280     struct dchub *hub;
2281     char *mbsstring, *mbsto;
2282     
2283     hub = fn->data;
2284     if((mbsto = icwcstombs(to, DCCHARSET)) == NULL)
2285     {
2286         errno = EILSEQ;
2287         return(1);
2288     }
2289     if((mbsstring = icwcstombs(string, DCCHARSET)) == NULL)
2290     {
2291         errno = EILSEQ;
2292         return(1);
2293     }
2294     if((strchr(mbsto, '|') != NULL) || (strchr(mbsto, ' ') != NULL))
2295     {
2296         free(mbsto);
2297         free(mbsstring);
2298         errno = ESRCH;
2299         return(1);
2300     }
2301     if(strchr(mbsstring, '|') != NULL)
2302     {
2303         free(mbsto);
2304         free(mbsstring);
2305         errno = EILSEQ;
2306         return(1);
2307     }
2308     if(public)
2309     {
2310         if(*to == L'\0')
2311         {
2312             qstrf(fn->sk, "<%s> %s|", hub->nativenick, mbsstring);
2313         } else {
2314             qstrf(fn->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
2315         }
2316     } else {
2317         qstrf(fn->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
2318     }
2319     free(mbsto);
2320     free(mbsstring);
2321     return(0);
2322 }
2323
2324 static void findsizelimit(struct sexpr *sexpr, int *min, int *max)
2325 {
2326     int minl, maxl, minr, maxr, retmin, retmax;
2327     
2328     switch(sexpr->op)
2329     {
2330     case SOP_AND:
2331         findsizelimit(sexpr->l, &minl, &maxl);
2332         findsizelimit(sexpr->r, &minr, &maxr);
2333         retmin = (minl > minr)?minl:minr;
2334         if((maxl != -1) && (maxr != -1))
2335             retmax = (maxl < maxr)?maxl:maxr;
2336         else if(maxl != -1)
2337             retmax = maxl;
2338         else if(maxr != -1)
2339             retmax = maxr;
2340         else
2341             retmax = -1;
2342         break;
2343     case SOP_OR:
2344         findsizelimit(sexpr->l, &minl, &maxl);
2345         findsizelimit(sexpr->r, &minr, &maxr);
2346         retmin = (minl < minr)?minl:minr;
2347         if((maxl == -1) || (maxr == -1))
2348             retmax = -1;
2349         else
2350             retmax = (maxl > maxr)?maxl:maxr;
2351         break;
2352     case SOP_NOT:
2353         findsizelimit(sexpr->l, &minl, &maxl);
2354         if((minl == 0) && (maxl == -1)) /* Interval is unspecified */
2355         {
2356             retmin = 0;
2357             retmax = -1;
2358         } else if((minl == 0) && (maxl != -1)) {
2359             retmin = maxl + 1;
2360             retmax = -1;
2361         } else if((minl != 0) && (maxl == -1)) {
2362             retmin = 0;
2363             retmax = minl - 1;
2364         } else { /* This would yield two seperate intervals, which DC cannot handle */
2365             retmin = 0;
2366             retmax = -1;
2367         }
2368     case SOP_SIZELT:
2369         retmin = 0;
2370         retmax = sexpr->d.n - 1;
2371         break;
2372     case SOP_SIZEEQ:
2373         retmin = sexpr->d.n;
2374         retmax = sexpr->d.n;
2375         break;
2376     case SOP_SIZEGT:
2377         retmin = sexpr->d.n + 1;
2378         retmax = -1;
2379         break;
2380     default:
2381         retmin = 0;
2382         retmax = -1;
2383         break;
2384     }
2385     if(min != NULL)
2386         *min = retmin;
2387     if(max != NULL)
2388         *max = retmax;
2389 }
2390
2391 static struct hash *findsehash(struct sexpr *sexpr)
2392 {
2393     struct hash *h1, *h2;
2394     
2395     switch(sexpr->op)
2396     {
2397     case SOP_AND:
2398         if((h1 = findsehash(sexpr->l)) != NULL)
2399             return(h1);
2400         if((h1 = findsehash(sexpr->r)) != NULL)
2401             return(h1);
2402         break;
2403     case SOP_OR:
2404         h1 = findsehash(sexpr->l);
2405         h2 = findsehash(sexpr->r);
2406         if(hashcmp(h1, h2))
2407             return(h1);
2408         break;
2409     case SOP_HASHIS:
2410         if(!wcscmp(sexpr->d.hash->algo, L"TTH"))
2411             return(sexpr->d.hash);
2412     default:
2413         break;
2414     }
2415     return(NULL);
2416 }
2417
2418 static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
2419 {
2420     struct dchub *hub;
2421     struct wcslist *list, *cur;
2422     char *sstr, *buf, *p;
2423     size_t sstrsize, sstrdata;
2424     struct sockaddr *name;
2425     socklen_t namelen;
2426     int minsize, maxsize;
2427     struct hash *hash;
2428     
2429     hub = fn->data;
2430     if((fn->state != FNN_EST) || (fn->sk == NULL) || (fn->sk->state != SOCK_EST))
2431         return(1);
2432     list = findsexprstrs(srch->sexpr);
2433     findsizelimit(srch->sexpr, &minsize, &maxsize);
2434     hash = findsehash(srch->sexpr);
2435     if((minsize != 0) && (maxsize != -1))
2436     {
2437         /* Choose either minsize or maxsize by trying to determine
2438          * which choice will be most restrictive. The result can be
2439          * approximative at best anyway, so don't try too hard... */
2440         if((50000000 - maxsize) > (minsize - 50000000))
2441             minsize = 0;
2442         else
2443             maxsize = -1;
2444     }
2445     sstr = NULL;
2446     sstrsize = sstrdata = 0;
2447     if((hash != NULL) && (hash->len == 24))
2448     {
2449         /* Prioritize hash searches above all else */
2450         bufcat(sstr, "F?T?0?9?TTH:", 12);
2451         buf = base32encode(hash->buf, hash->len);
2452         
2453         bufcat(sstr, buf, 39);
2454         free(buf);
2455     } else {
2456         if(minsize != 0)
2457         {
2458             sizebuf2(sstr, sstrdata + 32, 1);
2459             snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%i?1?", minsize);
2460         } else if(maxsize != -1) {
2461             sizebuf2(sstr, sstrdata + 32, 1);
2462             snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%i?1?", maxsize);
2463         } else {
2464             bufcat(sstr, "F?F?0?1?", 8);
2465         }
2466         if(list != NULL)
2467         {
2468             for(cur = list; cur != NULL; cur = cur->next)
2469             {
2470                 if((buf = icwcstombs(cur->str, DCCHARSET)) == NULL)
2471                 {
2472                     /* Can't find anything anyway if the search expression
2473                      * requires characters outside DC's charset. There's
2474                      * nothing technically wrong with the search itself,
2475                      * however, so return success. This should be
2476                      * considered as an optimization. */
2477                     freesl(&list);
2478                     if(sstr != NULL)
2479                         free(sstr);
2480                     return(0);
2481                 }
2482                 if(cur != list)
2483                     addtobuf(sstr, '$');
2484                 /*
2485                  * It probably doesn't hurt if buf contains any extra
2486                  * dollar signs - it will just result in extra search
2487                  * terms, and the extraneous results will be filtered by
2488                  * the search layer anyway. It hurts if it contains any
2489                  * pipes, though, so let's sell them for money.
2490                  */
2491                 for(p = buf; *p; p++)
2492                 {
2493                     if(*p == '|')
2494                         *p = '$';
2495                 }
2496                 bufcat(sstr, buf, strlen(buf));
2497                 free(buf);
2498             }
2499         } else {
2500             /* Will match all files... :-/ */
2501             addtobuf(sstr, '.');
2502         }
2503     }
2504     addtobuf(sstr, 0);
2505     if(tcpsock != NULL)
2506     {
2507         if(sockgetremotename(udpsock, &name, &namelen) < 0)
2508         {
2509             flog(LOG_WARNING, "cannot get address of UDP socket");
2510         } else {
2511             qstrf(fn->sk, "$Search %s %s|", formataddress(name, namelen), sstr);
2512             free(name);
2513         }
2514     } else {
2515         qstrf(fn->sk, "$Search Hub:%s %s|", hub->nativenick, sstr);
2516     }
2517     free(sstr);
2518     freesl(&list);
2519     fn->lastsrch = time(NULL);
2520     return(0);
2521 }
2522
2523 #undef skipcmd
2524 #undef qstr
2525 #undef qstrf
2526
2527 #define cc(c) ((void (*)(struct socket *, void *, char *, char *))(c))
2528 struct command hubcmds[] =
2529 {
2530     {"$Lock", cc(cmd_lock)},
2531     {"$HubName", cc(cmd_hubname)},
2532     {"$Hello", cc(cmd_hello)},
2533     {"$Quit", cc(cmd_quit)},
2534     {"$NickList", cc(cmd_nicklist)},
2535     {"$OpList", cc(cmd_oplist)},
2536     {"$MyINFO", cc(cmd_myinfo)},
2537     {"$ForceMove", cc(cmd_forcemove)},
2538     {"$Search", cc(cmd_search)},
2539     {"$MultiSearch", cc(cmd_search)},
2540     {"$ConnectToMe", cc(cmd_connecttome)},
2541     {"$RevConnectToMe", cc(cmd_revconnecttome)},
2542     {"$GetNetInfo", cc(cmd_getnetinfo)},
2543     {"$To:", cc(cmd_to)},
2544     {"$SR", cc(cmd_sr)},
2545     {"$UserCommand", cc(cmd_usercommand)},
2546     {"$GetPass", cc(cmd_getpass)},
2547     {"$LogedIn", cc(cmd_logedin)}, /* sic */
2548     {NULL, NULL}
2549 };
2550
2551 struct command peercmds[] =
2552 {
2553     {"$MyNick", cc(cmd_mynick)},
2554     {"$Lock", cc(cmd_peerlock)},
2555     {"$Direction", cc(cmd_direction)},
2556     {"$Key", cc(cmd_key)},
2557     {"$FileLength", cc(cmd_filelength)},
2558     {"$Error", cc(cmd_error)},
2559     {"$MaxedOut", cc(cmd_maxedout)},
2560     {"$Get", cc(cmd_get)},
2561     {"$Send", cc(cmd_send)},
2562     {"$Supports", cc(cmd_supports)},
2563     {"$GetBlock", cc(cmd_getblock)},
2564     {"$UGetBlock", cc(cmd_getblock)},
2565     {"$GetZBlock", cc(cmd_getblock)},
2566     {"$UGetZBlock", cc(cmd_getblock)},
2567     {"$ADCGET", cc(cmd_adcget)},
2568     {"$ADCSND", cc(cmd_adcsnd), 1},
2569     {"$Sending", cc(cmd_sending), 1},
2570     {NULL, NULL}
2571 };
2572 #undef cc
2573
2574 static void dctransdetach(struct transfer *transfer, struct dcpeer *peer)
2575 {
2576     CBUNREG(transfer, trans_filterout, peer);
2577     if(peer->freeing)
2578         return;
2579     peer->transfer = NULL;
2580     freedcpeer(peer);
2581 }
2582
2583 static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
2584 {
2585     int ret;
2586     void *buf;
2587     char outbuf[1024];
2588     z_stream *cstr;
2589     size_t bufsize;
2590     
2591     if((peer->state == PEER_TRNS) || (peer->state == PEER_SYNC))
2592     {
2593         if(sockqueuesize(peer->sk) < 65536)
2594         {
2595             if((buf = transfergetdata(transfer, &bufsize)) != NULL)
2596             {
2597                 if(peer->compress == CPRS_NONE)
2598                 {
2599                     sockqueue(peer->sk, buf, bufsize);
2600                 } else if(peer->compress == CPRS_ZLIB) {
2601                     cstr = peer->cprsdata;
2602                     cstr->next_in = buf;
2603                     cstr->avail_in = bufsize;
2604                     while(cstr->avail_in > 0)
2605                     {
2606                         cstr->next_out = outbuf;
2607                         cstr->avail_out = sizeof(outbuf);
2608                         if((ret = deflate(cstr, 0)) != Z_OK)
2609                         {
2610                             flog(LOG_WARNING, "bug? deflate() did not return Z_OK (but rather %i)", ret);
2611                             freedcpeer(peer);
2612                             return;
2613                         }
2614                         sockqueue(peer->sk, outbuf, sizeof(outbuf) - cstr->avail_out);
2615                     }
2616                 }
2617                 free(buf);
2618             }
2619             if(peer->state == PEER_SYNC)
2620             {
2621                 if(peer->compress == CPRS_ZLIB)
2622                 {
2623                     cstr = peer->cprsdata;
2624                     cstr->next_in = NULL;
2625                     cstr->avail_in = 0;
2626                     do
2627                     {
2628                         cstr->next_out = outbuf;
2629                         cstr->avail_out = sizeof(outbuf);
2630                         ret = deflate(cstr, Z_FINISH);
2631                         if((ret != Z_OK) && (ret != Z_STREAM_END))
2632                         {
2633                             flog(LOG_WARNING, "bug? deflate(Z_FINISH) did not return Z_OK (but rather %i)", ret);
2634                             freedcpeer(peer);
2635                             return;
2636                         }
2637                         sockqueue(peer->sk, outbuf, sizeof(outbuf) - cstr->avail_out);
2638                     } while(ret != Z_STREAM_END);
2639                 }
2640                 if(peer->ptclose)
2641                 {
2642                     freedcpeer(peer);
2643                 } else {
2644                     if(peer->timeout == NULL)
2645                         peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
2646                     peer->state = PEER_CMD;
2647                     endcompress(peer);
2648                     transfersetstate(transfer, TRNS_HS);
2649                     socksettos(peer->sk, confgetint("fnet", "fnptos"));
2650                     transfer->flags.b.minislot = 0;
2651                     peer->sk->writecb = NULL;
2652                 }
2653             }
2654         }
2655     }
2656 }
2657
2658 static void dctransendofdata(struct transfer *transfer, struct dcpeer *peer)
2659 {
2660     peer->state = PEER_SYNC;
2661     dctransgotdata(transfer, peer);
2662 }
2663
2664 static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
2665 {
2666     if(transferdatasize(transfer) < 65536)
2667         peer->sk->ignread = 0;
2668 }
2669
2670 static void transread(struct socket *sk, struct dcpeer *peer)
2671 {
2672     void *buf;
2673     size_t bufsize;
2674     struct transfer *transfer;
2675     
2676     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
2677         return;
2678     if(peer->transfer == NULL)
2679     {
2680         free(buf);
2681         freedcpeer(peer);
2682         return;
2683     }
2684     transferputdata(peer->transfer, buf, bufsize);
2685     free(buf);
2686     if(peer->transfer->curpos >= peer->transfer->size)
2687     {
2688         transfer = peer->transfer;
2689         transferdetach(transfer);
2690         transferendofdata(transfer);
2691         return;
2692     }
2693     if(transferdatasize(peer->transfer) > 65535)
2694         sk->ignread = 1;
2695 }
2696
2697 static void transerr(struct socket *sk, int err, struct dcpeer *peer)
2698 {
2699     struct transfer *transfer;
2700
2701     if((transfer = peer->transfer) == NULL)
2702     {
2703         freedcpeer(peer);
2704         return;
2705     }
2706     transferdetach(transfer);
2707     transferendofdata(transfer);
2708 }
2709
2710 static void transwrite(struct socket *sk, struct dcpeer *peer)
2711 {
2712     if((peer->state != PEER_TRNS) && (peer->state != PEER_SYNC))
2713         return;
2714     if(peer->transfer == NULL)
2715     {
2716         freedcpeer(peer);
2717         return;
2718     }
2719     dctransgotdata(peer->transfer, peer);
2720 }
2721
2722 static void udpread(struct socket *sk, void *data)
2723 {
2724     char *buf, *p, *p2, *hashbuf;
2725     size_t buflen, hashlen;
2726     char *nick, *filename, *hubname;
2727     int size, slots;
2728     struct fnetnode *fn, *myfn;
2729     struct dchub *hub;
2730     struct srchres *sr;
2731     wchar_t *wnick, *wfile;
2732     struct hash *hash;
2733     
2734     if((buf = sockgetinbuf(sk, &buflen)) == NULL)
2735         return;
2736     buf = srealloc(buf, buflen + 1);
2737     buf[buflen] = 0;
2738     if(!strncmp(buf, "$SR ", 4))
2739     {
2740         p = buf + 4;
2741         nick = p;
2742         if((p2 = strchr(p, ' ')) == NULL)
2743         {
2744             free(buf);
2745             return;
2746         }
2747         *p2 = 0;
2748         p = p2 + 1;
2749         filename = p;
2750         if((p2 = strchr(p, 5)) == NULL)
2751         {
2752             free(buf);
2753             return;
2754         }
2755         *p2 = 0;
2756         p = p2 + 1;
2757         if((p2 = strchr(p, ' ')) == NULL)
2758         {
2759             free(buf);
2760             return;
2761         }
2762         *p2 = 0;
2763         size = atoi(p);
2764         p = p2 + 1;
2765         if((p2 = strchr(p, '/')) == NULL)
2766         {
2767             free(buf);
2768             return;
2769         }
2770         *p2 = 0;
2771         slots = atoi(p);
2772         p = p2 + 1;
2773         if((p2 = strchr(p, 5)) == NULL)
2774         {
2775             free(buf);
2776             return;
2777         }
2778         p = p2 + 1;
2779         hubname = p;
2780         if((p2 = strstr(p, " (")) == NULL)
2781         {
2782             free(buf);
2783             return;
2784         }
2785         *p2 = 0;
2786         if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
2787         {
2788             free(buf);
2789             return;
2790         }
2791         if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
2792         {
2793             free(wnick);
2794             free(buf);
2795             return;
2796         }
2797         myfn = NULL;
2798         hash = NULL;
2799         if(!strncmp(hubname, "TTH:", 4))
2800         {
2801             if((hashbuf = base32decode(hubname + 4, &hashlen)) != NULL)
2802             {
2803                 if(hashlen == 24)
2804                     hash = newhash(L"TTH", 24, hashbuf);
2805                 free(hashbuf);
2806             }
2807         } else {
2808             for(fn = fnetnodes; fn != NULL; fn = fn->next)
2809             {
2810                 if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
2811                 {
2812                     if((hub->nativename != NULL) && !strcmp(hub->nativename, hubname))
2813                     {
2814                         if(myfn == NULL)
2815                         {
2816                             myfn = fn;
2817                         } else {
2818                             myfn = NULL;
2819                             break;
2820                         }
2821                     }
2822                 }
2823             }
2824         }
2825         sr = newsrchres(&dcnet, wfile, wnick);
2826         if(sr->peernick != NULL)
2827             free(sr->peernick);
2828         sr->peernick = swcsdup(wnick);
2829         sr->size = size;
2830         sr->slots = slots;
2831         free(wfile);
2832         free(wnick);
2833         if(myfn != NULL)
2834             getfnetnode(sr->fn = myfn);
2835         if(hash != NULL)
2836             sr->hash = hash;
2837         submitsrchres(sr);
2838         freesrchres(sr);
2839     }
2840     free(buf);
2841 }
2842
2843 static void hubread(struct socket *sk, struct fnetnode *fn)
2844 {
2845     struct dchub *hub;
2846     char *newbuf;
2847     size_t datalen;
2848     char *p;
2849     
2850     hub = (struct dchub *)fn->data;
2851     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
2852         return;
2853     if(hub->inbufdata > 500000) /* Discard possible malicious data */
2854         hub->inbufdata = 0;
2855     sizebuf2(hub->inbuf, hub->inbufdata + datalen, 1);
2856     memcpy(hub->inbuf + hub->inbufdata, newbuf, datalen);
2857     free(newbuf);
2858     p = hub->inbuf + hub->inbufdata;
2859     hub->inbufdata += datalen;
2860     while((datalen > 0) && ((p = memchr(p, '|', datalen)) != NULL))
2861     {
2862         *(p++) = 0;
2863         newqcmd(&hub->queue, hub->inbuf);
2864         memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
2865         datalen = hub->inbufdata;
2866         p = hub->inbuf;
2867     }
2868 }
2869
2870 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
2871 {
2872     killfnetnode(fn);
2873 }
2874
2875 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
2876 {
2877     struct dchub *hub;
2878     char *buf;
2879     
2880     hub = fn->data;
2881     if((buf = icwcstombs(newnick, DCCHARSET)) == NULL)
2882         return(1);
2883     if((strchr(buf, ' ') != NULL) || (strchr(buf, '|') != NULL) || (strchr(buf, '$') != NULL))
2884     {
2885         free(buf);
2886         return(1);
2887     }
2888     if(hub == NULL) /* Not yet connected */
2889     {
2890         free(buf);
2891         return(0);
2892     }
2893     if(hub->nativenick != NULL)
2894         free(hub->nativenick);
2895     hub->nativenick = buf;
2896     return(0);
2897 }
2898
2899 static struct dchub *newdchub(struct fnetnode *fn)
2900 {
2901     struct dchub *new;
2902     wchar_t *emu;
2903     
2904     new = smalloc(sizeof(*new));
2905     memset(new, 0, sizeof(*new));
2906     fn->data = new;
2907     if(confgetint("dc", "dcppemu"))
2908         new->dcppemu = 1;
2909     if((emu = wpfind(fn->args, L"dcppemu")) != NULL)
2910     {
2911         if(*emu == L'y')
2912             new->dcppemu = 1;
2913         if(*emu == L'n')
2914             new->dcppemu = 0;
2915     }
2916     if(hubsetnick(fn, fn->mynick))
2917         fnetsetnick(fn, L"DoldaConnectUser-IN");
2918     /* IN as in Invalid Nick */
2919     return(new);
2920 }
2921
2922 static struct dcpeer *newdcpeer(struct socket *sk)
2923 {
2924     struct dcpeer *new;
2925     
2926     new = smalloc(sizeof(*new));
2927     memset(new, 0, sizeof(*new));
2928     new->transfer = NULL;
2929     getsock(sk);
2930     new->sk = sk;
2931     if(confgetint("dc", "dcppemu"))
2932         new->dcppemu = 1;
2933     new->next = peers;
2934     new->prev = NULL;
2935     if(peers != NULL)
2936         peers->prev = new;
2937     peers = new;
2938     numdcpeers++;
2939     return(new);
2940 }
2941
2942 static void freedcpeer(struct dcpeer *peer)
2943 {
2944     int i;
2945     struct qcommand *qcmd;
2946     
2947     peer->freeing = 1;
2948     if(peers == peer)
2949         peers = peer->next;
2950     if(peer->next != NULL)
2951         peer->next->prev = peer->prev;
2952     if(peer->prev != NULL)
2953         peer->prev->next = peer->next;
2954     if(peer->transfer != NULL)
2955     {
2956         if(peer->transfer->dir == TRNSD_UP)
2957             peer->transfer->close = 1;
2958         if(peer->transfer->dir == TRNSD_DOWN)
2959             resettransfer(peer->transfer);
2960         transferdetach(peer->transfer);
2961     }
2962     if(peer->timeout != NULL)
2963         canceltimer(peer->timeout);
2964     if(peer->sk->data == peer)
2965         peer->sk->data = NULL;
2966     peer->sk->readcb = NULL;
2967     peer->sk->writecb = NULL;
2968     peer->sk->errcb = NULL;
2969     putsock(peer->sk);
2970     endcompress(peer);
2971     if(peer->supports != NULL)
2972     {
2973         for(i = 0; peer->supports[i] != NULL; i++)
2974             free(peer->supports[i]);
2975         free(peer->supports);
2976     }
2977     if(peer->inbuf != NULL)
2978         free(peer->inbuf);
2979     if(peer->key != NULL)
2980         free(peer->key);
2981     if(peer->wcsname != NULL)
2982         free(peer->wcsname);
2983     if(peer->nativename != NULL)
2984         free(peer->nativename);
2985     if(peer->fn != NULL)
2986         putfnetnode(peer->fn);
2987     while((qcmd = ulqcmd(&peer->queue)) != NULL)
2988         freeqcmd(qcmd);
2989     free(peer);
2990     numdcpeers--;
2991 }
2992
2993 static void hubconnect(struct fnetnode *fn)
2994 {
2995     fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
2996     fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
2997     getfnetnode(fn);
2998     fn->data = newdchub(fn);
2999     fn->sk->data = fn;
3000     return;
3001 }
3002
3003 static void hubdestroy(struct fnetnode *fn)
3004 {
3005     struct dchub *hub;
3006     struct qcommand *qcmd;
3007     
3008     hub = (struct dchub *)fn->data;
3009     if(fn->sk != NULL)
3010     {
3011         if(fn->sk->data == fn)
3012         {
3013             fn->sk->data = NULL;
3014             putfnetnode(fn);
3015         }
3016     }
3017     if(hub == NULL)
3018         return;
3019     while((qcmd = ulqcmd(&hub->queue)) != NULL)
3020         freeqcmd(qcmd);
3021     if(hub->nativename != NULL)
3022         free(hub->nativename);
3023     if(hub->nativenick != NULL)
3024         free(hub->nativenick);
3025     if(hub->inbuf != NULL)
3026         free(hub->inbuf);
3027     free(hub);
3028 }
3029
3030 static wchar_t *dcbasename(wchar_t *filename)
3031 {
3032     wchar_t *ret;
3033     
3034     if((ret = wcsrchr(filename, L'\\')) != NULL)
3035         return(ret + 1);
3036     return(filename);
3037 }
3038
3039 static struct transferiface dctransfer =
3040 {
3041     .detach = (void (*)(struct transfer *, void *))dctransdetach,
3042     .gotdata = (void (*)(struct transfer *, void *))dctransgotdata,
3043     .endofdata = (void (*)(struct transfer *, void *))dctransendofdata,
3044     .wantdata = (void (*)(struct transfer *, void *))dcwantdata
3045 };
3046
3047 static struct fnet dcnet =
3048 {
3049     .name = L"dc",
3050     .connect = hubconnect,
3051     .destroy = hubdestroy,
3052     .setnick = hubsetnick,
3053     .reqconn = hubreqconn,
3054     .sendchat = hubsendchat,
3055     .search = hubsearch,
3056     .filebasename = dcbasename
3057 };
3058
3059 static void peerread(struct socket *sk, struct dcpeer *peer)
3060 {
3061     char *newbuf, *p;
3062     size_t datalen;
3063     struct command *cmd;
3064
3065     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
3066         return;
3067     sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
3068     memcpy(peer->inbuf + peer->inbufdata, newbuf, datalen);
3069     free(newbuf);
3070     peer->inbufdata += datalen;
3071     if(peer->state == PEER_CMD)
3072     {
3073         p = peer->inbuf;
3074         while((peer->inbufdata > 0) && (p = memchr(peer->inbuf, '|', peer->inbufdata)) != NULL)
3075         {
3076             *(p++) = 0;
3077             newqcmd(&peer->queue, peer->inbuf);
3078             for(cmd = peercmds; cmd->handler != NULL; cmd++)
3079             {
3080                 if(!memcmp(peer->inbuf, cmd->name, strlen(cmd->name)) && ((peer->inbuf[strlen(cmd->name)] == ' ') || (peer->inbuf[strlen(cmd->name)] == '|')))
3081                     break;
3082             }
3083             memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
3084             if(cmd->stop)
3085             {
3086                 peer->state = PEER_STOP;
3087                 break;
3088             }
3089         }
3090     } else if(peer->state == PEER_TTHL) {
3091         handletthl(peer);
3092     }
3093 }
3094
3095 static void peererror(struct socket *sk, int err, struct dcpeer *peer)
3096 {
3097     freedcpeer(peer);
3098 }
3099
3100 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
3101 {
3102     struct dcpeer *peer;
3103     struct dchub *hub;
3104     
3105     if(err != 0)
3106     {
3107         putfnetnode(fn);
3108         return;
3109     }
3110     hub = fn->data;
3111     peer = newdcpeer(sk);
3112     peer->fn = fn;
3113     peer->accepted = 0;
3114     peer->dcppemu = hub->dcppemu;
3115     sk->readcb = (void (*)(struct socket *, void *))peerread;
3116     sk->errcb = (void (*)(struct socket *, int, void *))peererror;
3117     sk->data = peer;
3118     socksettos(sk, confgetint("fnet", "fnptos"));
3119     putsock(sk);
3120     peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
3121     sendmynick(peer);
3122     sendpeerlock(peer);
3123 }
3124
3125 static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
3126 {
3127     struct dcpeer *peer;
3128     
3129     peer = newdcpeer(newsk);
3130     peer->accepted = 1;
3131     newsk->readcb = (void (*)(struct socket *, void *))peerread;
3132     newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
3133     newsk->data = peer;
3134     socksettos(newsk, confgetint("fnet", "fnptos"));
3135     peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
3136 }
3137
3138 static void updatehmlist(void)
3139 {
3140     int i, lev, ic, ret;
3141     struct sharecache *node;
3142     char *buf, *buf2, numbuf[32];
3143     size_t bufsize, bufdata;
3144     int fd, ibuf;
3145     
3146     bufdata = 0;
3147     buf = smalloc(bufsize = 65536);
3148     node = shareroot->child;
3149     lev = 0;
3150     while(1)
3151     {
3152         ic = 0;
3153         if((buf2 = icwcstombs(node->name, DCCHARSET)) != NULL)
3154         {
3155             for(i = 0; i < lev; i++)
3156                 addtobuf(buf, 9);
3157             bufcat(buf, buf2, strlen(buf2));
3158             free(buf2);
3159             if(node->f.b.type == FILE_REG)
3160             {
3161                 addtobuf(buf, '|');
3162                 sprintf(numbuf, "%i", node->size);
3163                 bufcat(buf, numbuf, strlen(numbuf));
3164             }
3165             addtobuf(buf, 13);
3166             addtobuf(buf, 10);
3167         } else {
3168             ic = 1;
3169         }
3170         if((node->child != NULL) && !ic)
3171         {
3172             lev++;
3173             node = node->child;
3174         } else if(node->next != NULL) {
3175             node = node->next;
3176         } else {
3177             while(node->next == NULL)
3178             {
3179                 lev--;
3180                 node = node->parent;
3181                 if(node == shareroot)
3182                     break;
3183             }
3184             if(node == shareroot)
3185                 break;
3186             node = node->next;
3187         }
3188     }
3189     if(hmlistname != NULL)
3190     {
3191         unlink(hmlistname);
3192         free(hmlistname);
3193     }
3194     hmlistname = sstrdup("/tmp/dc-filelist-hm-XXXXXX");
3195     if((fd = mkstemp(hmlistname)) < 0)
3196     {
3197         flog(LOG_WARNING, "could not create HM file list tempfile: %s", strerror(errno));
3198         free(hmlistname);
3199         hmlistname = NULL;
3200     } else {
3201         /*
3202          * I do not want to implement a good Huffman encoder, and it's not
3203          * like Huffman encoding actually yields any impressive results
3204          * for DC file lists anyway, so I'll just output a bogus
3205          * tree. Implement a good encoder if you want to.
3206          */
3207         write(fd, "HE3\r\0", 5);
3208         write(fd, &bufdata, 4);
3209         ibuf = 256;
3210         write(fd, &ibuf, 2);
3211         ibuf = 8;
3212         for(i = 0; i < 256; i++)
3213         {
3214             write(fd, &i, 1);
3215             write(fd, &ibuf, 1);
3216         }
3217         for(i = 0; i < 256; i++)
3218             write(fd, &i, 1);
3219         for(buf2 = buf; bufdata > 0;)
3220         {
3221             if((ret = write(fd, buf2, bufdata)) < 0)
3222             {
3223                 flog(LOG_WARNING, "could not write file list: %s", strerror(errno));
3224                 break;
3225             }
3226             bufdata -= ret;
3227             buf2 += ret;
3228         }
3229         close(fd);
3230     }
3231     free(buf);
3232 }
3233
3234 static struct xmlent
3235 {
3236     wchar_t c;
3237     wchar_t *ent;
3238 } entities[] = {
3239     {L'&', L"&amp;"},
3240     {L'\"', L"&#34;"},
3241 /*  {L'\'', L"&quot;"},  Emulate DC++ escaping by omitting this. */
3242     {L'\0', NULL}
3243 };
3244
3245 static wchar_t *escapexml(wchar_t *src)
3246 {
3247     static wchar_t *buf = NULL;;
3248     int ret, c;
3249     wchar_t nbuf[32];
3250     size_t bufsize, bufdata;
3251     struct xmlent *ent;
3252     
3253     if(buf != NULL)
3254         free(buf);
3255     buf = NULL;
3256     bufsize = bufdata = 0;
3257     for(; *src != L'\0'; src++)
3258     {
3259         c = wctob(*src);
3260         if((c > 0) && (c < 32))
3261         {
3262             bufcat(buf, L"&#", 2);
3263             ret = swprintf(nbuf, (sizeof(nbuf) / sizeof(*nbuf)), L"%i", c);
3264             bufcat(buf, nbuf, ret);
3265             addtobuf(buf, L';');
3266         } else {
3267             for(ent = entities; ent->ent != NULL; ent++)
3268             {
3269                 if(ent->c == *src)
3270                 {
3271                     bufcat(buf, ent->ent, wcslen(ent->ent));
3272                     break;
3273                 }
3274             }
3275             if(ent->ent == NULL)
3276                 addtobuf(buf, *src);
3277         }
3278     }
3279     addtobuf(buf, L'\0');
3280     return(buf);
3281 }
3282
3283 static void updatexmllist(void)
3284 {
3285     int i, fd, lev;
3286     FILE *fs;
3287     char cidbuf[14], *namebuf;
3288     char *hashbuf;
3289     struct sharecache *node;
3290     
3291     if(xmllistname != NULL)
3292     {
3293         unlink(xmllistname);
3294         free(xmllistname);
3295     }
3296     xmllistname = sstrdup("/tmp/dc-filelist-dcxml-XXXXXX");
3297     if((fd = mkstemp(xmllistname)) < 0)
3298     {
3299         flog(LOG_WARNING, "could not create XML file list tempfile: %s", strerror(errno));
3300         free(xmllistname);
3301         xmllistname = NULL;
3302         return;
3303     }
3304     if((fs = fdopen(fd, "w")) == NULL)
3305     {
3306         flog(LOG_WARNING, "could not fdopen XML list fd %i: %s", fd, strerror(errno));
3307         unlink(xmllistname);
3308         free(xmllistname);
3309         xmllistname = NULL;
3310         close(fd);
3311         return;
3312     }
3313     fprintf(fs, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n");
3314     for(i = 0; i < sizeof(cidbuf) - 1; i++)
3315         cidbuf[i] = (rand() % ('Z' - 'A' + 1)) + 'A';
3316     cidbuf[i] = 0;
3317     if(confgetint("dc", "dcppemu"))
3318         fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"DC++ 0.674\">\r\n", cidbuf);
3319     else
3320         fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, "DoldaConnect" VERSION);
3321     
3322     node = shareroot->child;
3323     lev = 0;
3324     while(1)
3325     {
3326         if((namebuf = icswcstombs(escapexml(node->name), "UTF-8", NULL)) != NULL)
3327         {
3328             for(i = 0; i < lev; i++)
3329                 fputc('\t', fs);
3330             if(node->child != NULL)
3331             {
3332                 fprintf(fs, "<Directory Name=\"%s\">\r\n", namebuf);
3333                 node = node->child;
3334                 lev++;
3335                 continue;
3336             } else {
3337                 fprintf(fs, "<File Name=\"%s\" Size=\"%i\"", namebuf, node->size);
3338                 if(node->f.b.hastth)
3339                 {
3340                     hashbuf = base32encode(node->hashtth, 24);
3341                     fprintf(fs, " TTH=\"%.39s\"", hashbuf);
3342                     free(hashbuf);
3343                 }
3344                 fprintf(fs, "/>\r\n");
3345             }
3346             while(node->next == NULL)
3347             {
3348                 node = node->parent;
3349                 if(node == shareroot)
3350                 {
3351                     break;
3352                 } else {
3353                     lev--;
3354                     for(i = 0; i < lev; i++)
3355                         fputc('\t', fs);
3356                     fprintf(fs, "</Directory>\r\n");
3357                 }
3358             }
3359             if(node == shareroot)
3360                 break;
3361             node = node->next;
3362         }
3363     }
3364     
3365     if(confgetint("dc", "dcppemu"))
3366         fprintf(fs, "</FileListing>");
3367     else
3368         fprintf(fs, "</FileListing>\r\n");
3369     fclose(fs);
3370 }
3371
3372 static void updatexmlbz2list(void)
3373 {
3374     int err, fd;
3375     FILE *in;
3376     FILE *real;
3377     BZFILE *out;
3378     char buf[1024];
3379     size_t bufdata;
3380     
3381     if(xmllistname == NULL)
3382         return;
3383     if((in = fopen(xmllistname, "r")) == NULL)
3384     {
3385         flog(LOG_WARNING, "could not open XML file list for bzipping: %s", strerror(errno));
3386         return;
3387     }
3388     if(xmlbz2listname != NULL)
3389     {
3390         unlink(xmlbz2listname);
3391         free(xmlbz2listname);
3392     }
3393     xmlbz2listname = sstrdup("/tmp/dc-filelist-dcxmlbz2-XXXXXX");
3394     if((fd = mkstemp(xmlbz2listname)) < 0)
3395     {
3396         flog(LOG_WARNING, "could not create bzipped XML file list tempfile: %s", strerror(errno));
3397         free(xmlbz2listname);
3398         xmlbz2listname = NULL;
3399         fclose(in);
3400         return;
3401     }
3402     if((real = fdopen(fd, "w")) == NULL)
3403     {
3404         flog(LOG_WARNING, "could not fdopen bzipped XML list fd %i: %s", fd, strerror(errno));
3405         close(fd);
3406         unlink(xmlbz2listname);
3407         free(xmlbz2listname);
3408         xmlbz2listname = NULL;
3409         fclose(in);
3410         return;
3411     }
3412     out = BZ2_bzWriteOpen(&err, real, 9, 0, 0);
3413     if(err != BZ_OK)
3414     {
3415         flog(LOG_WARNING, "could not open bzip2 stream from XML list");
3416         fclose(real);
3417         unlink(xmlbz2listname);
3418         free(xmlbz2listname);
3419         xmlbz2listname = NULL;
3420         fclose(in);
3421         return;
3422     }
3423     while(!feof(in))
3424     {
3425         bufdata = fread(buf, 1, sizeof(buf), in);
3426         BZ2_bzWrite(&err, out, buf, bufdata);
3427     }
3428     fclose(in);
3429     BZ2_bzWriteClose(&err, out, 0, NULL, NULL);
3430     fclose(real);
3431 }
3432
3433 static int shareupdate(unsigned long long uusharesize, void *data)
3434 {
3435     updatehmlist();
3436     updatexmllist();
3437     updatexmlbz2list();
3438     return(0);
3439 }
3440
3441 static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, struct socket *sk, void *data)
3442 {
3443     char *p;
3444     struct command *cmd;
3445     
3446     if((p = strchr(qcmd->string, ' ')) != NULL)
3447         *(p++) = 0;
3448     for(cmd = cmdlist; cmd->handler != NULL; cmd++)
3449     {
3450         if(!strcmp(cmd->name, qcmd->string))
3451             break;
3452     }
3453     if(cmd->handler != NULL)
3454         cmd->handler(sk, data, qcmd->string, p);
3455 /*
3456     else
3457         flog(LOG_DEBUG, "Unimplemented DC command: %s \"%s\"", qcmd->string, p?p:"noargs");
3458 */
3459 }
3460
3461 static int run(void)
3462 {
3463     struct fnetnode *fn, *nextfn;
3464     struct dchub *hub;
3465     struct dcpeer *peer, *nextpeer;
3466     struct qcommand *qcmd;
3467     int ret;
3468     
3469     ret = 0;
3470     for(fn = fnetnodes; fn != NULL; fn = nextfn)
3471     {
3472         nextfn = fn->next;
3473         if(fn->fnet != &dcnet)
3474             continue;
3475         if(fn->data == NULL)
3476             continue;
3477         hub = (struct dchub *)fn->data;
3478         if((qcmd = ulqcmd(&hub->queue)) != NULL)
3479         {
3480             if(*qcmd->string == '$')
3481             {
3482                 if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
3483                     dispatchcommand(qcmd, hubcmds, fn->sk, fn);
3484             } else if(*qcmd->string != 0) {
3485                 hubrecvchat(fn->sk, fn, NULL, qcmd->string);
3486             }
3487             freeqcmd(qcmd);
3488             ret = 1;
3489             break;
3490         }
3491     }
3492     for(peer = peers; peer != NULL; peer = nextpeer)
3493     {
3494         nextpeer = peer->next;
3495         if((qcmd = ulqcmd(&peer->queue)) != NULL)
3496         {
3497             if(peer->timeout != NULL)
3498                 canceltimer(peer->timeout);
3499             peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
3500             if(*qcmd->string == '$')
3501                 dispatchcommand(qcmd, peercmds, peer->sk, peer);
3502             freeqcmd(qcmd);
3503             ret = 1;
3504             break;
3505         }
3506     }
3507     return(ret);
3508 }
3509
3510 static void preinit(int hup)
3511 {
3512     if(hup)
3513         return;
3514     regfnet(&dcnet);
3515 }
3516
3517 static int updateudpport(struct configvar *var, void *uudata)
3518 {
3519     struct sockaddr_in addr;
3520     struct socket *newsock;
3521     
3522     memset(&addr, 0, sizeof(addr));
3523     addr.sin_family = AF_INET;
3524     addr.sin_port = htons(var->val.num);
3525     if((newsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3526     {
3527         flog(LOG_WARNING, "could not create new DC UDP socket, reverting to old: %s", strerror(errno));
3528         return(0);
3529     }
3530     newsock->readcb = udpread;
3531     if(udpsock != NULL)
3532         putsock(udpsock);
3533     udpsock = newsock;
3534     return(0);
3535 }
3536
3537 static int updatetcpport(struct configvar *var, void *uudata)
3538 {
3539     struct sockaddr_in addr;
3540     struct socket *newsock;
3541     
3542     memset(&addr, 0, sizeof(addr));
3543     addr.sin_family = AF_INET;
3544     addr.sin_port = htons(var->val.num);
3545     if((newsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3546         flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3547     if(tcpsock != NULL)
3548         putsock(tcpsock);
3549     tcpsock = newsock;
3550     return(0);
3551 }
3552
3553 static int init(int hup)
3554 {
3555     struct sockaddr_in addr;
3556     
3557     if(!hup)
3558     {
3559         GCBREG(sharechangecb, shareupdate, NULL);
3560         if(udpsock != NULL)
3561             putsock(udpsock);
3562         if(tcpsock != NULL)
3563             putsock(tcpsock);
3564         addr.sin_family = AF_INET;
3565         memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
3566         addr.sin_port = htons(confgetint("dc", "udpport"));
3567         if((udpsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3568         {
3569             flog(LOG_CRIT, "could not create DC UDP socket: %s", strerror(errno));
3570             return(1);
3571         }
3572         udpsock->readcb = udpread;
3573         addr.sin_port = htons(confgetint("dc", "tcpport"));
3574         if((tcpsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3575             flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3576         CBREG(confgetvar("dc", "udpport"), conf_update, updateudpport, NULL, NULL);
3577         CBREG(confgetvar("dc", "tcpport"), conf_update, updatetcpport, NULL, NULL);
3578         CBREG(confgetvar("net", "mode"), conf_update, updatetcpport, NULL, NULL);
3579     }
3580     return(0);
3581 }
3582
3583 static void terminate(void)
3584 {
3585     if(hmlistname != NULL)
3586     {
3587         unlink(hmlistname);
3588         free(hmlistname);
3589     }
3590     if(xmllistname != NULL)
3591     {
3592         unlink(xmllistname);
3593         free(xmllistname);
3594     }
3595     if(xmlbz2listname != NULL)
3596     {
3597         unlink(xmlbz2listname);
3598         free(xmlbz2listname);
3599     }
3600 }
3601
3602 static struct configvar myvars[] =
3603 {
3604     {CONF_VAR_STRING, "desc", {.str = L""}},
3605     {CONF_VAR_STRING, "speedstring", {.str = L"LAN(T1)"}},
3606     {CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
3607     {CONF_VAR_INT, "udpport", {.num = 0}},
3608     {CONF_VAR_INT, "tcpport", {.num = 0}},
3609     {CONF_VAR_BOOL, "dcppemu", {.num = 0}},
3610     {CONF_VAR_END}
3611 };
3612
3613 static struct module me =
3614 {
3615     .conf =
3616     {
3617         .vars = myvars
3618     },
3619     .preinit = preinit,
3620     .init = init,
3621     .run = run,
3622     .terminate = terminate,
3623     .name = "dc"
3624 };
3625
3626 MODULE(me)