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