Return hashes in SRs.
[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, *buf;
1406     char *nick, *filename, *hubname;
1407     int size, slots;
1408     size_t buflen;
1409     struct srchres *sr;
1410     wchar_t *wnick, *wfile;
1411     
1412     hub = fn->data;
1413     nick = p = args;
1414     if((p2 = strchr(p, ' ')) == NULL)
1415         return;
1416     *p2 = 0;
1417     p = p2 + 1;
1418     filename = p;
1419     if((p2 = strchr(p, 5)) == NULL)
1420         return;
1421     *p2 = 0;
1422     p = p2 + 1;
1423     if((p2 = strchr(p, ' ')) == NULL)
1424         return;
1425     *p2 = 0;
1426     size = atoi(p);
1427     p = p2 + 1;
1428     if((p2 = strchr(p, '/')) == NULL)
1429         return;
1430     *p2 = 0;
1431     slots = atoi(p);
1432     p = p2 + 1;
1433     if((p2 = strchr(p, 5)) == NULL)
1434         return;
1435     p = p2 + 1;
1436     hubname = p;
1437     if((p2 = strstr(p, " (")) == NULL)
1438         return;
1439     *p2 = 0;
1440     if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
1441         return;
1442     if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
1443     {
1444         free(wnick);
1445         return;
1446     }
1447     sr = newsrchres(&dcnet, wfile, wnick);
1448     if(sr->peernick != NULL)
1449         free(sr->peernick);
1450     sr->peernick = swcsdup(wnick);
1451     sr->size = size;
1452     sr->slots = slots;
1453     free(wfile);
1454     free(wnick);
1455     if(!strncmp(hubname, "TTH:", 4))
1456     {
1457         if((buf = base32decode(hubname + 4, &buflen)) != NULL)
1458         {
1459             if(buflen == 24)
1460                 sr->hash = newhash(L"TTH", 24, buf);
1461             free(buf);
1462         }
1463     }
1464     getfnetnode(sr->fn = fn);
1465     submitsrchres(sr);
1466     freesrchres(sr);
1467     hubhandleaction(sk, fn, cmd, args);
1468 }
1469
1470 static void cmd_usercommand(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
1471 {
1472     /* Do nothing for now. */
1473 }
1474
1475 static void cmd_mynick(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1476 {
1477     if(peer->nativename != NULL)
1478         free(peer->nativename);
1479     peer->nativename = sstrdup(args);
1480     if(peer->wcsname != NULL)
1481         free(peer->wcsname);
1482     if((peer->wcsname = icmbstowcs(peer->nativename, DCCHARSET)) == NULL)
1483     {
1484         freedcpeer(peer);
1485         return;
1486     }
1487     peerhandleaction(sk, peer, cmd, args);
1488 }
1489
1490 static void cmd_direction(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1491 {
1492     char *p;
1493     
1494     if((p = strchr(args, ' ')) == NULL)
1495         return;
1496     *p = 0;
1497     if(!strcmp(args, "Upload"))
1498         peer->direction = TRNSD_DOWN;
1499     if(!strcmp(args, "Download"))
1500         peer->direction = TRNSD_UP;
1501     peerhandleaction(sk, peer, cmd, args);
1502 }
1503
1504 static void cmd_peerlock(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1505 {
1506     char *p;
1507     
1508     if((p = strchr(args, ' ')) == NULL)
1509         return;
1510     *p = 0;
1511     if(!strncmp(args, "EXTENDEDPROTOCOL", 16))
1512         peer->extended = 1;
1513     if(peer->key != NULL)
1514         free(peer->key);
1515     peer->key = dcmakekey(args);
1516     peerhandleaction(sk, peer, cmd, args);
1517 }
1518
1519 static void cmd_key(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1520 {
1521     peerhandleaction(sk, peer, cmd, args);
1522 }
1523
1524 static void cmd_filelength(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1525 {
1526     int size;
1527     
1528     if(peer->transfer == NULL)
1529     {
1530         freedcpeer(peer);
1531         return;
1532     }
1533     size = atoi(args);
1534     if(peer->transfer->size == size)
1535     {
1536         
1537     } else {
1538         /* Will have to restart, then... I really hate this, but what
1539          * am I to do then, considering the DC protocol requires the
1540          * resume offset before it sends the filesize? */
1541         transfersetsize(peer->transfer, size);
1542         freedcpeer(peer);
1543         return;
1544     }
1545     peerhandleaction(sk, peer, cmd, args);
1546 }
1547
1548 static void cmd_error(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1549 {
1550     if((peer->transfer != NULL) && (peer->transfer->dir == TRNSD_DOWN))
1551     {
1552         transferseterror(peer->transfer, TRNSE_NOTFOUND);
1553         resettransfer(peer->transfer);
1554         return;
1555     }
1556     freedcpeer(peer);
1557 }
1558
1559 static void cmd_maxedout(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1560 {
1561     if((peer->transfer != NULL) && (peer->transfer->dir == TRNSD_DOWN))
1562     {
1563         transferseterror(peer->transfer, TRNSE_NOSLOTS);
1564         resettransfer(peer->transfer);
1565         return;
1566     }
1567     freedcpeer(peer);
1568 }
1569
1570 static struct
1571 {
1572     char *name;
1573     char **file;
1574     void (*func)(void);
1575 } lists[] =
1576 {
1577     {"MyList.DcLst", &hmlistname, updatehmlist},
1578     {"files.xml", &xmllistname, updatexmllist},
1579     {"files.xml.bz2", &xmlbz2listname, updatexmlbz2list},
1580     {NULL, NULL}
1581 };
1582
1583 static int openfilelist(char *name)
1584 {
1585     int i, fd;
1586     int errnobak;
1587     
1588     for(i = 0; lists[i].name != NULL; i++)
1589     {
1590         if(!strcmp(name, lists[i].name))
1591             break;
1592     }
1593     errno = 0;
1594     if(lists[i].name == NULL)
1595         return(-1);
1596     fd = -1;
1597     if((*lists[i].file == NULL) || ((fd = open(*lists[i].file, O_RDONLY)) < 0))
1598         lists[i].func();
1599     if((fd < 0) && ((*lists[i].file == NULL) || ((fd = open(*lists[i].file, O_RDONLY)) < 0)))
1600     {
1601         errnobak = errno;
1602         flog(LOG_ERR, "could not open filelist tempfile: %s", strerror(errno));
1603         errno = errnobak;
1604         return(-1);
1605     }
1606     return(fd);
1607 }
1608
1609 static struct sharecache *findbytth(char *tth32)
1610 {
1611     char *buf;
1612     size_t buflen;
1613     struct sharecache *node;
1614     
1615     if((buf = base32decode(tth32, &buflen)) == NULL)
1616         return(NULL);
1617     if(buflen != 24)
1618     {
1619         free(buf);
1620         return(NULL);
1621     }
1622     for(node = shareroot->child; node != NULL; node = nextscnode(node))
1623     {
1624         if(node->f.b.hastth && !memcmp(node->hashtth, buf, 24))
1625             break;
1626     }
1627     free(buf);
1628     return(node);
1629 }
1630
1631 static struct sharecache *resdcpath(char *path, char *charset, char sep)
1632 {
1633     struct sharecache *node;
1634     char *p, *p2;
1635
1636     node = shareroot;
1637     p = path;
1638     while(p != NULL)
1639     {
1640         if((p2 = strchr(p, sep)) != NULL)
1641             *(p2++) = 0;
1642         if((node = findcache(node, icsmbstowcs(p, charset, L""))) == NULL)
1643             return(NULL);
1644         p = p2;
1645     }
1646     return(node);
1647 }
1648
1649 static void cmd_get(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1650 {
1651     int offset;
1652     char *p, *buf;
1653     wchar_t *buf2;
1654     struct sharecache *node;
1655     struct socket *lesk;
1656     int fd;
1657     struct stat sb;
1658     
1659     if(peer->transfer == NULL)
1660     {
1661         freedcpeer(peer);
1662         return;
1663     }
1664     if((p = strchr(args, '$')) == NULL)
1665     {
1666         freedcpeer(peer);
1667         return;
1668     }
1669     *(p++) = 0;
1670     if((offset = (atoi(p) - 1)) < 0)
1671     {
1672         freedcpeer(peer);
1673         return;
1674     }
1675     if(((fd = openfilelist(args)) < 0) && (errno != 0))
1676     {
1677         qstr(sk, "$Error Could not send file list|");
1678         freedcpeer(peer);
1679         return;
1680     } else if(fd >= 0) {
1681         if((buf2 = icsmbstowcs(args, DCCHARSET, NULL)) != NULL)
1682             transfersetpath(peer->transfer, buf2);
1683     }
1684     if(fd < 0)
1685     {
1686         if(slotsleft() < 1)
1687         {
1688             qstr(sk, "$MaxedOut|");
1689             freedcpeer(peer);
1690             return;
1691         }
1692         if((node = resdcpath(args, DCCHARSET, '\\')) == NULL)
1693         {
1694             qstrf(sk, "$Error File not in share|");
1695             freedcpeer(peer);
1696             return;
1697         }
1698         if((fd = opensharecache(node)) < 0)
1699         {
1700             qstrf(sk, "$Error %s|", strerror(errno));
1701             freedcpeer(peer);
1702             return;
1703         }
1704         buf = getfspath(node);
1705         if((buf2 = icsmbstowcs(buf, NULL, NULL)) == NULL)
1706             flog(LOG_WARNING, "could not convert native path into a wcs (%s): %s", buf, strerror(errno));
1707         else
1708             transfersetpath(peer->transfer, buf2);
1709         free(buf);
1710     }
1711     if(fstat(fd, &sb) < 0)
1712     {
1713         close(fd);
1714         flog(LOG_WARNING, "could not stat file %ls: %s", node->name, strerror(errno));
1715         qstrf(sk, "$Error|");
1716         freedcpeer(peer);
1717         return;
1718     }
1719     if((offset != 0) && (lseek(fd, offset, SEEK_SET) < 0))
1720     {
1721         close(fd);
1722         qstrf(sk, "$Error Offset out of range|");
1723         freedcpeer(peer);
1724         return;
1725     }
1726     lesk = wrapsock(fd);
1727     transferprepul(peer->transfer, sb.st_size, offset, -1, lesk);
1728     putsock(lesk);
1729     qstrf(sk, "$FileLength %i|", peer->transfer->size);
1730     peerhandleaction(sk, peer, cmd, args);
1731 }
1732
1733 static void starttrans(struct dcpeer *peer)
1734 {
1735     peer->state = PEER_TRNS;
1736     transferstartul(peer->transfer, peer->sk);
1737     peer->sk->writecb = (void (*)(struct socket *, void *))transwrite;
1738 }
1739
1740 static void cmd_send(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1741 {
1742     if(peer->transfer == NULL)
1743     {
1744         freedcpeer(peer);
1745         return;
1746     }
1747     if(peer->transfer->localend == NULL)
1748     {
1749         freedcpeer(peer);
1750         return;
1751     }
1752     peer->ptclose = 1;
1753     starttrans(peer);
1754     peerhandleaction(sk, peer, cmd, args);
1755 }
1756
1757 static void cmd_supports(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1758 {
1759     int i;
1760     char *p, *p2;
1761     char **arr;
1762     size_t arrsize, arrdata;
1763     
1764     if(peer->supports != NULL)
1765     {
1766         for(i = 0; peer->supports[i] != NULL; i++)
1767             free(peer->supports[i]);
1768         free(peer->supports);
1769     }
1770     arr = NULL;
1771     arrsize = arrdata = 0;
1772     p = args;
1773     do
1774     {
1775         if((p2 = strchr(p, ' ')) != NULL)
1776             *(p2++) = 0;
1777         if(*p == 0)
1778             continue;
1779         addtobuf(arr, sstrdup(p));
1780     } while((p = p2) != NULL);
1781     addtobuf(arr, NULL);
1782     peer->supports = arr;
1783     peerhandleaction(sk, peer, cmd, args);
1784 }
1785
1786 static void cmd_getblock(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1787 {
1788     int fd;
1789     char *p, *p2;
1790     int start, numbytes;
1791     char *charset, *buf;
1792     wchar_t *buf2;
1793     struct sharecache *node;
1794     struct stat sb;
1795     struct socket *lesk;
1796     
1797     if(peer->transfer == NULL)
1798     {
1799         freedcpeer(peer);
1800         return;
1801     }
1802     p = args;
1803     if((p2 = strchr(p, ' ')) == NULL)
1804     {
1805         freedcpeer(peer);
1806         return;
1807     }
1808     *(p2++) = 0;
1809     start = atoi(p);
1810     p = p2;
1811     if((p2 = strchr(p, ' ')) == NULL)
1812     {
1813         freedcpeer(peer);
1814         return;
1815     }
1816     *(p2++) = 0;
1817     numbytes = atoi(p);
1818     p = p2;
1819     if(!strcmp(cmd, "$UGetBlock") || !strcmp(cmd, "$UGetZBlock"))
1820         charset = "UTF-8";
1821     else
1822         charset = DCCHARSET;
1823     if(!strcmp(cmd, "$GetZBlock") || !strcmp(cmd, "$UGetZBlock"))
1824         initcompress(peer, CPRS_ZLIB);
1825     if(((fd = openfilelist(p)) < 0) && (errno != 0))
1826     {
1827         qstr(sk, "$Error Could not send file list|");
1828         return;
1829     } else if(fd >= 0) {
1830         if((buf2 = icsmbstowcs(args, charset, NULL)) != NULL)
1831             transfersetpath(peer->transfer, buf2);
1832     }
1833     if(fd < 0)
1834     {
1835         if(slotsleft() < 1)
1836         {
1837             qstr(sk, "$MaxedOut|");
1838             return;
1839         }
1840         if((node = resdcpath(p, charset, '\\')) == NULL)
1841         {
1842             qstr(sk, "$Error File not in cache|");
1843             return;
1844         }
1845         if((fd = opensharecache(node)) < 0)
1846         {
1847             qstrf(sk, "$Error %s|", strerror(errno));
1848             return;
1849         }
1850         buf = getfspath(node);
1851         if((buf2 = icsmbstowcs(buf, NULL, NULL)) == NULL)
1852             flog(LOG_WARNING, "could not convert native path into a wcs (%s): %s", buf, strerror(errno));
1853         else
1854             transfersetpath(peer->transfer, buf2);
1855         free(buf);
1856     }
1857     if(fstat(fd, &sb) < 0)
1858     {
1859         close(fd);
1860         flog(LOG_WARNING, "could not stat file %ls: %s", node->name, strerror(errno));
1861         qstr(sk, "$Error|");
1862         return;
1863     }
1864     if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
1865     {
1866         close(fd);
1867         qstr(sk, "$Error Offset out of range|");
1868         return;
1869     }
1870     if((numbytes < 0) || (start + numbytes > sb.st_size))
1871         numbytes = sb.st_size - start;
1872     lesk = wrapsock(fd);
1873     transferprepul(peer->transfer, sb.st_size, start, start + numbytes, lesk);
1874     putsock(lesk);
1875     qstrf(sk, "$Sending %i|", numbytes);
1876     starttrans(peer);
1877     peerhandleaction(sk, peer, cmd, args);
1878 }
1879
1880 static void cmd_adcget(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
1881 {
1882     int i;
1883     char **argv, *buf;
1884     int start, numbytes;
1885     struct sharecache *node;
1886     struct stat sb;
1887     struct socket *lesk;
1888     wchar_t *wbuf;
1889     int fd;
1890     
1891     if(peer->transfer == NULL)
1892     {
1893         freedcpeer(peer);
1894         return;
1895     }
1896     if((argv = parseadc(args)) == NULL)
1897     {
1898         freedcpeer(peer);
1899         return;
1900     }
1901     if(parrlen(argv) < 4)
1902     {
1903         freedcpeer(peer);
1904         goto out;
1905     }
1906     start = atoi(argv[2]);
1907     numbytes = atoi(argv[3]);
1908     node = NULL;
1909     fd = -1;
1910     if(((fd = openfilelist(argv[1])) < 0) && (errno != 0))
1911     {
1912         qstr(sk, "$Error Could not send file list|");
1913         goto out;
1914     } else if(fd >= 0) {
1915         if((wbuf = icsmbstowcs(argv[1], "UTF-8", NULL)) != NULL)
1916             transfersetpath(peer->transfer, wbuf);
1917     }
1918     if(fd < 0)
1919     {
1920         if(slotsleft() < 1)
1921         {
1922             qstr(sk, "$MaxedOut|");
1923             goto out;
1924         }
1925         if(!strncmp(argv[1], "TTH/", 4))
1926         {
1927             if((node = findbytth(argv[1] + 4)) == NULL)
1928             {
1929                 qstr(sk, "$Error File not in cache|");
1930                 goto out;
1931             }
1932         } else {
1933             if((node = resdcpath(argv[1], "UTF-8", '/')) == NULL)
1934             {
1935                 qstr(sk, "$Error File not in cache|");
1936                 goto out;
1937             }
1938         }
1939         if((fd = opensharecache(node)) < 0)
1940         {
1941             qstrf(sk, "$Error %s|", strerror(errno));
1942             goto out;
1943         }
1944         buf = getfspath(node);
1945         if((wbuf = icsmbstowcs(buf, NULL, NULL)) == NULL)
1946             flog(LOG_WARNING, "could not convert native path into a wcs (%s): %s", buf, strerror(errno));
1947         else
1948             transfersetpath(peer->transfer, wbuf);
1949         free(buf);
1950     }
1951     if(!strcmp(argv[0], "file"))
1952     {
1953         for(i = 4; argv[i] != NULL; i++)
1954         {
1955             if(!strcmp(argv[i], "ZL1"))
1956                 initcompress(peer, CPRS_ZLIB);
1957         }
1958         if(fstat(fd, &sb) < 0)
1959         {
1960             flog(LOG_WARNING, "could not stat file %ls: %s", node->name, strerror(errno));
1961             qstr(sk, "$Error|");
1962             goto out;
1963         }
1964         if((start != 0) && ((start >= sb.st_size) || (lseek(fd, start, SEEK_SET) < 0)))
1965         {
1966             qstr(sk, "$Error Offset out of range|");
1967             goto out;
1968         }
1969         if((numbytes < 0) || (start + numbytes > sb.st_size))
1970             numbytes = sb.st_size - start;
1971         lesk = wrapsock(fd);
1972         transferprepul(peer->transfer, sb.st_size, start, start + numbytes, lesk);
1973         putsock(lesk);
1974         fd = -1;
1975         qstr(sk, "$ADCSND");
1976         sendadc(sk, "file");
1977         sendadc(sk, argv[1]);
1978         sendadcf(sk, "%i", start);
1979         sendadcf(sk, "%i", numbytes);
1980         if(peer->compress == CPRS_ZLIB)
1981             sendadc(sk, "ZL1");
1982         qstr(sk, "|");
1983         starttrans(peer);
1984     } else if(!strcmp(argv[0], "tthl")) {
1985         /*
1986          * XXX: Implement full TTHL support.
1987          *
1988          * In the meantime, this is better than nothing, since it at
1989          * least allows fetching of the TTH root if it isn't already
1990          * known.
1991          */
1992         if(node == NULL)
1993         {
1994             qstr(sk, "$Error no TTHL data for virtual files|");
1995             goto out;
1996         }
1997         qstr(sk, "$ADCSND");
1998         sendadc(sk, "tthl");
1999         sendadc(sk, argv[1]);
2000         sendadc(sk, "0");
2001         sendadc(sk, "24");
2002         qstr(sk, "|");
2003         sockqueue(sk, node->hashtth, 24);
2004     } else {
2005         qstr(sk, "$Error Namespace not implemented|");
2006         goto out;
2007     }
2008     peerhandleaction(sk, peer, cmd, args);
2009     
2010  out:
2011     if(fd >= 0)
2012         close(fd);
2013     freeparr(argv);
2014 }
2015
2016 /*
2017 Hub skeleton:
2018 static void cmd_(struct socket *sk, struct fnetnode *fn, char *cmd, char *args)
2019 {
2020     struct dchub *hub;
2021     
2022     hub = fn->data;
2023     hubhandleaction(sk, fn, cmd, args);
2024 }
2025
2026 Peer skeleton:
2027 static void cmd_(struct socket *sk, struct dcpeer *peer, char *cmd, char *args)
2028 {
2029     peerhandleaction(sk, peer, cmd, args);
2030 }
2031
2032 */
2033
2034 static int hubreqconn(struct fnetpeer *peer)
2035 {
2036     struct dchub *hub;
2037     char *mbsnick;
2038     
2039     if((peer->fn->state != FNN_EST) || (peer->fn->fnet != &dcnet))
2040     {
2041         errno = EINVAL;
2042         return(1);
2043     }
2044     if((hub = peer->fn->data) == NULL)
2045     {
2046         errno = EFAULT;
2047         return(1);
2048     }
2049     if((mbsnick = icwcstombs(peer->id, DCCHARSET)) == NULL)
2050         return(1); /* Shouldn't happen, of course, but who knows... */
2051     if(tcpsock != NULL)
2052     {
2053         sendctm(peer->fn->sk, mbsnick);
2054         expectpeer(mbsnick, peer->fn);
2055     } else {
2056         qstrf(peer->fn->sk, "$RevConnectToMe %s %s|", hub->nativenick, mbsnick);
2057     }
2058     free(mbsnick);
2059     return(0);
2060 }
2061
2062 static int hubsendchat(struct fnetnode *fn, int public, wchar_t *to, wchar_t *string)
2063 {
2064     struct dchub *hub;
2065     char *mbsstring, *mbsto;
2066     
2067     hub = fn->data;
2068     if((mbsto = icwcstombs(to, DCCHARSET)) == NULL)
2069     {
2070         errno = EILSEQ;
2071         return(1);
2072     }
2073     if((mbsstring = icwcstombs(string, DCCHARSET)) == NULL)
2074     {
2075         errno = EILSEQ;
2076         return(1);
2077     }
2078     if((strchr(mbsto, '|') != NULL) || (strchr(mbsto, ' ') != NULL))
2079     {
2080         free(mbsto);
2081         free(mbsstring);
2082         errno = ESRCH;
2083         return(1);
2084     }
2085     if(strchr(mbsstring, '|') != NULL)
2086     {
2087         free(mbsto);
2088         free(mbsstring);
2089         errno = EILSEQ;
2090         return(1);
2091     }
2092     if(public)
2093     {
2094         if(*to == L'\0')
2095         {
2096             qstrf(fn->sk, "<%s> %s|", hub->nativenick, mbsstring);
2097         } else {
2098             qstrf(fn->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
2099         }
2100     } else {
2101         qstrf(fn->sk, "$To: %s From: %s $<%s> %s|", mbsto, hub->nativenick, hub->nativenick, mbsstring);
2102     }
2103     free(mbsto);
2104     free(mbsstring);
2105     return(0);
2106 }
2107
2108 static void findsizelimit(struct sexpr *sexpr, int *min, int *max)
2109 {
2110     int minl, maxl, minr, maxr, retmin, retmax;
2111     
2112     switch(sexpr->op)
2113     {
2114     case SOP_AND:
2115         findsizelimit(sexpr->l, &minl, &maxl);
2116         findsizelimit(sexpr->r, &minr, &maxr);
2117         retmin = (minl > minr)?minl:minr;
2118         if((maxl != -1) && (maxr != -1))
2119             retmax = (maxl < maxr)?maxl:maxr;
2120         else if(maxl != -1)
2121             retmax = maxl;
2122         else if(maxr != -1)
2123             retmax = maxr;
2124         else
2125             retmax = -1;
2126         break;
2127     case SOP_OR:
2128         findsizelimit(sexpr->l, &minl, &maxl);
2129         findsizelimit(sexpr->r, &minr, &maxr);
2130         retmin = (minl < minr)?minl:minr;
2131         if((maxl == -1) || (maxr == -1))
2132             retmax = -1;
2133         else
2134             retmax = (maxl > maxr)?maxl:maxr;
2135         break;
2136     case SOP_NOT:
2137         findsizelimit(sexpr->l, &minl, &maxl);
2138         if((minl == 0) && (maxl == -1)) /* Interval is unspecified */
2139         {
2140             retmin = 0;
2141             retmax = -1;
2142         } else if((minl == 0) && (maxl != -1)) {
2143             retmin = maxl + 1;
2144             retmax = -1;
2145         } else if((minl != 0) && (maxl == -1)) {
2146             retmin = 0;
2147             retmax = minl - 1;
2148         } else { /* This would yield two seperate intervals, which DC cannot handle */
2149             retmin = 0;
2150             retmax = -1;
2151         }
2152     case SOP_SIZELT:
2153         retmin = 0;
2154         retmax = sexpr->d.n - 1;
2155         break;
2156     case SOP_SIZEEQ:
2157         retmin = sexpr->d.n;
2158         retmax = sexpr->d.n;
2159         break;
2160     case SOP_SIZEGT:
2161         retmin = sexpr->d.n + 1;
2162         retmax = -1;
2163         break;
2164     default:
2165         retmin = 0;
2166         retmax = -1;
2167         break;
2168     }
2169     if(min != NULL)
2170         *min = retmin;
2171     if(max != NULL)
2172         *max = retmax;
2173 }
2174
2175 static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
2176 {
2177     struct dchub *hub;
2178     struct wcslist *list, *cur;
2179     char *sstr, *buf, *p;
2180     size_t sstrsize, sstrdata;
2181     struct sockaddr *name;
2182     socklen_t namelen;
2183     int minsize, maxsize;
2184     char sizedesc[32];
2185     
2186     hub = fn->data;
2187     if((fn->state != FNN_EST) || (fn->sk == NULL) || (fn->sk->state != SOCK_EST))
2188         return(1);
2189     list = findsexprstrs(srch->sexpr);
2190     findsizelimit(srch->sexpr, &minsize, &maxsize);
2191     if((minsize != 0) && (maxsize != -1))
2192     {
2193         /* Choose either minsize or maxsize by trying to determine
2194          * which choice will be most restrictive. The result can be
2195          * approximative at best anyway, so don't try too hard... */
2196         if((50000000 - maxsize) > (minsize - 50000000))
2197             minsize = 0;
2198         else
2199             maxsize = -1;
2200     }
2201     if(minsize != 0)
2202     {
2203         snprintf(sizedesc, sizeof(sizedesc), "T?F?%i", minsize);
2204     } else if(maxsize != -1) {
2205         snprintf(sizedesc, sizeof(sizedesc), "T?T?%i", maxsize);
2206     } else {
2207         strcpy(sizedesc, "F?F?0");
2208     }
2209     sstr = NULL;
2210     sstrsize = sstrdata = 0;
2211     if(list != NULL)
2212     {
2213         for(cur = list; cur != NULL; cur = cur->next)
2214         {
2215             if((buf = icwcstombs(cur->str, DCCHARSET)) == NULL)
2216             {
2217                 /* Can't find anything anyway if the search expression
2218                  * requires characters outside DC's charset. There's
2219                  * nothing technically wrong with the search itself,
2220                  * however, so return success. This should be
2221                  * considered as an optimization. */
2222                 freesl(&list);
2223                 if(sstr != NULL)
2224                     free(sstr);
2225                 return(0);
2226             }
2227             if(cur != list)
2228                 addtobuf(sstr, '$');
2229             /*
2230              * It probably doesn't hurt if buf contains any extra
2231              * dollar signs - it will just result in extra search
2232              * terms, and the extraneous results will be filtered by
2233              * the search layer anyway. It hurts if it contains any
2234              * pipes, though, so let's sell them for money.
2235              */
2236             for(p = buf; *p; p++)
2237             {
2238                 if(*p == '|')
2239                     *p = '$';
2240             }
2241             bufcat(sstr, buf, strlen(buf));
2242             free(buf);
2243         }
2244     } else {
2245         /* Will match all files... :-/ */
2246         addtobuf(sstr, '.');
2247     }
2248     addtobuf(sstr, 0);
2249     if(tcpsock != NULL)
2250     {
2251         if(sockgetremotename(udpsock, &name, &namelen) < 0)
2252         {
2253             flog(LOG_WARNING, "cannot get address of UDP socket");
2254         } else {
2255             qstrf(fn->sk, "$Search %s %s?1?%s|", formataddress(name, namelen), sizedesc, sstr);
2256             free(name);
2257         }
2258     } else {
2259         qstrf(fn->sk, "$Search Hub:%s %s?1?%s|", hub->nativenick, sizedesc, sstr);
2260     }
2261     free(sstr);
2262     freesl(&list);
2263     fn->lastsrch = time(NULL);
2264     return(0);
2265 }
2266
2267 #undef skipcmd
2268 #undef qstr
2269 #undef qstrf
2270
2271 #define cc(c) ((void (*)(struct socket *, void *, char *, char *))(c))
2272 struct command hubcmds[] =
2273 {
2274     {"$Lock", cc(cmd_lock)},
2275     {"$HubName", cc(cmd_hubname)},
2276     {"$Hello", cc(cmd_hello)},
2277     {"$Quit", cc(cmd_quit)},
2278     {"$NickList", cc(cmd_nicklist)},
2279     {"$OpList", cc(cmd_oplist)},
2280     {"$MyINFO", cc(cmd_myinfo)},
2281     {"$ForceMove", cc(cmd_forcemove)},
2282     {"$Search", cc(cmd_search)},
2283     {"$MultiSearch", cc(cmd_search)},
2284     {"$ConnectToMe", cc(cmd_connecttome)},
2285     {"$RevConnectToMe", cc(cmd_revconnecttome)},
2286     {"$GetNetInfo", cc(cmd_getnetinfo)},
2287     {"$To:", cc(cmd_to)},
2288     {"$SR", cc(cmd_sr)},
2289     {"$UserCommand", cc(cmd_usercommand)},
2290     {NULL, NULL}
2291 };
2292
2293 struct command peercmds[] =
2294 {
2295     {"$MyNick", cc(cmd_mynick)},
2296     {"$Lock", cc(cmd_peerlock)},
2297     {"$Direction", cc(cmd_direction)},
2298     {"$Key", cc(cmd_key)},
2299     {"$FileLength", cc(cmd_filelength)},
2300     {"$Error", cc(cmd_error)},
2301     {"$MaxedOut", cc(cmd_maxedout)},
2302     {"$Get", cc(cmd_get)},
2303     {"$Send", cc(cmd_send)},
2304     {"$Supports", cc(cmd_supports)},
2305     {"$GetBlock", cc(cmd_getblock)},
2306     {"$UGetBlock", cc(cmd_getblock)},
2307     {"$GetZBlock", cc(cmd_getblock)},
2308     {"$UGetZBlock", cc(cmd_getblock)},
2309     {"$ADCGET", cc(cmd_adcget)},
2310     {NULL, NULL}
2311 };
2312 #undef cc
2313
2314 static void dctransdetach(struct transfer *transfer, struct dcpeer *peer)
2315 {
2316     CBUNREG(transfer, trans_filterout, peer);
2317     if(peer->freeing)
2318         return;
2319     peer->transfer = NULL;
2320     freedcpeer(peer);
2321 }
2322
2323 static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
2324 {
2325     int ret;
2326     void *buf;
2327     char outbuf[1024];
2328     z_stream *cstr;
2329     size_t bufsize;
2330     
2331     if((peer->state == PEER_TRNS) || (peer->state == PEER_SYNC))
2332     {
2333         if(sockqueuesize(peer->sk) < 65536)
2334         {
2335             if((buf = transfergetdata(transfer, &bufsize)) != NULL)
2336             {
2337                 if(peer->compress == CPRS_NONE)
2338                 {
2339                     sockqueue(peer->sk, buf, bufsize);
2340                 } else if(peer->compress == CPRS_ZLIB) {
2341                     cstr = peer->cprsdata;
2342                     cstr->next_in = buf;
2343                     cstr->avail_in = bufsize;
2344                     while(cstr->avail_in > 0)
2345                     {
2346                         cstr->next_out = outbuf;
2347                         cstr->avail_out = sizeof(outbuf);
2348                         if((ret = deflate(cstr, 0)) != Z_OK)
2349                         {
2350                             flog(LOG_WARNING, "bug? deflate() did not return Z_OK (but rather %i)", ret);
2351                             freedcpeer(peer);
2352                             return;
2353                         }
2354                         sockqueue(peer->sk, outbuf, sizeof(outbuf) - cstr->avail_out);
2355                     }
2356                 }
2357                 free(buf);
2358             }
2359             if(peer->state == PEER_SYNC)
2360             {
2361                 if(peer->compress == CPRS_ZLIB)
2362                 {
2363                     cstr = peer->cprsdata;
2364                     cstr->next_in = NULL;
2365                     cstr->avail_in = 0;
2366                     do
2367                     {
2368                         cstr->next_out = outbuf;
2369                         cstr->avail_out = sizeof(outbuf);
2370                         ret = deflate(cstr, Z_FINISH);
2371                         if((ret != Z_OK) && (ret != Z_STREAM_END))
2372                         {
2373                             flog(LOG_WARNING, "bug? deflate(Z_FINISH) did not return Z_OK (but rather %i)", ret);
2374                             freedcpeer(peer);
2375                             return;
2376                         }
2377                         sockqueue(peer->sk, outbuf, sizeof(outbuf) - cstr->avail_out);
2378                     } while(ret != Z_STREAM_END);
2379                 }
2380                 if(peer->ptclose)
2381                 {
2382                     freedcpeer(peer);
2383                 } else {
2384                     peer->state = PEER_CMD;
2385                     endcompress(peer);
2386                     transfersetstate(transfer, TRNS_HS);
2387                     socksettos(peer->sk, confgetint("fnet", "fnptos"));
2388                     peer->sk->writecb = NULL;
2389                 }
2390             }
2391         }
2392     }
2393 }
2394
2395 static void dctransendofdata(struct transfer *transfer, struct dcpeer *peer)
2396 {
2397     peer->state = PEER_SYNC;
2398     dctransgotdata(transfer, peer);
2399 }
2400
2401 static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
2402 {
2403     if(transferdatasize(transfer) < 65536)
2404         peer->sk->ignread = 0;
2405 }
2406
2407 static void transread(struct socket *sk, struct dcpeer *peer)
2408 {
2409     void *buf;
2410     size_t bufsize;
2411     struct transfer *transfer;
2412     
2413     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
2414         return;
2415     if(peer->transfer == NULL)
2416     {
2417         free(buf);
2418         freedcpeer(peer);
2419         return;
2420     }
2421     transferputdata(peer->transfer, buf, bufsize);
2422     free(buf);
2423     if(peer->transfer->curpos >= peer->transfer->size)
2424     {
2425         transfer = peer->transfer;
2426         transferdetach(transfer);
2427         transferendofdata(transfer);
2428         return;
2429     }
2430     if(transferdatasize(peer->transfer) > 65535)
2431         sk->ignread = 1;
2432 }
2433
2434 static void transerr(struct socket *sk, int err, struct dcpeer *peer)
2435 {
2436     struct transfer *transfer;
2437
2438     if((transfer = peer->transfer) == NULL)
2439     {
2440         freedcpeer(peer);
2441         return;
2442     }
2443     transferdetach(transfer);
2444     transferendofdata(transfer);
2445 }
2446
2447 static void transwrite(struct socket *sk, struct dcpeer *peer)
2448 {
2449     if((peer->state != PEER_TRNS) && (peer->state != PEER_SYNC))
2450         return;
2451     if(peer->transfer == NULL)
2452     {
2453         freedcpeer(peer);
2454         return;
2455     }
2456     dctransgotdata(peer->transfer, peer);
2457 }
2458
2459 static void udpread(struct socket *sk, void *data)
2460 {
2461     char *buf, *p, *p2, *hashbuf;
2462     size_t buflen, hashlen;
2463     char *nick, *filename, *hubname;
2464     int size, slots;
2465     struct fnetnode *fn, *myfn;
2466     struct dchub *hub;
2467     struct srchres *sr;
2468     wchar_t *wnick, *wfile;
2469     struct hash *hash;
2470     
2471     if((buf = sockgetinbuf(sk, &buflen)) == NULL)
2472         return;
2473     buf = srealloc(buf, buflen + 1);
2474     buf[buflen] = 0;
2475     if(!strncmp(buf, "$SR ", 4))
2476     {
2477         p = buf + 4;
2478         nick = p;
2479         if((p2 = strchr(p, ' ')) == NULL)
2480         {
2481             free(buf);
2482             return;
2483         }
2484         *p2 = 0;
2485         p = p2 + 1;
2486         filename = p;
2487         if((p2 = strchr(p, 5)) == NULL)
2488         {
2489             free(buf);
2490             return;
2491         }
2492         *p2 = 0;
2493         p = p2 + 1;
2494         if((p2 = strchr(p, ' ')) == NULL)
2495         {
2496             free(buf);
2497             return;
2498         }
2499         *p2 = 0;
2500         size = atoi(p);
2501         p = p2 + 1;
2502         if((p2 = strchr(p, '/')) == NULL)
2503         {
2504             free(buf);
2505             return;
2506         }
2507         *p2 = 0;
2508         slots = atoi(p);
2509         p = p2 + 1;
2510         if((p2 = strchr(p, 5)) == NULL)
2511         {
2512             free(buf);
2513             return;
2514         }
2515         p = p2 + 1;
2516         hubname = p;
2517         if((p2 = strstr(p, " (")) == NULL)
2518         {
2519             free(buf);
2520             return;
2521         }
2522         *p2 = 0;
2523         if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
2524         {
2525             free(buf);
2526             return;
2527         }
2528         if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
2529         {
2530             free(wnick);
2531             free(buf);
2532             return;
2533         }
2534         myfn = NULL;
2535         hash = NULL;
2536         if(!strncmp(hubname, "TTH:", 4))
2537         {
2538             if((hashbuf = base32decode(hubname + 4, &hashlen)) != NULL)
2539             {
2540                 if(hashlen == 24)
2541                     hash = newhash(L"TTH", 24, hashbuf);
2542                 free(hashbuf);
2543             }
2544         } else {
2545             for(fn = fnetnodes; fn != NULL; fn = fn->next)
2546             {
2547                 if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
2548                 {
2549                     if((hub->nativename != NULL) && !strcmp(hub->nativename, hubname))
2550                     {
2551                         if(myfn == NULL)
2552                         {
2553                             myfn = fn;
2554                         } else {
2555                             myfn = NULL;
2556                             break;
2557                         }
2558                     }
2559                 }
2560             }
2561         }
2562         sr = newsrchres(&dcnet, wfile, wnick);
2563         if(sr->peernick != NULL)
2564             free(sr->peernick);
2565         sr->peernick = swcsdup(wnick);
2566         sr->size = size;
2567         sr->slots = slots;
2568         free(wfile);
2569         free(wnick);
2570         if(myfn != NULL)
2571             getfnetnode(sr->fn = myfn);
2572         if(hash != NULL)
2573             sr->hash = hash;
2574         submitsrchres(sr);
2575         freesrchres(sr);
2576     }
2577     free(buf);
2578 }
2579
2580 static void hubread(struct socket *sk, struct fnetnode *fn)
2581 {
2582     struct dchub *hub;
2583     char *newbuf;
2584     size_t datalen;
2585     char *p;
2586     
2587     hub = (struct dchub *)fn->data;
2588     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
2589         return;
2590     if(hub->inbufdata > 500000) /* Discard possible malicious data */
2591         hub->inbufdata = 0;
2592     sizebuf2(hub->inbuf, hub->inbufdata + datalen, 1);
2593     memcpy(hub->inbuf + hub->inbufdata, newbuf, datalen);
2594     free(newbuf);
2595     p = hub->inbuf + hub->inbufdata;
2596     hub->inbufdata += datalen;
2597     while((datalen > 0) && ((p = memchr(p, '|', datalen)) != NULL))
2598     {
2599         *(p++) = 0;
2600         newqcmd(&hub->queue, hub->inbuf);
2601         memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
2602         datalen = hub->inbufdata;
2603         p = hub->inbuf;
2604     }
2605 }
2606
2607 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
2608 {
2609     killfnetnode(fn);
2610 }
2611
2612 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
2613 {
2614     struct dchub *hub;
2615     char *buf;
2616     
2617     hub = fn->data;
2618     if((buf = icwcstombs(newnick, DCCHARSET)) == NULL)
2619         return(1);
2620     if((strchr(buf, ' ') != NULL) || (strchr(buf, '|') != NULL) || (strchr(buf, '$') != NULL))
2621     {
2622         free(buf);
2623         return(1);
2624     }
2625     if(hub == NULL) /* Not yet connected */
2626     {
2627         free(buf);
2628         return(0);
2629     }
2630     if(hub->nativenick != NULL)
2631         free(hub->nativenick);
2632     hub->nativenick = buf;
2633     return(0);
2634 }
2635
2636 static struct dchub *newdchub(struct fnetnode *fn)
2637 {
2638     struct dchub *new;
2639     
2640     new = smalloc(sizeof(*new));
2641     memset(new, 0, sizeof(*new));
2642     fn->data = new;
2643     if(hubsetnick(fn, fn->mynick))
2644         fnetsetnick(fn, L"DoldaConnectUser-IN");
2645     /* IN as in Invalid Nick */
2646     return(new);
2647 }
2648
2649 static struct dcpeer *newdcpeer(struct socket *sk)
2650 {
2651     struct dcpeer *new;
2652     
2653     new = smalloc(sizeof(*new));
2654     memset(new, 0, sizeof(*new));
2655     new->transfer = NULL;
2656     getsock(sk);
2657     new->sk = sk;
2658     new->next = peers;
2659     new->prev = NULL;
2660     if(peers != NULL)
2661         peers->prev = new;
2662     peers = new;
2663     numdcpeers++;
2664     return(new);
2665 }
2666
2667 static void freedcpeer(struct dcpeer *peer)
2668 {
2669     int i;
2670     struct qcommand *qcmd;
2671     
2672     peer->freeing = 1;
2673     if(peers == peer)
2674         peers = peer->next;
2675     if(peer->next != NULL)
2676         peer->next->prev = peer->prev;
2677     if(peer->prev != NULL)
2678         peer->prev->next = peer->next;
2679     if(peer->transfer != NULL)
2680     {
2681         if(peer->transfer->dir == TRNSD_UP)
2682             peer->transfer->close = 1;
2683         if(peer->transfer->dir == TRNSD_DOWN)
2684             resettransfer(peer->transfer);
2685         transferdetach(peer->transfer);
2686     }
2687     if(peer->sk->data == peer)
2688         peer->sk->data = NULL;
2689     peer->sk->readcb = NULL;
2690     peer->sk->writecb = NULL;
2691     peer->sk->errcb = NULL;
2692     putsock(peer->sk);
2693     endcompress(peer);
2694     if(peer->supports != NULL)
2695     {
2696         for(i = 0; peer->supports[i] != NULL; i++)
2697             free(peer->supports[i]);
2698         free(peer->supports);
2699     }
2700     if(peer->mbspath != NULL)
2701         free(peer->mbspath);
2702     if(peer->inbuf != NULL)
2703         free(peer->inbuf);
2704     if(peer->key != NULL)
2705         free(peer->key);
2706     if(peer->wcsname != NULL)
2707         free(peer->wcsname);
2708     if(peer->nativename != NULL)
2709         free(peer->nativename);
2710     if(peer->fn != NULL)
2711         putfnetnode(peer->fn);
2712     while((qcmd = ulqcmd(&peer->queue)) != NULL)
2713         freeqcmd(qcmd);
2714     free(peer);
2715     numdcpeers--;
2716 }
2717
2718 static void hubconnect(struct fnetnode *fn)
2719 {
2720     fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
2721     fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
2722     getfnetnode(fn);
2723     fn->data = newdchub(fn);
2724     fn->sk->data = fn;
2725     return;
2726 }
2727
2728 static void hubdestroy(struct fnetnode *fn)
2729 {
2730     struct dchub *hub;
2731     struct qcommand *qcmd;
2732     
2733     hub = (struct dchub *)fn->data;
2734     if(fn->sk != NULL)
2735     {
2736         if(fn->sk->data == fn)
2737         {
2738             fn->sk->data = NULL;
2739             putfnetnode(fn);
2740         }
2741     }
2742     if(hub == NULL)
2743         return;
2744     while((qcmd = ulqcmd(&hub->queue)) != NULL)
2745         freeqcmd(qcmd);
2746     if(hub->nativename != NULL)
2747         free(hub->nativename);
2748     if(hub->nativenick != NULL)
2749         free(hub->nativenick);
2750     if(hub->inbuf != NULL)
2751         free(hub->inbuf);
2752     free(hub);
2753 }
2754
2755 static wchar_t *dcbasename(wchar_t *filename)
2756 {
2757     wchar_t *ret;
2758     
2759     if((ret = wcsrchr(filename, L'\\')) != NULL)
2760         return(ret + 1);
2761     return(filename);
2762 }
2763
2764 static struct transferiface dctransfer =
2765 {
2766     .detach = (void (*)(struct transfer *, void *))dctransdetach,
2767     .gotdata = (void (*)(struct transfer *, void *))dctransgotdata,
2768     .endofdata = (void (*)(struct transfer *, void *))dctransendofdata,
2769     .wantdata = (void (*)(struct transfer *, void *))dcwantdata
2770 };
2771
2772 static struct fnet dcnet =
2773 {
2774     .name = L"dc",
2775     .connect = hubconnect,
2776     .destroy = hubdestroy,
2777     .setnick = hubsetnick,
2778     .reqconn = hubreqconn,
2779     .sendchat = hubsendchat,
2780     .search = hubsearch,
2781     .filebasename = dcbasename
2782 };
2783
2784 static void peerread(struct socket *sk, struct dcpeer *peer)
2785 {
2786     char *newbuf, *p;
2787     size_t datalen;
2788
2789     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
2790         return;
2791     sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
2792     memcpy(peer->inbuf + peer->inbufdata, newbuf, datalen);
2793     free(newbuf);
2794     p = peer->inbuf + peer->inbufdata;
2795     peer->inbufdata += datalen;
2796     while((datalen > 0) && (p = memchr(p, '|', datalen)) != NULL)
2797     {
2798         *(p++) = 0;
2799         newqcmd(&peer->queue, peer->inbuf);
2800         memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
2801         datalen = peer->inbufdata;
2802         p = peer->inbuf;
2803     }
2804 }
2805
2806 static void peererror(struct socket *sk, int err, struct dcpeer *peer)
2807 {
2808     freedcpeer(peer);
2809 }
2810
2811 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
2812 {
2813     struct dcpeer *peer;
2814     
2815     if(err != 0)
2816     {
2817         putfnetnode(fn);
2818         return;
2819     }
2820     peer = newdcpeer(sk);
2821     peer->fn = fn;
2822     peer->accepted = 0;
2823     sk->readcb = (void (*)(struct socket *, void *))peerread;
2824     sk->errcb = (void (*)(struct socket *, int, void *))peererror;
2825     sk->data = peer;
2826     socksettos(sk, confgetint("fnet", "fnptos"));
2827     peerhandleaction(sk, peer, NULL, NULL);
2828     putsock(sk);
2829 }
2830
2831 static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
2832 {
2833     struct dcpeer *peer;
2834     
2835     peer = newdcpeer(newsk);
2836     peer->accepted = 1;
2837     newsk->readcb = (void (*)(struct socket *, void *))peerread;
2838     newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
2839     newsk->data = peer;
2840     socksettos(newsk, confgetint("fnet", "fnptos"));
2841     peerhandleaction(sk, peer, NULL, NULL);
2842 }
2843
2844 static void updatehmlist(void)
2845 {
2846     int i, lev, ic, ret;
2847     struct sharecache *node;
2848     char *buf, *buf2, numbuf[32];
2849     size_t bufsize, bufdata;
2850     int fd, ibuf;
2851     
2852     bufdata = 0;
2853     buf = smalloc(bufsize = 65536);
2854     node = shareroot->child;
2855     lev = 0;
2856     while(1)
2857     {
2858         ic = 0;
2859         if((buf2 = icwcstombs(node->name, DCCHARSET)) != NULL)
2860         {
2861             for(i = 0; i < lev; i++)
2862                 addtobuf(buf, 9);
2863             bufcat(buf, buf2, strlen(buf2));
2864             free(buf2);
2865             if(node->f.b.type == FILE_REG)
2866             {
2867                 addtobuf(buf, '|');
2868                 sprintf(numbuf, "%i", node->size);
2869                 bufcat(buf, numbuf, strlen(numbuf));
2870             }
2871             addtobuf(buf, 13);
2872             addtobuf(buf, 10);
2873         } else {
2874             ic = 1;
2875         }
2876         if((node->child != NULL) && !ic)
2877         {
2878             lev++;
2879             node = node->child;
2880         } else if(node->next != NULL) {
2881             node = node->next;
2882         } else {
2883             while(node->next == NULL)
2884             {
2885                 lev--;
2886                 node = node->parent;
2887                 if(node == shareroot)
2888                     break;
2889             }
2890             if(node == shareroot)
2891                 break;
2892             node = node->next;
2893         }
2894     }
2895     if(hmlistname != NULL)
2896     {
2897         unlink(hmlistname);
2898         free(hmlistname);
2899     }
2900     hmlistname = sstrdup("/tmp/dc-filelist-hm-XXXXXX");
2901     if((fd = mkstemp(hmlistname)) < 0)
2902     {
2903         flog(LOG_WARNING, "could not create HM file list tempfile: %s", strerror(errno));
2904         free(hmlistname);
2905         hmlistname = NULL;
2906     } else {
2907         /*
2908          * I do not want to implement a good Huffman encoder, and it's not
2909          * like Huffman encoding actually yields any impressive results
2910          * for DC file lists anyway, so I'll just output a bogus
2911          * tree. Implement a good encoder if you want to.
2912          */
2913         write(fd, "HE3\r\0", 5);
2914         write(fd, &bufdata, 4);
2915         ibuf = 256;
2916         write(fd, &ibuf, 2);
2917         ibuf = 8;
2918         for(i = 0; i < 256; i++)
2919         {
2920             write(fd, &i, 1);
2921             write(fd, &ibuf, 1);
2922         }
2923         for(i = 0; i < 256; i++)
2924             write(fd, &i, 1);
2925         for(buf2 = buf; bufdata > 0;)
2926         {
2927             if((ret = write(fd, buf2, bufdata)) < 0)
2928             {
2929                 flog(LOG_WARNING, "could not write file list: %s", strerror(errno));
2930                 break;
2931             }
2932             bufdata -= ret;
2933             buf2 += ret;
2934         }
2935         close(fd);
2936     }
2937     free(buf);
2938 }
2939
2940 static struct xmlent
2941 {
2942     wchar_t c;
2943     wchar_t *ent;
2944 } entities[] = {
2945     {L'&', L"&amp;"},
2946     {L'\"', L"&#34;"},
2947 /*  {L'\'', L"&quot;"},  Emulate DC++ escaping by omitting this. */
2948     {L'\0', NULL}
2949 };
2950
2951 static wchar_t *escapexml(wchar_t *src)
2952 {
2953     static wchar_t *buf = NULL;;
2954     int ret, c;
2955     wchar_t nbuf[32];
2956     size_t bufsize, bufdata;
2957     struct xmlent *ent;
2958     
2959     if(buf != NULL)
2960         free(buf);
2961     buf = NULL;
2962     bufsize = bufdata = 0;
2963     for(; *src != L'\0'; src++)
2964     {
2965         c = wctob(*src);
2966         if((c > 0) && (c < 32))
2967         {
2968             bufcat(buf, L"&#", 2);
2969             ret = swprintf(nbuf, (sizeof(nbuf) / sizeof(*nbuf)), L"%i", c);
2970             bufcat(buf, nbuf, ret);
2971             addtobuf(buf, L';');
2972         } else {
2973             for(ent = entities; ent->ent != NULL; ent++)
2974             {
2975                 if(ent->c == *src)
2976                 {
2977                     bufcat(buf, ent->ent, wcslen(ent->ent));
2978                     break;
2979                 }
2980             }
2981             if(ent->ent == NULL)
2982                 addtobuf(buf, *src);
2983         }
2984     }
2985     addtobuf(buf, L'\0');
2986     return(buf);
2987 }
2988
2989 static void updatexmllist(void)
2990 {
2991     int i, fd, lev;
2992     FILE *fs;
2993     char cidbuf[14], *namebuf;
2994     char *hashbuf;
2995     struct sharecache *node;
2996     
2997     if(xmllistname != NULL)
2998     {
2999         unlink(xmllistname);
3000         free(xmllistname);
3001     }
3002     xmllistname = sstrdup("/tmp/dc-filelist-dcxml-XXXXXX");
3003     if((fd = mkstemp(xmllistname)) < 0)
3004     {
3005         flog(LOG_WARNING, "could not create XML file list tempfile: %s", strerror(errno));
3006         free(xmllistname);
3007         xmllistname = NULL;
3008         return;
3009     }
3010     if((fs = fdopen(fd, "w")) == NULL)
3011     {
3012         flog(LOG_WARNING, "could not fdopen XML list fd %i: %s", fd, strerror(errno));
3013         unlink(xmllistname);
3014         free(xmllistname);
3015         xmllistname = NULL;
3016         close(fd);
3017         return;
3018     }
3019     fprintf(fs, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n");
3020     for(i = 0; i < sizeof(cidbuf) - 1; i++)
3021         cidbuf[i] = (rand() % ('Z' - 'A' + 1)) + 'A';
3022     cidbuf[i] = 0;
3023     fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, DCIDFULL);
3024     
3025     node = shareroot->child;
3026     lev = 0;
3027     while(1)
3028     {
3029         if((namebuf = icswcstombs(escapexml(node->name), "UTF-8", NULL)) != NULL)
3030         {
3031             for(i = 0; i < lev; i++)
3032                 fputc('\t', fs);
3033             if(node->child != NULL)
3034             {
3035                 fprintf(fs, "<Directory Name=\"%s\">\r\n", namebuf);
3036                 node = node->child;
3037                 lev++;
3038                 continue;
3039             } else {
3040                 fprintf(fs, "<File Name=\"%s\" Size=\"%i\"", namebuf, node->size);
3041                 if(node->f.b.hastth)
3042                 {
3043                     hashbuf = base32encode(node->hashtth, 24);
3044                     fprintf(fs, " TTH=\"%.39s\"", hashbuf);
3045                     free(hashbuf);
3046                 }
3047                 fprintf(fs, "/>\r\n");
3048             }
3049             while(node->next == NULL)
3050             {
3051                 node = node->parent;
3052                 if(node == shareroot)
3053                 {
3054                     break;
3055                 } else {
3056                     lev--;
3057                     for(i = 0; i < lev; i++)
3058                         fputc('\t', fs);
3059                     fprintf(fs, "</Directory>\r\n");
3060                 }
3061             }
3062             if(node == shareroot)
3063                 break;
3064             node = node->next;
3065         }
3066     }
3067     
3068 #ifdef DCPP_MASQUERADE
3069     fprintf(fs, "</FileListing>");
3070 #else
3071     fprintf(fs, "</FileListing>\r\n");
3072 #endif
3073     fclose(fs);
3074 }
3075
3076 static void updatexmlbz2list(void)
3077 {
3078     int err, fd;
3079     FILE *in;
3080     FILE *real;
3081     BZFILE *out;
3082     char buf[1024];
3083     size_t bufdata;
3084     
3085     if(xmllistname == NULL)
3086         return;
3087     if((in = fopen(xmllistname, "r")) == NULL)
3088     {
3089         flog(LOG_WARNING, "could not open XML file list for bzipping: %s", strerror(errno));
3090         return;
3091     }
3092     if(xmlbz2listname != NULL)
3093     {
3094         unlink(xmlbz2listname);
3095         free(xmlbz2listname);
3096     }
3097     xmlbz2listname = sstrdup("/tmp/dc-filelist-dcxmlbz2-XXXXXX");
3098     if((fd = mkstemp(xmlbz2listname)) < 0)
3099     {
3100         flog(LOG_WARNING, "could not create bzipped XML file list tempfile: %s", strerror(errno));
3101         free(xmlbz2listname);
3102         xmlbz2listname = NULL;
3103         fclose(in);
3104         return;
3105     }
3106     if((real = fdopen(fd, "w")) == NULL)
3107     {
3108         flog(LOG_WARNING, "could not fdopen bzipped XML list fd %i: %s", fd, strerror(errno));
3109         close(fd);
3110         unlink(xmlbz2listname);
3111         free(xmlbz2listname);
3112         xmlbz2listname = NULL;
3113         fclose(in);
3114         return;
3115     }
3116     out = BZ2_bzWriteOpen(&err, real, 9, 0, 0);
3117     if(err != BZ_OK)
3118     {
3119         flog(LOG_WARNING, "could not open bzip2 stream from XML list");
3120         fclose(real);
3121         unlink(xmlbz2listname);
3122         free(xmlbz2listname);
3123         xmlbz2listname = NULL;
3124         fclose(in);
3125         return;
3126     }
3127     while(!feof(in))
3128     {
3129         bufdata = fread(buf, 1, sizeof(buf), in);
3130         BZ2_bzWrite(&err, out, buf, bufdata);
3131     }
3132     fclose(in);
3133     BZ2_bzWriteClose(&err, out, 0, NULL, NULL);
3134     fclose(real);
3135 }
3136
3137 static int shareupdate(unsigned long long uusharesize, void *data)
3138 {
3139     updatehmlist();
3140     updatexmllist();
3141     updatexmlbz2list();
3142     return(0);
3143 }
3144
3145 static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, struct socket *sk, void *data)
3146 {
3147     char *p;
3148     struct command *cmd;
3149     
3150     if((p = strchr(qcmd->string, ' ')) != NULL)
3151         *(p++) = 0;
3152     for(cmd = cmdlist; cmd->handler != NULL; cmd++)
3153     {
3154         if(!strcmp(cmd->name, qcmd->string))
3155             break;
3156     }
3157     if(cmd->handler != NULL)
3158         cmd->handler(sk, data, qcmd->string, p);
3159 /*
3160     else
3161         flog(LOG_DEBUG, "Unimplemented DC command: %s \"%s\"", qcmd->string, p?p:"noargs");
3162 */
3163 }
3164
3165 static int run(void)
3166 {
3167     struct fnetnode *fn, *nextfn;
3168     struct dchub *hub;
3169     struct dcpeer *peer, *nextpeer;
3170     struct qcommand *qcmd;
3171     int ret;
3172     
3173     ret = 0;
3174     for(fn = fnetnodes; fn != NULL; fn = nextfn)
3175     {
3176         nextfn = fn->next;
3177         if(fn->fnet != &dcnet)
3178             continue;
3179         if(fn->data == NULL)
3180             continue;
3181         hub = (struct dchub *)fn->data;
3182         if((qcmd = ulqcmd(&hub->queue)) != NULL)
3183         {
3184             if(*qcmd->string == '$')
3185             {
3186                 if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
3187                     dispatchcommand(qcmd, hubcmds, fn->sk, fn);
3188             } else if(*qcmd->string != 0) {
3189                 hubrecvchat(fn->sk, fn, NULL, qcmd->string);
3190             }
3191             freeqcmd(qcmd);
3192             ret = 1;
3193             break;
3194         }
3195     }
3196     for(peer = peers; peer != NULL; peer = nextpeer)
3197     {
3198         nextpeer = peer->next;
3199         if((qcmd = ulqcmd(&peer->queue)) != NULL)
3200         {
3201             if(*qcmd->string == '$')
3202                 dispatchcommand(qcmd, peercmds, peer->sk, peer);
3203             freeqcmd(qcmd);
3204             ret = 1;
3205             break;
3206         }
3207     }
3208     return(ret);
3209 }
3210
3211 static void preinit(int hup)
3212 {
3213     if(hup)
3214         return;
3215     regfnet(&dcnet);
3216 }
3217
3218 static int updateudpport(struct configvar *var, void *uudata)
3219 {
3220     struct sockaddr_in addr;
3221     struct socket *newsock;
3222     
3223     memset(&addr, 0, sizeof(addr));
3224     addr.sin_family = AF_INET;
3225     addr.sin_port = htons(var->val.num);
3226     if((newsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3227     {
3228         flog(LOG_WARNING, "could not create new DC UDP socket, reverting to old: %s", strerror(errno));
3229         return(0);
3230     }
3231     newsock->readcb = udpread;
3232     if(udpsock != NULL)
3233         putsock(udpsock);
3234     udpsock = newsock;
3235     return(0);
3236 }
3237
3238 static int updatetcpport(struct configvar *var, void *uudata)
3239 {
3240     struct sockaddr_in addr;
3241     struct socket *newsock;
3242     
3243     memset(&addr, 0, sizeof(addr));
3244     addr.sin_family = AF_INET;
3245     addr.sin_port = htons(var->val.num);
3246     if((newsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3247         flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3248     if(tcpsock != NULL)
3249         putsock(tcpsock);
3250     tcpsock = newsock;
3251     return(0);
3252 }
3253
3254 static int init(int hup)
3255 {
3256     struct sockaddr_in addr;
3257     
3258     if(!hup)
3259     {
3260         GCBREG(sharechangecb, shareupdate, NULL);
3261         if(udpsock != NULL)
3262             putsock(udpsock);
3263         if(tcpsock != NULL)
3264             putsock(tcpsock);
3265         addr.sin_family = AF_INET;
3266         memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
3267         addr.sin_port = htons(confgetint("dc", "udpport"));
3268         if((udpsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3269         {
3270             flog(LOG_CRIT, "could not create DC UDP socket: %s", strerror(errno));
3271             return(1);
3272         }
3273         udpsock->readcb = udpread;
3274         addr.sin_port = htons(confgetint("dc", "tcpport"));
3275         if((tcpsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3276             flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3277         CBREG(confgetvar("dc", "udpport"), conf_update, updateudpport, NULL, NULL);
3278         CBREG(confgetvar("dc", "tcpport"), conf_update, updatetcpport, NULL, NULL);
3279         CBREG(confgetvar("net", "mode"), conf_update, updatetcpport, NULL, NULL);
3280     }
3281     return(0);
3282 }
3283
3284 static void terminate(void)
3285 {
3286     if(hmlistname != NULL)
3287     {
3288         unlink(hmlistname);
3289         free(hmlistname);
3290     }
3291     if(xmllistname != NULL)
3292     {
3293         unlink(xmllistname);
3294         free(xmllistname);
3295     }
3296     if(xmlbz2listname != NULL)
3297     {
3298         unlink(xmlbz2listname);
3299         free(xmlbz2listname);
3300     }
3301 }
3302
3303 static struct configvar myvars[] =
3304 {
3305     {CONF_VAR_STRING, "desc", {.str = L""}},
3306     {CONF_VAR_STRING, "speedstring", {.str = L"LAN(T1)"}},
3307     {CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
3308     {CONF_VAR_INT, "udpport", {.num = 0}},
3309     {CONF_VAR_INT, "tcpport", {.num = 0}},
3310     {CONF_VAR_END}
3311 };
3312
3313 static struct module me =
3314 {
3315     .conf =
3316     {
3317         .vars = myvars
3318     },
3319     .preinit = preinit,
3320     .init = init,
3321     .run = run,
3322     .terminate = terminate,
3323     .name = "dc"
3324 };
3325
3326 MODULE(me)