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