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