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