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