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