Changed the fnetnode peer list to a btree.
[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     off_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 #if defined(__GNUC__)
506 static void __attribute__ ((format (printf, 2, 3))) sendadcf(struct socket *sk, char *arg, ...) 
507 #else
508 static void sendadcf(struct socket *sk, char *arg, ...) 
509 #endif
510 {
511     char *buf;
512     va_list args;
513     
514     va_start(args, arg);
515     buf = vsprintf2(arg, args);
516     va_end(args);
517     if(buf == NULL)
518         return;
519     sendadc(sk, buf);
520     free(buf);
521 }
522
523 static char **parseadc(char *args)
524 {
525     char **retbuf;
526     size_t retbufsize, retbufdata;
527     char *buf;
528     size_t bufsize, bufdata;
529     int state;
530     
531     retbuf = NULL;
532     buf = NULL;
533     retbufsize = retbufdata = bufsize = bufdata = 0;
534     state = 0;
535     while(state != 3)
536     {
537         switch(state)
538         {
539         case 0:
540             if(*args == 0)
541                 state = 3;
542             else if(*args != ' ')
543                 state = 1;
544             break;
545         case 1:
546             if((*args == ' ') || (*args == 0))
547             {
548                 addtobuf(buf, 0);
549                 addtobuf(retbuf, buf);
550                 buf = NULL;
551                 bufsize = bufdata = 0;
552                 if(*args == 0)
553                     state = 3;
554             } else if(*args == '\\') {
555                 state = 2;
556             } else {
557                 addtobuf(buf, *args);
558             }
559             args++;
560             break;
561         case 2:
562             if(*args == 0)
563             {
564                 if(buf != NULL)
565                     free(buf);
566                 addtobuf(retbuf, NULL);
567                 freeparr(retbuf);
568                 return(NULL);
569             } else if((*args == 's') || (*args == ' ')) {
570                 addtobuf(buf, ' ');
571             } else if(*args == 'n') {
572                 addtobuf(buf, '\n');
573             } else if(*args == '\\') {
574                 addtobuf(buf, '\\');
575             }
576             args++;
577             state = 1;
578             break;
579         }
580     }
581     if(buf != NULL)
582         free(buf);
583     addtobuf(retbuf, NULL);
584     return(retbuf);
585 }
586
587 /* Macros useful in command handlers */
588 #define skipspace(s) ({if(((s) = strchr((s), ' ')) == NULL) return; else (s)++;})
589 #define qstr(sk, str) sockqueue(sk, str, strlen(str))
590 #define qstrf(sk, strandargs...) \
591 do { \
592     char *__buf__; \
593     if((__buf__ = sprintf2(strandargs)) != NULL) { \
594         sockqueue(sk, __buf__, strlen(__buf__)); \
595         free(__buf__); \
596     } \
597 } while(0)
598
599 static char *tr(char *str, char *trans)
600 {
601     char *p;
602     
603     for(; *trans; trans += 2)
604     {
605         for(p = strchr(str, trans[0]); p != NULL; p = strchr(p, trans[0]))
606             *p = trans[1];
607     }
608     return(str);
609 }
610
611 static char *getadcid(struct dcpeer *peer)
612 {
613     char *buf;
614     char *ret;
615     int isfilelist;
616     
617     isfilelist = 0;
618     if(!wcscmp(peer->transfer->path, L"files.xml") || !wcscmp(peer->transfer->path, L"files.xml.bz2") || !wcscmp(peer->transfer->path, L"MyList.DcLst"))
619         isfilelist = 1;
620     if(!isfilelist && (peer->transfer->hash != NULL) && isdchash(peer->transfer->hash) && supports(peer, "tthf"))
621     {
622         buf = base32encode(peer->transfer->hash->buf, 24);
623         ret = sprintf2("TTH/%.39s", buf);
624         free(buf);
625     } else {
626         if((buf = icwcstombs(peer->transfer->path, "UTF-8")) == NULL)
627             return(NULL);
628         ret = pathnmdc2adc(buf);
629         free(buf);
630     }
631     return(ret);
632 }
633
634
635 static int trresumecb(struct transfer *transfer, wchar_t *cmd, wchar_t *arg, struct dcpeer *peer)
636 {
637     if(!wcscmp(cmd, L"resume"))
638     {
639         if(arg == NULL)
640         {
641             flog(LOG_WARNING, "filter returned no position for \"resume\" on transfer %i", transfer->id);
642             freedcpeer(peer);
643         } else {
644             transfer->curpos = wcstoll(arg, NULL, 10);
645             peer->hascurpos = 1;
646             requestfile(peer);
647         }
648         return(1);
649     }
650     return(0);
651 }
652
653 static void sendmynick(struct dcpeer *peer)
654 {
655     struct fnetnode *fn;
656     
657     fn = peer->fn;
658     if(fn == NULL)
659         qstrf(peer->sk, "$MyNick %s|", icswcstombs(confgetstr("cli", "defnick"), peer->charset, "DoldaConnectUser-IN"));
660     else
661         qstrf(peer->sk, "$MyNick %s|", icswcstombs(fn->mynick, peer->charset, "DoldaConnectUser-IN"));
662 }
663
664 static void sendpeerlock(struct dcpeer *peer)
665 {
666     if(peer->dcppemu)
667         qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DCPLUSPLUS0.674ABCABC|");
668     else
669         qstrf(peer->sk, "$Lock EXTENDEDPROTOCOLABCABCABCABCABCABC Pk=DOLDA%sABCABCABC|", VERSION);
670 }
671
672 static void sendsupports(struct dcpeer *peer)
673 {
674     if(peer->dcppemu) {
675         qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG |");
676     } else {
677         qstr(peer->sk, "$Supports MiniSlots XmlBZList ADCGet TTHL TTHF");
678         if(!confgetint("dc", "hidedeflate"))
679             qstr(peer->sk, " GetZBlock ZLIG");
680         qstr(peer->sk, "|");
681     }
682 }
683
684 static void requestfile(struct dcpeer *peer)
685 {
686     char *buf;
687     
688     if(peer->transfer->size == -1)
689     {
690         /* Use DCCHARSET for $Get paths until further researched... */
691         if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
692         {
693             transferseterror(peer->transfer, TRNSE_NOTFOUND);
694             freedcpeer(peer);
695             return;
696         }
697         /* The transfer will be restarted later from
698          * cmd_filelength when it detects that the sizes
699          * don't match. */
700         qstrf(peer->sk, "$Get %s$1|", buf);
701         return;
702     }
703     if((peer->transfer->hash == NULL) && !peer->notthl)
704     {
705         if(supports(peer, "adcget") && supports(peer, "tthl"))
706         {
707             qstr(peer->sk, "$ADCGET");
708             sendadc(peer->sk, "tthl");
709             if((buf = getadcid(peer)) == NULL)
710             {
711                 transferseterror(peer->transfer, TRNSE_NOTFOUND);
712                 freedcpeer(peer);
713                 return;
714             }
715             sendadc(peer->sk, buf);
716             free(buf);
717             sendadc(peer->sk, "0");
718             sendadc(peer->sk, "-1");
719             qstr(peer->sk, "|");
720             peer->fetchingtthl = 1;
721             return;
722         }
723     }
724     if(!peer->hascurpos)
725     {
726         if(forkfilter(peer->transfer))
727         {
728             flog(LOG_WARNING, "could not fork filter for transfer %i: %s", peer->transfer->id, strerror(errno));
729             freedcpeer(peer);
730             return;
731         }
732         CBREG(peer->transfer, trans_filterout, (int (*)(struct transfer *, wchar_t *, wchar_t *, void *))trresumecb, NULL, peer);
733         return;
734     }
735     if(supports(peer, "adcget"))
736     {
737         qstr(peer->sk, "$ADCGET");
738         sendadc(peer->sk, "file");
739         if((buf = getadcid(peer)) == NULL)
740         {
741             transferseterror(peer->transfer, TRNSE_NOTFOUND);
742             freedcpeer(peer);
743             return;
744         }
745         sendadc(peer->sk, buf);
746         free(buf);
747         sendadcf(peer->sk, "%ji", (intmax_t)peer->transfer->curpos);
748         sendadcf(peer->sk, "%ji", (intmax_t)(peer->transfer->size - peer->transfer->curpos));
749         qstr(peer->sk, "|");
750     } else if(supports(peer, "xmlbzlist")) {
751         if((buf = icswcstombs(peer->transfer->path, "UTF-8", NULL)) == NULL)
752         {
753             transferseterror(peer->transfer, TRNSE_NOTFOUND);
754             freedcpeer(peer);
755             return;
756         }
757         qstrf(peer->sk, "$UGetBlock %ji %ji %s|", (intmax_t)peer->transfer->curpos, (intmax_t)(peer->transfer->size - peer->transfer->curpos), buf);
758     } else {
759         /* Use DCCHARSET for $Get paths until further researched... */
760         if((buf = icswcstombs(peer->transfer->path, DCCHARSET, NULL)) == NULL)
761         {
762             transferseterror(peer->transfer, TRNSE_NOTFOUND);
763             freedcpeer(peer);
764             return;
765         }
766         qstrf(peer->sk, "$Get %s$%ji|", buf, (intmax_t)peer->transfer->curpos + 1);
767     }
768 }
769
770 static void sendmyinfo(struct socket *sk, struct fnetnode *fn)
771 {
772     struct dchub *hub;
773     char *buf;
774     struct fnetnode *cfn;
775     int hn1, hn2, hn3;
776     
777     hub = fn->data;
778     qstrf(sk, "$MyINFO $ALL %s ", hub->nativenick);
779     buf = tr(icswcstombs(confgetstr("dc", "desc"), hub->charset, "Charset_conv_failure"), "$_|_");
780     qstrf(sk, "%s", buf);
781     hn1 = hn2 = hn3 = 0;
782     for(cfn = fnetnodes; cfn != NULL; cfn = cfn->next)
783     {
784         if((cfn->state == FNN_EST) || (cfn->state == FNN_HS))
785         {
786             if(cfn->regstatus == FNNS_OP)
787                 hn3++;
788             else if(cfn->regstatus == FNNS_REG)
789                 hn2++;
790             else
791                 hn1++;
792         }
793     }
794     qstrf(sk, "<%s V:%s,M:%c,H:%i/%i/%i,S:%i>",
795           (hub->dcppemu)?"++":"Dolda",
796           (hub->dcppemu)?"0.674":VERSION,
797           (tcpsock == NULL)?'P':'A',
798           hn1, hn2, hn3,
799           confgetint("transfer", "slots")
800           );
801     qstrf(sk, "$ $");
802     buf = tr(icswcstombs(confgetstr("dc", "speedstring"), hub->charset, "Charset_conv_failure"), "$_|_");
803     qstrf(sk, "%s\x01$", buf);
804     buf = tr(icswcstombs(confgetstr("dc", "email"), hub->charset, "Charset_conv_failure"), "$_|_");
805     qstrf(sk, "%s$", buf);
806     qstrf(sk, "%llu$|", sharesize);
807 }
808
809 static void hubhandleaction(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
810 {
811     struct dchub *hub;
812     
813     hub = fn->data;
814     if(!strcmp(cmd, "$Lock"))
815     {
816         qstrf(sk, "$ValidateNick %s|", hub->nativenick);
817     } else if(!strcmp(cmd, "$Hello")) {
818         if(fn->state == FNN_HS)
819         {
820             qstrf(sk, "$Version 1,0091|");
821             qstrf(sk, "$GetNickList|");
822             sendmyinfo(sk, fn);
823             fnetsetstate(fn, FNN_EST);
824         } else {
825             qstrf(sk, "$GetINFO %s %s|", args, hub->nativenick);
826         }
827     }
828 }
829
830 static void cmd_lock(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
831 {
832     struct dchub *hub;
833     char *key;
834     char *p;
835     
836     hub = fn->data;
837     if(!strncmp(args, "EXTENDEDPROTOCOL", 16))
838         hub->extended = 1;
839     if((p = strchr(args, ' ')) != NULL)
840         *(p++) = 0;
841     if(hub->extended)
842     {
843         if(hub->dcppemu) {
844             qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock |");
845         } else {
846             qstrf(sk, "$Supports UserCommand NoGetINFO NoHello UserIP2 TTHSearch");
847             if(!confgetint("dc", "hidedeflate"))
848                 qstr(sk, " GetZBlock");
849             qstr(sk, "|");
850         }
851     }
852     key = dcmakekey(args);
853     qstrf(sk, "$Key %s|", key);
854     free(key);
855     hubhandleaction(sk, fn, cmd, args);
856 }
857
858 static void cmd_hubname(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
859 {
860     wchar_t *buf;
861     struct dchub *hub;
862     
863     hub = fn->data;
864     if(hub->nativename == NULL)
865         free(hub->nativename);
866     hub->nativename = sstrdup(args);
867     buf = icmbstowcs(args, hub->charset);
868     fnetsetname(fn, (buf == NULL)?L"Hubname conv error":buf);
869     if(buf != NULL)
870         free(buf);
871     hubhandleaction(sk, fn, cmd, args);
872 }
873
874 static void cmd_hello(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
875 {
876     wchar_t *nick;
877     struct dchub *hub;
878     
879     hub = fn->data;
880     if((nick = icmbstowcs(args, hub->charset)) == NULL)
881         return;
882     if(strcmp(args, hub->nativenick) && (fnetfindpeer(fn, nick) == NULL))
883         fnetaddpeer(fn, nick, nick);
884     free(nick);
885     hubhandleaction(sk, fn, cmd, args);
886 }
887
888 static void cmd_quit(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
889 {
890     wchar_t *nick;
891     struct fnetpeer *peer;
892     struct dchub *hub;
893     
894     hub = fn->data;
895     if((nick = icmbstowcs(args, hub->charset)) == NULL)
896         return;
897     if((peer = fnetfindpeer(fn, nick)) != NULL)
898         fnetdelpeer(peer);
899     free(nick);
900     hubhandleaction(sk, fn, cmd, args);
901 }
902
903 static void cmd_nicklist(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
904 {
905     struct dchub *hub;
906     char *p;
907     wchar_t *buf;
908     struct fnetpeer *peer;
909     
910     hub = fn->data;
911     for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL))
912         peer->flags.b.delete = 1;
913     while((p = strstr(args, "$$")) != NULL)
914     {
915         *p = 0;
916         if((buf = icmbstowcs(args, hub->charset)) != NULL)
917         {
918             if((peer = fnetfindpeer(fn, buf)) == NULL)
919                 peer = fnetaddpeer(fn, buf, buf);
920             else
921                 peer->flags.b.delete = 0;
922             free(buf);
923             qstrf(sk, "$GetINFO %s %s|", args, hub->nativenick);
924         }
925         args = p + 2;
926     }
927     fnetpeerdm(fn);
928     hubhandleaction(sk, fn, cmd, args);
929 }
930
931 static void cmd_oplist(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
932 {
933     struct dchub *hub;
934     char *p;
935     wchar_t *buf;
936     struct fnetpeer *peer;
937     
938     hub = fn->data;
939     for(peer = btreeiter(fn->peers); peer != NULL; peer = btreeiter(NULL))
940         peer->flags.b.op = 0;
941     while((p = strstr(args, "$$")) != NULL)
942     {
943         *p = 0;
944         if((buf = icmbstowcs(args, hub->charset)) != NULL)
945         {
946             if((peer = fnetfindpeer(fn, buf)) != NULL)
947                 peer->flags.b.op = 1;
948             free(buf);
949         }
950         args = p + 2;
951     }
952     hubhandleaction(sk, fn, cmd, args);
953 }
954
955 static void cmd_myinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
956 {
957     char *p, *p2;
958     wchar_t *buf, *wp, *wp2;
959     wchar_t abuf[10];
960     struct fnetpeer *peer;
961     struct dchub *hub;
962     
963     hub = fn->data;
964     p = args;
965     if(strncmp(p, "$ALL ", 5))
966         return;
967     p += 5;
968     if((p2 = strchr(p, ' ')) == NULL)
969         return;
970     *p2 = 0;
971     if((buf = icmbstowcs(p, hub->charset)) == NULL)
972         return;
973     if((peer = fnetfindpeer(fn, buf)) == NULL)
974         peer = fnetaddpeer(fn, buf, buf);
975     free(buf);
976     p = p2 + 1;
977     if((p2 = strstr(p, "$ $")) == NULL)
978         return;
979     *p2 = 0;
980     if((buf = icmbstowcs(p, hub->charset)) == NULL)
981         return;
982     if((wcslen(buf) > 0) && (buf[wcslen(buf) - 1] == L'>') && ((wp = wcsrchr(buf, L'<')) != NULL))
983     {
984         buf[wcslen(buf) - 1] = L'\0';
985         *(wp++) = L'\0';
986         if((wp2 = wcschr(wp, L' ')) != NULL)
987         {
988             *(wp2++) = L'\0';
989             fnetpeersetstr(peer, L"dc-client", wp);
990             wp = wp2;
991             do
992             {
993                 if((wp2 = wcschr(wp, L',')) != NULL)
994                     *(wp2++) = L'\0';
995                 if(wp[1] != L':')
996                     continue;
997                 swprintf(abuf, 10, L"dc-tag-%lc", wp[0]);
998                 fnetpeersetstr(peer, abuf, wp + 2);
999                 wp = wp2;
1000             } while(wp2 != NULL);
1001         }
1002     }
1003     fnetpeersetstr(peer, L"descr", buf);
1004     free(buf);
1005     p = p2 + 3;
1006     if((p2 = strchr(p, '$')) == NULL)
1007         return;
1008     *(p2 - 1) = 0;
1009     if((buf = icmbstowcs(p, hub->charset)) == NULL)
1010         return;
1011     fnetpeersetstr(peer, L"dc-speed", buf);
1012     free(buf);
1013     p = p2 + 1;
1014     if((p2 = strchr(p, '$')) == NULL)
1015         return;
1016     *p2 = 0;
1017     if((buf = icmbstowcs(p, hub->charset)) == NULL)
1018         return;
1019     fnetpeersetstr(peer, L"email", buf);
1020     free(buf);
1021     p = p2 + 1;
1022     if(strlen(p) < 1)
1023         return;
1024     fnetpeersetlnum(peer, L"share", strtoll(p, NULL, 10));
1025     hubhandleaction(sk, fn, cmd, args);
1026 }
1027
1028 /* I do not implement the fully in that I do not disconnect from the
1029  * old hub. I do this since I believe that if the hub owner really
1030  * wants to get rid of us, then it is his responsibility to disconnect
1031  * us. */
1032 static void cmd_forcemove(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1033 {
1034     struct dchub *hub;
1035     struct fnetnode *newfn;
1036     int freeargs;
1037     
1038     hub = fn->data;
1039     if(strchr(args, ':') == NULL)
1040     {
1041         args = strcpy(smalloc(strlen(args) + 5), args);
1042         strcat(args, ":411");
1043         freeargs = 1;
1044     } else {
1045         freeargs = 0;
1046     }
1047     if((newfn = fnetinitconnect(L"dc", fn->owner, args, NULL)) != NULL)
1048     {
1049         linkfnetnode(newfn);
1050         putfnetnode(newfn);
1051     }
1052     hubhandleaction(sk, fn, cmd, args);
1053     if(freeargs)
1054         free(args);
1055 }
1056
1057 static char *getdcpath(struct sharecache *node, size_t *retlen, char *charset)
1058 {
1059     char *buf, *buf2;
1060     size_t len, len2;
1061     
1062     if(node->parent == NULL)
1063         return(NULL);
1064     if(node->parent == shareroot)
1065     {
1066         if((buf = icwcstombs(node->name, charset)) == NULL)
1067             return(NULL);
1068         if(retlen != NULL)
1069             *retlen = strlen(buf);
1070         return(buf);
1071     } else {
1072         if((buf2 = icwcstombs(node->name, charset)) == NULL)
1073             return(NULL);
1074         if((buf = getdcpath(node->parent, &len, charset)) == NULL)
1075         {
1076             free(buf2);
1077             return(NULL);
1078         }
1079         len2 = strlen(buf2);
1080         buf = srealloc(buf, len + 1 + len2 + 1);
1081         buf[len++] = '\\';
1082         strcpy(buf + len, buf2);
1083         buf[len + len2] = 0;
1084         free(buf2);
1085         if(retlen != NULL)
1086             *retlen = len + len2;
1087         return(buf);
1088     }
1089 }
1090
1091 /*
1092  * This is the main share searching function for Direct Connect
1093  * peers. Feel free to optimize it if you feel the need for it. I
1094  * haven't ever seen it take any noticable CPU time anyway, so I
1095  * haven't felt that need.
1096  *
1097  * It may feel dubious to just return the directory when all terms are
1098  * satisfied rather than all the files in it, but it really benefits
1099  * everyone: Less cycles used for us, less UDP squelching for active
1100  * searchers, and less bandwidth waste for hubs when serving passive
1101  * searchers.
1102  */
1103 static void cmd_search(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1104 {
1105     int i, done;
1106     struct dchub *hub;
1107     char *p, *p2;
1108     char *prefix, *infix, *postfix, *buf, *buf2;
1109     struct socket *dsk;
1110     struct sockaddr_in addr;
1111     struct sharecache *node;
1112     int minsize, maxsize;
1113     int dotth;
1114     size_t buflen;
1115     int termnum, satisfied, skipcheck;
1116     int level, tersat[32];
1117     wchar_t *terms[32];
1118     char hashtth[24];
1119     
1120     hub = fn->data;
1121     if((p = strchr(args, ' ')) == NULL)
1122         return;
1123     *(p++) = 0;
1124     
1125     memset(terms, 0, sizeof(terms));
1126     prefix = infix = postfix = NULL;
1127     dsk = NULL;
1128     dotth = 0;
1129     
1130     if(!strncmp(args, "Hub:", 4))
1131     {
1132         if(!strcmp(cmd, "$MultiSearch"))
1133             goto out;
1134         if(!strcmp(args + 4, hub->nativenick))
1135             goto out;
1136         prefix = sprintf2("$SR %s ", hub->nativenick);
1137         infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
1138         postfix = sprintf2(" (%s)\005%s|", formataddress(hub->sk->remote, hub->sk->remotelen), args + 4);
1139         dsk = sk;
1140         getsock(dsk);
1141     } else {
1142         if((p2 = strchr(args, ':')) == NULL)
1143             goto out;
1144         *(p2++) = 0;
1145         addr.sin_family = AF_INET;
1146         if(!inet_aton(args, &addr.sin_addr))
1147             goto out;
1148         addr.sin_port = htons(atoi(p2));
1149         prefix = sprintf2("$SR %s ", hub->nativenick);
1150         infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
1151         postfix = sprintf2(" (%s)|", formataddress(hub->sk->remote, hub->sk->remotelen));
1152         netdgramconn(dsk = netdupsock(udpsock), (struct sockaddr *)&addr, sizeof(addr));
1153     }
1154     
1155     minsize = maxsize = -1;
1156     if(*p == 0)
1157         goto out;
1158     if(*(p++) == 'T')
1159         minsize = 0;
1160     if(*(p++) != '?')
1161         goto out;
1162     if(*p == 0)
1163         goto out;
1164     if((*(p++) == 'T') && (minsize == 0))
1165     {
1166         maxsize = 0;
1167         minsize = -1;
1168     }
1169     if(*(p++) != '?')
1170         goto out;
1171     if((p2 = strchr(p, '?')) == NULL)
1172         goto out;
1173     *(p2++) = 0;
1174     if(minsize == 0)
1175         minsize = atoi(p);
1176     if(maxsize == 0)
1177         maxsize = atoi(p);
1178     p = p2 + 1;
1179     if(*(p++) != '?')
1180         goto out;
1181     termnum = 0;
1182     p2 = p;
1183     done = 0;
1184     while(!done)
1185     {
1186         if((*p2 == 0) || (*p2 == '$'))
1187         {
1188             if(*p2 == 0)
1189                 done = 1;
1190             else
1191                 *p2 = 0;
1192             if(*p)
1193             {
1194                 if(!dotth && !strncmp(p, "TTH:", 4))
1195                 {
1196                     dotth = 1;
1197                     if(((buf = base32decode(p + 4, &buflen)) == NULL) || (buflen != 24))
1198                     {
1199                         free(buf);
1200                         goto out;
1201                     }
1202                     memcpy(hashtth, buf, 24);
1203                     free(buf);
1204                 } else {
1205                     if((terms[termnum] = icmbstowcs(p, hub->charset)) != NULL)
1206                         termnum++;
1207                 }
1208             }
1209             p = p2 + 1;
1210             if(termnum == 32)
1211                 break;
1212         }
1213         p2++;
1214     }
1215     
1216     node = shareroot->child;
1217     level = 0;
1218     for(i = 0; i < termnum; i++)
1219         tersat[i] = -1;
1220     satisfied = 0;
1221     while(1)
1222     {
1223         skipcheck = 0;
1224         if(node->f.b.type == FILE_REG)
1225         {
1226             if((minsize >= 0) && (node->size < minsize))
1227                 skipcheck = 1;
1228             if((maxsize >= 0) && (node->size > maxsize))
1229                 skipcheck = 1;
1230         }
1231         if(!skipcheck && dotth)
1232         {
1233             if((node->f.b.type != FILE_REG) || (node->f.b.hastth && memcmp(hashtth, node->hashtth, 24)))
1234                 skipcheck = 1;
1235         }
1236         if(!skipcheck)
1237         {
1238             for(i = 0; i < termnum; i++)
1239             {
1240                 if(tersat[i] >= 0)
1241                     continue;
1242                 if(wcsexists(node->name, terms[i]))
1243                 {
1244                     tersat[i] = level;
1245                     satisfied++;
1246                 } else if(node->child == NULL) {
1247                     break;
1248                 }
1249             }
1250         }
1251         if(!skipcheck && (satisfied == termnum))
1252         {
1253             /* Use DCCHARSET in $Get paths until further researched... */
1254             if((buf = getdcpath(node, NULL, DCCHARSET)) != NULL)
1255             {
1256                 if(node->f.b.hastth)
1257                 {
1258                     buf2 = base32encode(node->hashtth, 24);
1259                     qstrf(dsk, "%s%s\005%ji%sTTH:%.39s%s", prefix, buf, (intmax_t)node->size, infix, buf2, postfix);
1260                     free(buf2);
1261                 } else {
1262                     qstrf(dsk, "%s%s\005%ji%s%s%s", prefix, buf, (intmax_t)node->size, infix, hub->nativename, postfix);
1263                 }
1264                 free(buf);
1265             }
1266         }
1267         if((!skipcheck && (satisfied == termnum)) || (node->child == NULL))
1268         {
1269             while(node->next == NULL)
1270             {
1271                 if((node = node->parent) == shareroot)
1272                     break;
1273                 level--;
1274             }
1275             if(node == shareroot)
1276                 break;
1277             for(i = 0; i < termnum; i++)
1278             {
1279                 if(tersat[i] >= level)
1280                 {
1281                     tersat[i] = -1;
1282                     satisfied--;
1283                 }
1284             }
1285             node = node->next;
1286         } else {
1287             node = node->child;
1288             level++;
1289         }
1290     }
1291
1292     hubhandleaction(sk, fn, cmd, args);
1293     
1294  out:
1295     if(dsk != NULL)
1296         putsock(dsk);
1297     if(prefix != NULL)
1298         free(prefix);
1299     if(infix != NULL)
1300         free(infix);
1301     if(postfix != NULL)
1302         free(postfix);
1303     for(i = 0; (i < 32) && (terms[i] != NULL); i++)
1304         free(terms[i]);
1305 }
1306
1307 static void cmd_connecttome(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1308 {
1309     char *p;
1310     struct dchub *hub;
1311     struct socket *newsk;
1312     struct sockaddr_in addr;
1313     
1314     hub = fn->data;
1315     if((p = strchr(args, ' ')) == NULL)
1316         return;
1317     *(p++) = 0;
1318     if(strcmp(args, hub->nativenick))
1319         return;
1320     addr.sin_family = AF_INET;
1321     args = p;
1322     if((p = strchr(args, ':')) == NULL)
1323         return;
1324     *(p++) = 0;
1325     addr.sin_port = htons(atoi(p));
1326     if(!inet_aton(args, &addr.sin_addr))
1327         return;
1328     newsk = netcsconn((struct sockaddr *)&addr, sizeof(addr), (void (*)(struct socket *, int, void *))peerconnect, fn);
1329     getfnetnode(fn);
1330     hubhandleaction(sk, fn, cmd, args);
1331 }
1332
1333 static void sendctm(struct socket *sk, char *nick)
1334 {
1335     struct sockaddr *addr;
1336     socklen_t addrlen;
1337     
1338     if(tcpsock == NULL)
1339         return;
1340     if(sockgetremotename2(tcpsock, sk, &addr, &addrlen) < 0)
1341         return;
1342     if(addr->sa_family == AF_INET)
1343         qstrf(sk, "$ConnectToMe %s %s|", nick, formataddress(addr, addrlen));
1344     else
1345         flog(LOG_WARNING, "Direct Connect TCP socket is suddenly not AF_INET, but %i", addr->sa_family);
1346     free(addr);
1347 }
1348
1349 static void cmd_revconnecttome(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1350 {
1351     struct dchub *hub;
1352     char *p;
1353     
1354     hub = fn->data;
1355     if((p = strchr(args, ' ')) == NULL)
1356         return;
1357     *(p++) = 0;
1358     if(strcmp(p, hub->nativenick))
1359         return;
1360     sendctm(sk, args);
1361     expectpeer(args, fn);
1362     hubhandleaction(sk, fn, cmd, args);
1363 }
1364
1365 static void cmd_getnetinfo(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1366 {
1367     struct dchub *hub;
1368     struct fnetnode *node;
1369     int numhubs;
1370     
1371     hub = fn->data;
1372     numhubs = 0;
1373     for(node = fnetnodes; node != NULL; node = node->next)
1374     {
1375         if(node->state == FNN_EST)
1376             numhubs++;
1377     }
1378     qstrf(sk, "$NetInfo %i$%i$%c$0|", confgetint("transfer", "slots"), numhubs, (tcpsock == NULL)?'P':'A');
1379     hubhandleaction(sk, fn, cmd, args);
1380 }
1381
1382 static void cmd_to(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1383 {
1384     struct dchub *hub;
1385     char *p, *p2;
1386     
1387     hub = fn->data;
1388     p = args;
1389     if((p2 = strchr(p, ' ')) == NULL)
1390         return;
1391     *(p2++) = 0;
1392     p = p2;
1393     if((p2 = strchr(p, ' ')) == NULL)
1394         return;
1395     *(p2++) = 0;
1396     if(strcmp(p, "From:"))
1397         return;
1398     p = p2;
1399     if((p2 = strstr(p, " $")) == NULL)
1400         return;
1401     *p2 = 0;
1402     p2 += 2;
1403     hubrecvchat(hub->sk, fn, p, p2);
1404     hubhandleaction(sk, fn, cmd, args);
1405 }
1406
1407 static void cmd_sr(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1408 {
1409     struct dchub *hub;
1410     char *p, *p2, *buf;
1411     char *nick, *filename, *hubname;
1412     off_t size;
1413     int 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 = strtoll(p, NULL, 10);
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     off_t size;
1708     struct transfer *transfer;
1709     
1710     if(peer->transfer == NULL)
1711     {
1712         freedcpeer(peer);
1713         return;
1714     }
1715     size = strtoll(args, NULL, 10);
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     off_t 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 = (strtoll(p, NULL, 10) - 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 %ji|", (intmax_t)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     off_t 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 = strtoll(p, NULL, 10);
1991     p = p2;
1992     if((p2 = strchr(p, ' ')) == NULL)
1993     {
1994         freedcpeer(peer);
1995         return;
1996     }
1997     *(p2++) = 0;
1998     numbytes = strtoll(p, NULL, 10);
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 %ji|", (intmax_t)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     off_t 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 = strtoll(argv[2], NULL, 10);
2091     numbytes = strtoll(argv[3], NULL, 10);
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, "%ji", (intmax_t)start);
2165         sendadcf(sk, "%ji", (intmax_t)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     off_t 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 = strtoll(argv[2], NULL, 10);
2244     numbytes = strtoll(argv[3], NULL, 10);
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     off_t numbytes;
2293     
2294     if(peer->transfer == NULL)
2295     {
2296         freedcpeer(peer);
2297         return;
2298     }
2299     numbytes = strtoll(args, NULL, 10);
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     off_t size;
2815     int slots;
2816     struct fnetnode *fn, *myfn;
2817     struct dchub *hub;
2818     struct srchres *sr;
2819     wchar_t *wnick, *wfile;
2820     struct hash *hash;
2821     
2822     if((buf = sockgetinbuf(sk, &buflen)) == NULL)
2823         return;
2824     buf = srealloc(buf, buflen + 1);
2825     buf[buflen] = 0;
2826     if(!strncmp(buf, "$SR ", 4))
2827     {
2828         p = buf + 4;
2829         nick = p;
2830         if((p2 = strchr(p, ' ')) == NULL)
2831         {
2832             free(buf);
2833             return;
2834         }
2835         *p2 = 0;
2836         p = p2 + 1;
2837         filename = p;
2838         if((p2 = strchr(p, 5)) == NULL)
2839         {
2840             free(buf);
2841             return;
2842         }
2843         *p2 = 0;
2844         p = p2 + 1;
2845         if((p2 = strchr(p, ' ')) == NULL)
2846         {
2847             free(buf);
2848             return;
2849         }
2850         *p2 = 0;
2851         size = strtoll(p, NULL, 10);
2852         p = p2 + 1;
2853         if((p2 = strchr(p, '/')) == NULL)
2854         {
2855             free(buf);
2856             return;
2857         }
2858         *p2 = 0;
2859         slots = atoi(p);
2860         p = p2 + 1;
2861         if((p2 = strchr(p, 5)) == NULL)
2862         {
2863             free(buf);
2864             return;
2865         }
2866         p = p2 + 1;
2867         hubname = p;
2868         if((p2 = strstr(p, " (")) == NULL)
2869         {
2870             free(buf);
2871             return;
2872         }
2873         *p2 = 0;
2874         p = p2 + 2;
2875         if((p2 = strchr(p, ':')) == NULL)
2876         {
2877             free(buf);
2878             return;
2879         }
2880         *(p2++) = 0;
2881         hubaddr.sin_family = AF_INET;
2882         if(!inet_aton(p, &hubaddr.sin_addr))
2883         {
2884             free(buf);
2885             return;
2886         }
2887         p = p2;
2888         if((p2 = strchr(p, ')')) == NULL)
2889         {
2890             free(buf);
2891             return;
2892         }
2893         *p2 = 0;
2894         hubaddr.sin_port = htons(atoi(p));
2895         /* Use DCCHARSET in $Get paths until further researched... */
2896         if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
2897         {
2898             free(buf);
2899             return;
2900         }
2901         myfn = NULL;
2902         hash = NULL;
2903         if(!strncmp(hubname, "TTH:", 4))
2904         {
2905             if((hashbuf = base32decode(hubname + 4, &hashlen)) != NULL)
2906             {
2907                 if(hashlen == 24)
2908                     hash = newhash(L"TTH", 24, hashbuf);
2909                 free(hashbuf);
2910             }
2911         } else {
2912             for(fn = fnetnodes; fn != NULL; fn = fn->next)
2913             {
2914                 if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
2915                 {
2916                     if((hub->nativename != NULL) && !strcmp(hub->nativename, hubname))
2917                     {
2918                         if(myfn == NULL)
2919                         {
2920                             myfn = fn;
2921                         } else {
2922                             myfn = NULL;
2923                             break;
2924                         }
2925                     }
2926                 }
2927             }
2928         }
2929         if(myfn == NULL)
2930         {
2931             for(fn = fnetnodes; fn != NULL; fn = fn->next)
2932             {
2933                 if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
2934                 {
2935                     if((hub->sk != NULL) && addreq(hub->sk->remote, (struct sockaddr *)&hubaddr))
2936                     {
2937                         myfn = fn;
2938                         break;
2939                     }
2940                 }
2941             }
2942         }
2943         hub = NULL;
2944         if(myfn != NULL)
2945             hub = myfn->data;
2946         if((wnick = icmbstowcs(nick, (hub == NULL)?DCCHARSET:(hub->charset))) == NULL)
2947         {
2948             free(buf);
2949             return;
2950         }
2951         sr = newsrchres(&dcnet, wfile, wnick);
2952         if(sr->peernick != NULL)
2953             free(sr->peernick);
2954         sr->peernick = swcsdup(wnick);
2955         sr->size = size;
2956         sr->slots = slots;
2957         free(wfile);
2958         free(wnick);
2959         if(myfn != NULL)
2960             getfnetnode(sr->fn = myfn);
2961         if(hash != NULL)
2962             sr->hash = hash;
2963         submitsrchres(sr);
2964         freesrchres(sr);
2965     }
2966     free(buf);
2967 }
2968
2969 static void hubread(struct socket *sk, struct fnetnode *fn)
2970 {
2971     struct dchub *hub;
2972     char *newbuf;
2973     size_t datalen;
2974     char *p;
2975     
2976     hub = (struct dchub *)fn->data;
2977     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
2978         return;
2979     if(hub->inbufdata > 500000) /* Discard possible malicious data */
2980         hub->inbufdata = 0;
2981     sizebuf2(hub->inbuf, hub->inbufdata + datalen, 1);
2982     memcpy(hub->inbuf + hub->inbufdata, newbuf, datalen);
2983     free(newbuf);
2984     p = hub->inbuf + hub->inbufdata;
2985     hub->inbufdata += datalen;
2986     while((datalen > 0) && ((p = memchr(p, '|', datalen)) != NULL))
2987     {
2988         *(p++) = 0;
2989         newqcmd(&hub->queue, hub->inbuf);
2990         memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
2991         datalen = hub->inbufdata;
2992         p = hub->inbuf;
2993     }
2994 }
2995
2996 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
2997 {
2998     killfnetnode(fn);
2999 }
3000
3001 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
3002 {
3003     struct dchub *hub;
3004     char *buf;
3005     
3006     hub = fn->data;
3007     if((buf = icwcstombs(newnick, (hub == NULL)?DCCHARSET:(hub->charset))) == NULL)
3008         return(1);
3009     if((strchr(buf, ' ') != NULL) || (strchr(buf, '|') != NULL) || (strchr(buf, '$') != NULL))
3010     {
3011         free(buf);
3012         return(1);
3013     }
3014     if(hub == NULL) /* Not yet connected */
3015     {
3016         free(buf);
3017         return(0);
3018     }
3019     if(hub->nativenick != NULL)
3020         free(hub->nativenick);
3021     hub->nativenick = buf;
3022     return(0);
3023 }
3024
3025 static struct dchub *newdchub(struct fnetnode *fn)
3026 {
3027     struct dchub *new;
3028     wchar_t *emu;
3029     wchar_t *wcharset;
3030     char *charset;
3031     
3032     new = smalloc(sizeof(*new));
3033     memset(new, 0, sizeof(*new));
3034     fn->data = new;
3035     if(confgetint("dc", "dcppemu"))
3036         new->dcppemu = 1;
3037     if((emu = wpfind(fn->args, L"dcppemu")) != NULL)
3038     {
3039         if(*emu == L'y')
3040             new->dcppemu = 1;
3041         if(*emu == L'n')
3042             new->dcppemu = 0;
3043     }
3044     charset = NULL;
3045     if((wcharset = wpfind(fn->args, L"charset")) != NULL)
3046     {
3047         if((charset = icwcstombs(wcharset, "US-ASCII")) != NULL)
3048         {
3049             if(!havecharset(charset))
3050             {
3051                 free(charset);
3052                 charset = NULL;
3053             }
3054         }
3055     }
3056     if(charset != NULL)
3057         new->charset = charset;
3058     else
3059         new->charset = sstrdup(DCCHARSET);
3060     if(hubsetnick(fn, fn->mynick))
3061         fnetsetnick(fn, L"DoldaConnectUser-IN");
3062     /* IN as in Invalid Nick */
3063     return(new);
3064 }
3065
3066 static struct dcpeer *newdcpeer(struct socket *sk)
3067 {
3068     struct dcpeer *new;
3069     
3070     new = smalloc(sizeof(*new));
3071     memset(new, 0, sizeof(*new));
3072     new->transfer = NULL;
3073     getsock(sk);
3074     new->sk = sk;
3075     if(confgetint("dc", "dcppemu"))
3076         new->dcppemu = 1;
3077     new->next = peers;
3078     new->prev = NULL;
3079     if(peers != NULL)
3080         peers->prev = new;
3081     peers = new;
3082     numdcpeers++;
3083     return(new);
3084 }
3085
3086 static void freedcpeer(struct dcpeer *peer)
3087 {
3088     int i;
3089     struct qcommand *qcmd;
3090     
3091     peer->freeing = 1;
3092     if(peers == peer)
3093         peers = peer->next;
3094     if(peer->next != NULL)
3095         peer->next->prev = peer->prev;
3096     if(peer->prev != NULL)
3097         peer->prev->next = peer->next;
3098     if(peer->transfer != NULL)
3099     {
3100         if(peer->transfer->dir == TRNSD_UP)
3101             peer->transfer->close = 1;
3102         if(peer->transfer->dir == TRNSD_DOWN)
3103             resettransfer(peer->transfer);
3104         transferdetach(peer->transfer);
3105     }
3106     if(peer->timeout != NULL)
3107         canceltimer(peer->timeout);
3108     if(peer->sk->data == peer)
3109         peer->sk->data = NULL;
3110     peer->sk->readcb = NULL;
3111     peer->sk->writecb = NULL;
3112     peer->sk->errcb = NULL;
3113     putsock(peer->sk);
3114     endcompress(peer);
3115     if(peer->supports != NULL)
3116     {
3117         for(i = 0; peer->supports[i] != NULL; i++)
3118             free(peer->supports[i]);
3119         free(peer->supports);
3120     }
3121     if(peer->inbuf != NULL)
3122         free(peer->inbuf);
3123     if(peer->key != NULL)
3124         free(peer->key);
3125     if(peer->wcsname != NULL)
3126         free(peer->wcsname);
3127     if(peer->nativename != NULL)
3128         free(peer->nativename);
3129     if(peer->charset != NULL)
3130         free(peer->charset);
3131     if(peer->fn != NULL)
3132         putfnetnode(peer->fn);
3133     while((qcmd = ulqcmd(&peer->queue)) != NULL)
3134         freeqcmd(qcmd);
3135     free(peer);
3136     numdcpeers--;
3137 }
3138
3139 static void hubconnect(struct fnetnode *fn, struct socket *sk)
3140 {
3141     struct dchub *hub;
3142     
3143     sk->readcb = (void (*)(struct socket *, void *))hubread;
3144     sk->errcb = (void (*)(struct socket *, int, void *))huberr;
3145     fn->data = hub = newdchub(fn);
3146     sk->data = fn;
3147     getsock(hub->sk = sk);
3148     return;
3149 }
3150
3151 static void hubdestroy(struct fnetnode *fn)
3152 {
3153     int i;
3154     struct dchub *hub;
3155     struct qcommand *qcmd;
3156     
3157     hub = (struct dchub *)fn->data;
3158     putsock(hub->sk);
3159     while((qcmd = ulqcmd(&hub->queue)) != NULL)
3160         freeqcmd(qcmd);
3161     if(hub->supports != NULL)
3162     {
3163         for(i = 0; hub->supports[i] != NULL; i++)
3164             free(hub->supports[i]);
3165         free(hub->supports);
3166     }
3167     if(hub->nativename != NULL)
3168         free(hub->nativename);
3169     if(hub->nativenick != NULL)
3170         free(hub->nativenick);
3171     if(hub->charset != NULL)
3172         free(hub->charset);
3173     if(hub->inbuf != NULL)
3174         free(hub->inbuf);
3175     free(hub);
3176 }
3177
3178 static void hubkill(struct fnetnode *fn)
3179 {
3180     struct dchub *hub;
3181     
3182     hub = (struct dchub *)fn->data;
3183     hub->sk->close = 1;
3184 }
3185
3186 static wchar_t *dcbasename(wchar_t *filename)
3187 {
3188     wchar_t *ret;
3189     
3190     if((ret = wcsrchr(filename, L'\\')) != NULL)
3191         return(ret + 1);
3192     return(filename);
3193 }
3194
3195 static struct transferiface dctransfer =
3196 {
3197     .detach = (void (*)(struct transfer *, void *))dctransdetach,
3198     .gotdata = (void (*)(struct transfer *, void *))dctransgotdata,
3199     .endofdata = (void (*)(struct transfer *, void *))dctransendofdata,
3200     .wantdata = (void (*)(struct transfer *, void *))dcwantdata
3201 };
3202
3203 static struct fnet dcnet =
3204 {
3205     .name = L"dc",
3206     .connect = hubconnect,
3207     .destroy = hubdestroy,
3208     .kill = hubkill,
3209     .setnick = hubsetnick,
3210     .reqconn = hubreqconn,
3211     .sendchat = hubsendchat,
3212     .search = hubsearch,
3213     .filebasename = dcbasename
3214 };
3215
3216 static void peerread(struct socket *sk, struct dcpeer *peer)
3217 {
3218     char *newbuf, *p;
3219     size_t datalen;
3220     struct command *cmd;
3221
3222     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
3223         return;
3224     sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
3225     memcpy(peer->inbuf + peer->inbufdata, newbuf, datalen);
3226     free(newbuf);
3227     peer->inbufdata += datalen;
3228     if(peer->state == PEER_CMD)
3229     {
3230         p = peer->inbuf;
3231         while((peer->inbufdata > 0) && (p = memchr(peer->inbuf, '|', peer->inbufdata)) != NULL)
3232         {
3233             *(p++) = 0;
3234             newqcmd(&peer->queue, peer->inbuf);
3235             for(cmd = peercmds; cmd->handler != NULL; cmd++)
3236             {
3237                 if(!memcmp(peer->inbuf, cmd->name, strlen(cmd->name)) && ((peer->inbuf[strlen(cmd->name)] == ' ') || (peer->inbuf[strlen(cmd->name)] == '|')))
3238                     break;
3239             }
3240             memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
3241             if(cmd->stop)
3242             {
3243                 peer->state = PEER_STOP;
3244                 break;
3245             }
3246         }
3247     } else if(peer->state == PEER_TTHL) {
3248         handletthl(peer);
3249     }
3250 }
3251
3252 static void peererror(struct socket *sk, int err, struct dcpeer *peer)
3253 {
3254     freedcpeer(peer);
3255 }
3256
3257 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
3258 {
3259     struct dcpeer *peer;
3260     struct dchub *hub;
3261     
3262     if(err != 0)
3263     {
3264         putfnetnode(fn);
3265         putsock(sk);
3266         return;
3267     }
3268     hub = fn->data;
3269     peer = newdcpeer(sk);
3270     peer->fn = fn;
3271     peer->accepted = 0;
3272     peer->dcppemu = hub->dcppemu;
3273     sk->readcb = (void (*)(struct socket *, void *))peerread;
3274     sk->errcb = (void (*)(struct socket *, int, void *))peererror;
3275     sk->data = peer;
3276     socksettos(sk, confgetint("fnet", "fnptos"));
3277     putsock(sk);
3278     peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
3279     sendmynick(peer);
3280     sendpeerlock(peer);
3281 }
3282
3283 static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
3284 {
3285     struct dcpeer *peer;
3286     
3287     peer = newdcpeer(newsk);
3288     peer->accepted = 1;
3289     newsk->readcb = (void (*)(struct socket *, void *))peerread;
3290     newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
3291     newsk->data = peer;
3292     socksettos(newsk, confgetint("fnet", "fnptos"));
3293     peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
3294 }
3295
3296 static void updatehmlist(void)
3297 {
3298     int i, lev, ic, ret;
3299     struct sharecache *node;
3300     char *buf, *buf2, numbuf[32];
3301     size_t bufsize, bufdata;
3302     int fd, ibuf;
3303     
3304     bufdata = 0;
3305     buf = smalloc(bufsize = 65536);
3306     node = shareroot->child;
3307     lev = 0;
3308     while(1)
3309     {
3310         ic = 0;
3311         /* Use DCCHARSET in $Get paths until further researched... */
3312         if((buf2 = icwcstombs(node->name, DCCHARSET)) != NULL)
3313         {
3314             for(i = 0; i < lev; i++)
3315                 addtobuf(buf, 9);
3316             bufcat(buf, buf2, strlen(buf2));
3317             free(buf2);
3318             if(node->f.b.type == FILE_REG)
3319             {
3320                 addtobuf(buf, '|');
3321                 sprintf(numbuf, "%ji", (intmax_t)node->size);
3322                 bufcat(buf, numbuf, strlen(numbuf));
3323             }
3324             addtobuf(buf, 13);
3325             addtobuf(buf, 10);
3326         } else {
3327             ic = 1;
3328         }
3329         if((node->child != NULL) && !ic)
3330         {
3331             lev++;
3332             node = node->child;
3333         } else if(node->next != NULL) {
3334             node = node->next;
3335         } else {
3336             while(node->next == NULL)
3337             {
3338                 lev--;
3339                 node = node->parent;
3340                 if(node == shareroot)
3341                     break;
3342             }
3343             if(node == shareroot)
3344                 break;
3345             node = node->next;
3346         }
3347     }
3348     if(hmlistname != NULL)
3349     {
3350         unlink(hmlistname);
3351         free(hmlistname);
3352     }
3353     hmlistname = sstrdup("/tmp/dc-filelist-hm-XXXXXX");
3354     if((fd = mkstemp(hmlistname)) < 0)
3355     {
3356         flog(LOG_WARNING, "could not create HM file list tempfile: %s", strerror(errno));
3357         free(hmlistname);
3358         hmlistname = NULL;
3359     } else {
3360         /*
3361          * I do not want to implement a good Huffman encoder, and it's not
3362          * like Huffman encoding actually yields any impressive results
3363          * for DC file lists anyway, so I'll just output a bogus
3364          * tree. Implement a good encoder if you want to.
3365          */
3366         write(fd, "HE3\r\0", 5);
3367         write(fd, &bufdata, 4);
3368         ibuf = 256;
3369         write(fd, &ibuf, 2);
3370         ibuf = 8;
3371         for(i = 0; i < 256; i++)
3372         {
3373             write(fd, &i, 1);
3374             write(fd, &ibuf, 1);
3375         }
3376         for(i = 0; i < 256; i++)
3377             write(fd, &i, 1);
3378         for(buf2 = buf; bufdata > 0;)
3379         {
3380             if((ret = write(fd, buf2, bufdata)) < 0)
3381             {
3382                 flog(LOG_WARNING, "could not write file list: %s", strerror(errno));
3383                 break;
3384             }
3385             bufdata -= ret;
3386             buf2 += ret;
3387         }
3388         close(fd);
3389     }
3390     free(buf);
3391 }
3392
3393 static struct xmlent
3394 {
3395     wchar_t c;
3396     wchar_t *ent;
3397 } entities[] = {
3398     {L'&', L"&amp;"},
3399     {L'\"', L"&#34;"},
3400 /*  {L'\'', L"&quot;"},  Emulate DC++ escaping by omitting this. */
3401     {L'\0', NULL}
3402 };
3403
3404 static wchar_t *escapexml(wchar_t *src)
3405 {
3406     static wchar_t *buf = NULL;;
3407     int ret, c;
3408     wchar_t nbuf[32];
3409     size_t bufsize, bufdata;
3410     struct xmlent *ent;
3411     
3412     if(buf != NULL)
3413         free(buf);
3414     buf = NULL;
3415     bufsize = bufdata = 0;
3416     for(; *src != L'\0'; src++)
3417     {
3418         c = wctob(*src);
3419         if((c > 0) && (c < 32))
3420         {
3421             bufcat(buf, L"&#", 2);
3422             ret = swprintf(nbuf, (sizeof(nbuf) / sizeof(*nbuf)), L"%i", c);
3423             bufcat(buf, nbuf, ret);
3424             addtobuf(buf, L';');
3425         } else {
3426             for(ent = entities; ent->ent != NULL; ent++)
3427             {
3428                 if(ent->c == *src)
3429                 {
3430                     bufcat(buf, ent->ent, wcslen(ent->ent));
3431                     break;
3432                 }
3433             }
3434             if(ent->ent == NULL)
3435                 addtobuf(buf, *src);
3436         }
3437     }
3438     addtobuf(buf, L'\0');
3439     return(buf);
3440 }
3441
3442 static void updatexmllist(void)
3443 {
3444     int i, fd, lev;
3445     FILE *fs;
3446     char cidbuf[14], *namebuf;
3447     char *hashbuf;
3448     struct sharecache *node;
3449     
3450     if(xmllistname != NULL)
3451     {
3452         unlink(xmllistname);
3453         free(xmllistname);
3454     }
3455     xmllistname = sstrdup("/tmp/dc-filelist-dcxml-XXXXXX");
3456     if((fd = mkstemp(xmllistname)) < 0)
3457     {
3458         flog(LOG_WARNING, "could not create XML file list tempfile: %s", strerror(errno));
3459         free(xmllistname);
3460         xmllistname = NULL;
3461         return;
3462     }
3463     if((fs = fdopen(fd, "w")) == NULL)
3464     {
3465         flog(LOG_WARNING, "could not fdopen XML list fd %i: %s", fd, strerror(errno));
3466         unlink(xmllistname);
3467         free(xmllistname);
3468         xmllistname = NULL;
3469         close(fd);
3470         return;
3471     }
3472     fprintf(fs, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n");
3473     for(i = 0; i < sizeof(cidbuf) - 1; i++)
3474         cidbuf[i] = (rand() % ('Z' - 'A' + 1)) + 'A';
3475     cidbuf[i] = 0;
3476     if(confgetint("dc", "dcppemu"))
3477         fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"DC++ 0.674\">\r\n", cidbuf);
3478     else
3479         fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, "DoldaConnect" VERSION);
3480     
3481     node = shareroot->child;
3482     lev = 0;
3483     while(1)
3484     {
3485         if((namebuf = icswcstombs(escapexml(node->name), "UTF-8", NULL)) != NULL)
3486         {
3487             for(i = 0; i < lev; i++)
3488                 fputc('\t', fs);
3489             if(node->child != NULL)
3490             {
3491                 fprintf(fs, "<Directory Name=\"%s\">\r\n", namebuf);
3492                 node = node->child;
3493                 lev++;
3494                 continue;
3495             } else {
3496                 fprintf(fs, "<File Name=\"%s\" Size=\"%ji\"", namebuf, (intmax_t)node->size);
3497                 if(node->f.b.hastth)
3498                 {
3499                     hashbuf = base32encode(node->hashtth, 24);
3500                     fprintf(fs, " TTH=\"%.39s\"", hashbuf);
3501                     free(hashbuf);
3502                 }
3503                 fprintf(fs, "/>\r\n");
3504             }
3505             while(node->next == NULL)
3506             {
3507                 node = node->parent;
3508                 if(node == shareroot)
3509                 {
3510                     break;
3511                 } else {
3512                     lev--;
3513                     for(i = 0; i < lev; i++)
3514                         fputc('\t', fs);
3515                     fprintf(fs, "</Directory>\r\n");
3516                 }
3517             }
3518             if(node == shareroot)
3519                 break;
3520             node = node->next;
3521         }
3522     }
3523     
3524     if(confgetint("dc", "dcppemu"))
3525         fprintf(fs, "</FileListing>");
3526     else
3527         fprintf(fs, "</FileListing>\r\n");
3528     fclose(fs);
3529 }
3530
3531 static void updatexmlbz2list(void)
3532 {
3533     int err, fd;
3534     FILE *in;
3535     FILE *real;
3536     BZFILE *out;
3537     char buf[1024];
3538     size_t bufdata;
3539     
3540     if(xmllistname == NULL)
3541         return;
3542     if((in = fopen(xmllistname, "r")) == NULL)
3543     {
3544         flog(LOG_WARNING, "could not open XML file list for bzipping: %s", strerror(errno));
3545         return;
3546     }
3547     if(xmlbz2listname != NULL)
3548     {
3549         unlink(xmlbz2listname);
3550         free(xmlbz2listname);
3551     }
3552     xmlbz2listname = sstrdup("/tmp/dc-filelist-dcxmlbz2-XXXXXX");
3553     if((fd = mkstemp(xmlbz2listname)) < 0)
3554     {
3555         flog(LOG_WARNING, "could not create bzipped XML file list tempfile: %s", strerror(errno));
3556         free(xmlbz2listname);
3557         xmlbz2listname = NULL;
3558         fclose(in);
3559         return;
3560     }
3561     if((real = fdopen(fd, "w")) == NULL)
3562     {
3563         flog(LOG_WARNING, "could not fdopen bzipped XML list fd %i: %s", fd, strerror(errno));
3564         close(fd);
3565         unlink(xmlbz2listname);
3566         free(xmlbz2listname);
3567         xmlbz2listname = NULL;
3568         fclose(in);
3569         return;
3570     }
3571     out = BZ2_bzWriteOpen(&err, real, 9, 0, 0);
3572     if(err != BZ_OK)
3573     {
3574         flog(LOG_WARNING, "could not open bzip2 stream from XML list");
3575         fclose(real);
3576         unlink(xmlbz2listname);
3577         free(xmlbz2listname);
3578         xmlbz2listname = NULL;
3579         fclose(in);
3580         return;
3581     }
3582     while(!feof(in))
3583     {
3584         bufdata = fread(buf, 1, sizeof(buf), in);
3585         BZ2_bzWrite(&err, out, buf, bufdata);
3586     }
3587     fclose(in);
3588     BZ2_bzWriteClose(&err, out, 0, NULL, NULL);
3589     fclose(real);
3590 }
3591
3592 static void listtimercb(int cancelled, void *uudata)
3593 {
3594     listwritetimer = NULL;
3595     if(!cancelled)
3596         updatelists(1);
3597 }
3598
3599 static void updatelists(int now)
3600 {
3601     if((hmlistname == NULL) || (xmllistname == NULL) || (xmlbz2listname == NULL))
3602         now = 1;
3603     if(!now)
3604     {
3605         if(listwritetimer == NULL)
3606             listwritetimer = timercallback(ntime() + confgetint("cli", "hashwritedelay"), listtimercb, NULL);
3607         return;
3608     }
3609     if(listwritetimer != NULL)
3610         canceltimer(listwritetimer);
3611     updatehmlist();
3612     updatexmllist();
3613     updatexmlbz2list();
3614 }
3615
3616 static int shareupdate(unsigned long long uusharesize, void *data)
3617 {
3618     updatelists(0);
3619     return(0);
3620 }
3621
3622 static char *quotestr(char *str)
3623 {
3624     unsigned char *buf;
3625     unsigned char *p;
3626     size_t bufsize, bufdata;
3627     wchar_t *wbuf;
3628     static char *enc = NULL;
3629     size_t encsize, encdata;
3630     
3631     buf = NULL;
3632     bufsize = bufdata = 0;
3633     for(p = (unsigned char *)str; *p; p++)
3634     {
3635         if(*p == '\b')
3636             bufcat(buf, "\\b", 2);
3637         else if(*p == '\t')
3638             bufcat(buf, "\\t", 2);
3639         else if(*p == '\n')
3640             bufcat(buf, "\\n", 2);
3641         else if(*p == '\r')
3642             bufcat(buf, "\\r", 2);
3643         else if(*p == '\\')
3644             bufcat(buf, "\\\\", 2);
3645         else if(*p >= 32)
3646             addtobuf(buf, *p);
3647         else
3648             bprintf(buf, "\\x%02x", *p);
3649     }
3650     addtobuf(buf, 0);
3651     if(enc != NULL)
3652         free(enc);
3653     enc = NULL;
3654     if((wbuf = icmbstowcs((char *)buf, DCCHARSET)) != NULL)
3655     {
3656         enc = icwcstombs(wbuf, NULL);
3657         free(wbuf);
3658     }
3659     if(enc == NULL)
3660     {
3661         encsize = encdata = 0;
3662         for(p = buf; *p; p++) {
3663             if(*p < 128)
3664                 addtobuf(enc, *p);
3665             else
3666                 bprintf(buf, "\\x%x", *p);
3667         }
3668     }
3669     free(buf);
3670     return(enc);
3671 }
3672
3673 static void logunimpl(char *list, char *cmd, char *args)
3674 {
3675     FILE *log;
3676     
3677     if((log = fopen("/tmp/dc-unimpl", "a")) == NULL)
3678     {
3679         flog(LOG_WARNING, "could not open unimpl log: %s", strerror(errno));
3680         return;
3681     }
3682     fputs(list, log);
3683     fputc('\t', log);
3684     fputs(quotestr(cmd), log);
3685     if(args != NULL)
3686     {
3687         fputc('\t', log);
3688         fputs(quotestr(args), log);
3689     }
3690     fputc('\n', log);
3691     fclose(log);
3692 }
3693
3694 static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, struct socket *sk, void *data)
3695 {
3696     char *p;
3697     struct command *cmd;
3698     
3699     if((p = strchr(qcmd->string, ' ')) != NULL)
3700         *(p++) = 0;
3701     for(cmd = cmdlist; cmd->handler != NULL; cmd++)
3702     {
3703         if(!strcmp(cmd->name, qcmd->string))
3704             break;
3705     }
3706     if(cmd->handler != NULL)
3707     {
3708         cmd->handler(sk, data, qcmd->string, p);
3709     } else if(confgetint("dc", "logunimpl")) {
3710         if(cmdlist == hubcmds)
3711             logunimpl("hub", qcmd->string, p);
3712         else if(cmdlist == peercmds)
3713             logunimpl("peer", qcmd->string, p);
3714         else
3715             logunimpl("other?!", qcmd->string, p);
3716     }
3717 }
3718
3719 static int run(void)
3720 {
3721     struct fnetnode *fn, *nextfn;
3722     struct dchub *hub;
3723     struct dcpeer *peer, *nextpeer;
3724     struct qcommand *qcmd;
3725     int ret;
3726     
3727     ret = 0;
3728     for(fn = fnetnodes; fn != NULL; fn = nextfn)
3729     {
3730         nextfn = fn->next;
3731         if(fn->fnet != &dcnet)
3732             continue;
3733         if(fn->data == NULL)
3734             continue;
3735         hub = (struct dchub *)fn->data;
3736         if((qcmd = ulqcmd(&hub->queue)) != NULL)
3737         {
3738             if(*qcmd->string == '$')
3739             {
3740                 if((hub->sk != NULL) && (hub->sk->state == SOCK_EST))
3741                     dispatchcommand(qcmd, hubcmds, hub->sk, fn);
3742             } else if(*qcmd->string != 0) {
3743                 hubrecvchat(hub->sk, fn, NULL, qcmd->string);
3744             }
3745             freeqcmd(qcmd);
3746             ret = 1;
3747             break;
3748         }
3749     }
3750     for(peer = peers; peer != NULL; peer = nextpeer)
3751     {
3752         nextpeer = peer->next;
3753         if((qcmd = ulqcmd(&peer->queue)) != NULL)
3754         {
3755             if(peer->timeout != NULL)
3756                 canceltimer(peer->timeout);
3757             peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
3758             if(*qcmd->string == '$')
3759                 dispatchcommand(qcmd, peercmds, peer->sk, peer);
3760             freeqcmd(qcmd);
3761             ret = 1;
3762             break;
3763         }
3764     }
3765     return(ret);
3766 }
3767
3768 static void preinit(int hup)
3769 {
3770     if(hup)
3771         return;
3772     regfnet(&dcnet);
3773 }
3774
3775 static int updateudpport(struct configvar *var, void *uudata)
3776 {
3777     struct sockaddr_in addr;
3778     struct socket *newsock;
3779     
3780     memset(&addr, 0, sizeof(addr));
3781     addr.sin_family = AF_INET;
3782     addr.sin_port = htons(var->val.num);
3783     if((newsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3784     {
3785         flog(LOG_WARNING, "could not create new DC UDP socket, reverting to old: %s", strerror(errno));
3786         return(0);
3787     }
3788     newsock->readcb = udpread;
3789     if(udpsock != NULL)
3790         putsock(udpsock);
3791     udpsock = newsock;
3792     return(0);
3793 }
3794
3795 static int updatetcpport(struct configvar *var, void *uudata)
3796 {
3797     struct sockaddr_in addr;
3798     struct socket *newsock;
3799     
3800     memset(&addr, 0, sizeof(addr));
3801     addr.sin_family = AF_INET;
3802     addr.sin_port = htons(var->val.num);
3803     if((newsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3804         flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3805     if(tcpsock != NULL)
3806         putsock(tcpsock);
3807     tcpsock = newsock;
3808     return(0);
3809 }
3810
3811 static int init(int hup)
3812 {
3813     struct sockaddr_in addr;
3814     
3815     if(!hup)
3816     {
3817         GCBREG(sharechangecb, shareupdate, NULL);
3818         if(udpsock != NULL)
3819             putsock(udpsock);
3820         if(tcpsock != NULL)
3821             putsock(tcpsock);
3822         addr.sin_family = AF_INET;
3823         memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
3824         addr.sin_port = htons(confgetint("dc", "udpport"));
3825         if((udpsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3826         {
3827             flog(LOG_CRIT, "could not create DC UDP socket: %s", strerror(errno));
3828             return(1);
3829         }
3830         udpsock->readcb = udpread;
3831         addr.sin_port = htons(confgetint("dc", "tcpport"));
3832         if((tcpsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3833             flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3834         CBREG(confgetvar("dc", "udpport"), conf_update, updateudpport, NULL, NULL);
3835         CBREG(confgetvar("dc", "tcpport"), conf_update, updatetcpport, NULL, NULL);
3836         CBREG(confgetvar("net", "mode"), conf_update, updatetcpport, NULL, NULL);
3837     }
3838     return(0);
3839 }
3840
3841 static void terminate(void)
3842 {
3843     if(hmlistname != NULL)
3844     {
3845         unlink(hmlistname);
3846         free(hmlistname);
3847     }
3848     if(xmllistname != NULL)
3849     {
3850         unlink(xmllistname);
3851         free(xmllistname);
3852     }
3853     if(xmlbz2listname != NULL)
3854     {
3855         unlink(xmlbz2listname);
3856         free(xmlbz2listname);
3857     }
3858 }
3859
3860 static struct configvar myvars[] =
3861 {
3862     /** Specifies the share description reported to other DC users. */
3863     {CONF_VAR_STRING, "desc", {.str = L""}},
3864     /** Specifies the speed reported to other DC users. Normal values
3865      * are 28.8Kbps, 33.6Kbps, 56Kbps, Satellite, ISDN, DSL, Cable,
3866      * LAN(T1) or LAN(T3)*/
3867     {CONF_VAR_STRING, "speedstring", {.str = L"LAN(T1)"}},
3868     /** The e-mail address to report to other DC users. */
3869     {CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
3870     /** Specifies a specific UDP port to use for DC search results. If
3871      * left unspecified, a port is allocated dynamically. Useful for
3872      * NAT routers (see also the net.visibleipv4 address for those
3873      * cases). */
3874     {CONF_VAR_INT, "udpport", {.num = 0}},
3875     /** Specifies a specific TCP port to use for DC peer
3876      * connections. If left unspecified, a port is allocated
3877      * dynamically. Useful for NAT routers (see also the
3878      * net.visibleipv4 address for those cases). */
3879     {CONF_VAR_INT, "tcpport", {.num = 0}},
3880     /** If set to true, doldacond will do its best to emulate DC++
3881      * (currently v0.674). This should be left off if at all possible,
3882      * since turning it on will violate the rules of most hubs and
3883      * thus give hub owners an actual reason to kick you if it is
3884      * detected. It might be needed for some of the more bone-headed
3885      * hub owners, though. Note that DC++ emulation can also be turned
3886      * on or off for individual hubs, overriding this setting. */
3887     {CONF_VAR_BOOL, "dcppemu", {.num = 0}},
3888     /** Use for debugging. If set to true, doldacond will log all
3889      * unknown commands it receives, and their arguments, to
3890      * /tmp/dc-unimpl. */
3891     {CONF_VAR_BOOL, "logunimpl", {.num = 0}},
3892     /** If set to true, doldacond will hide its support for deflate
3893      * compression of transfers from other clients, so that they will
3894      * not request compressed uploads. Compressed transfers may
3895      * consume a non-trivial amount of CPU time on slower machines. */
3896     {CONF_VAR_BOOL, "hidedeflate", {.num = 0}},
3897     {CONF_VAR_END}
3898 };
3899
3900 static struct module me =
3901 {
3902     .conf =
3903     {
3904         .vars = myvars
3905     },
3906     .preinit = preinit,
3907     .init = init,
3908     .run = run,
3909     .terminate = terminate,
3910     .name = "dc"
3911 };
3912
3913 MODULE(me)