Add the ability to search for hashes.
[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') && (minsize == 0))
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 struct hash *findsehash(struct sexpr *sexpr)
2176 {
2177     struct hash *h1, *h2;
2178     
2179     switch(sexpr->op)
2180     {
2181     case SOP_AND:
2182         if((h1 = findsehash(sexpr->l)) != NULL)
2183             return(h1);
2184         if((h1 = findsehash(sexpr->r)) != NULL)
2185             return(h1);
2186         break;
2187     case SOP_OR:
2188         h1 = findsehash(sexpr->l);
2189         h2 = findsehash(sexpr->r);
2190         if(hashcmp(h1, h2))
2191             return(h1);
2192         break;
2193     case SOP_HASHIS:
2194         if(!wcscmp(sexpr->d.hash->algo, L"TTH"))
2195             return(sexpr->d.hash);
2196     default:
2197         break;
2198     }
2199     return(NULL);
2200 }
2201
2202 static int hubsearch(struct fnetnode *fn, struct search *srch, struct srchfnnlist *ln)
2203 {
2204     struct dchub *hub;
2205     struct wcslist *list, *cur;
2206     char *sstr, *buf, *p;
2207     size_t sstrsize, sstrdata;
2208     struct sockaddr *name;
2209     socklen_t namelen;
2210     int minsize, maxsize;
2211     struct hash *hash;
2212     
2213     hub = fn->data;
2214     if((fn->state != FNN_EST) || (fn->sk == NULL) || (fn->sk->state != SOCK_EST))
2215         return(1);
2216     list = findsexprstrs(srch->sexpr);
2217     findsizelimit(srch->sexpr, &minsize, &maxsize);
2218     hash = findsehash(srch->sexpr);
2219     if((minsize != 0) && (maxsize != -1))
2220     {
2221         /* Choose either minsize or maxsize by trying to determine
2222          * which choice will be most restrictive. The result can be
2223          * approximative at best anyway, so don't try too hard... */
2224         if((50000000 - maxsize) > (minsize - 50000000))
2225             minsize = 0;
2226         else
2227             maxsize = -1;
2228     }
2229     sstr = NULL;
2230     sstrsize = sstrdata = 0;
2231     if((hash != NULL) && (hash->len == 24))
2232     {
2233         /* Prioritize hash searches above all else */
2234         bufcat(sstr, "F?T?0?9?TTH:", 12);
2235         buf = base32encode(hash->buf, hash->len);
2236         
2237         bufcat(sstr, buf, 39);
2238         free(buf);
2239     } else {
2240         if(minsize != 0)
2241         {
2242             sizebuf2(sstr, sstrdata + 32, 1);
2243             snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?F?%i?1?", minsize);
2244         } else if(maxsize != -1) {
2245             sizebuf2(sstr, sstrdata + 32, 1);
2246             snprintf(sstr + sstrdata, sstrsize - sstrdata, "T?T?%i?1?", maxsize);
2247         } else {
2248             bufcat(sstr, "F?F?0?1?", 8);
2249         }
2250         if(list != NULL)
2251         {
2252             for(cur = list; cur != NULL; cur = cur->next)
2253             {
2254                 if((buf = icwcstombs(cur->str, DCCHARSET)) == NULL)
2255                 {
2256                     /* Can't find anything anyway if the search expression
2257                      * requires characters outside DC's charset. There's
2258                      * nothing technically wrong with the search itself,
2259                      * however, so return success. This should be
2260                      * considered as an optimization. */
2261                     freesl(&list);
2262                     if(sstr != NULL)
2263                         free(sstr);
2264                     return(0);
2265                 }
2266                 if(cur != list)
2267                     addtobuf(sstr, '$');
2268                 /*
2269                  * It probably doesn't hurt if buf contains any extra
2270                  * dollar signs - it will just result in extra search
2271                  * terms, and the extraneous results will be filtered by
2272                  * the search layer anyway. It hurts if it contains any
2273                  * pipes, though, so let's sell them for money.
2274                  */
2275                 for(p = buf; *p; p++)
2276                 {
2277                     if(*p == '|')
2278                         *p = '$';
2279                 }
2280                 bufcat(sstr, buf, strlen(buf));
2281                 free(buf);
2282             }
2283         } else {
2284             /* Will match all files... :-/ */
2285             addtobuf(sstr, '.');
2286         }
2287     }
2288     addtobuf(sstr, 0);
2289     if(tcpsock != NULL)
2290     {
2291         if(sockgetremotename(udpsock, &name, &namelen) < 0)
2292         {
2293             flog(LOG_WARNING, "cannot get address of UDP socket");
2294         } else {
2295             qstrf(fn->sk, "$Search %s %s|", formataddress(name, namelen), sstr);
2296             free(name);
2297         }
2298     } else {
2299         qstrf(fn->sk, "$Search Hub:%s %s|", hub->nativenick, sstr);
2300     }
2301     free(sstr);
2302     freesl(&list);
2303     fn->lastsrch = time(NULL);
2304     return(0);
2305 }
2306
2307 #undef skipcmd
2308 #undef qstr
2309 #undef qstrf
2310
2311 #define cc(c) ((void (*)(struct socket *, void *, char *, char *))(c))
2312 struct command hubcmds[] =
2313 {
2314     {"$Lock", cc(cmd_lock)},
2315     {"$HubName", cc(cmd_hubname)},
2316     {"$Hello", cc(cmd_hello)},
2317     {"$Quit", cc(cmd_quit)},
2318     {"$NickList", cc(cmd_nicklist)},
2319     {"$OpList", cc(cmd_oplist)},
2320     {"$MyINFO", cc(cmd_myinfo)},
2321     {"$ForceMove", cc(cmd_forcemove)},
2322     {"$Search", cc(cmd_search)},
2323     {"$MultiSearch", cc(cmd_search)},
2324     {"$ConnectToMe", cc(cmd_connecttome)},
2325     {"$RevConnectToMe", cc(cmd_revconnecttome)},
2326     {"$GetNetInfo", cc(cmd_getnetinfo)},
2327     {"$To:", cc(cmd_to)},
2328     {"$SR", cc(cmd_sr)},
2329     {"$UserCommand", cc(cmd_usercommand)},
2330     {NULL, NULL}
2331 };
2332
2333 struct command peercmds[] =
2334 {
2335     {"$MyNick", cc(cmd_mynick)},
2336     {"$Lock", cc(cmd_peerlock)},
2337     {"$Direction", cc(cmd_direction)},
2338     {"$Key", cc(cmd_key)},
2339     {"$FileLength", cc(cmd_filelength)},
2340     {"$Error", cc(cmd_error)},
2341     {"$MaxedOut", cc(cmd_maxedout)},
2342     {"$Get", cc(cmd_get)},
2343     {"$Send", cc(cmd_send)},
2344     {"$Supports", cc(cmd_supports)},
2345     {"$GetBlock", cc(cmd_getblock)},
2346     {"$UGetBlock", cc(cmd_getblock)},
2347     {"$GetZBlock", cc(cmd_getblock)},
2348     {"$UGetZBlock", cc(cmd_getblock)},
2349     {"$ADCGET", cc(cmd_adcget)},
2350     {NULL, NULL}
2351 };
2352 #undef cc
2353
2354 static void dctransdetach(struct transfer *transfer, struct dcpeer *peer)
2355 {
2356     CBUNREG(transfer, trans_filterout, peer);
2357     if(peer->freeing)
2358         return;
2359     peer->transfer = NULL;
2360     freedcpeer(peer);
2361 }
2362
2363 static void dctransgotdata(struct transfer *transfer, struct dcpeer *peer)
2364 {
2365     int ret;
2366     void *buf;
2367     char outbuf[1024];
2368     z_stream *cstr;
2369     size_t bufsize;
2370     
2371     if((peer->state == PEER_TRNS) || (peer->state == PEER_SYNC))
2372     {
2373         if(sockqueuesize(peer->sk) < 65536)
2374         {
2375             if((buf = transfergetdata(transfer, &bufsize)) != NULL)
2376             {
2377                 if(peer->compress == CPRS_NONE)
2378                 {
2379                     sockqueue(peer->sk, buf, bufsize);
2380                 } else if(peer->compress == CPRS_ZLIB) {
2381                     cstr = peer->cprsdata;
2382                     cstr->next_in = buf;
2383                     cstr->avail_in = bufsize;
2384                     while(cstr->avail_in > 0)
2385                     {
2386                         cstr->next_out = outbuf;
2387                         cstr->avail_out = sizeof(outbuf);
2388                         if((ret = deflate(cstr, 0)) != Z_OK)
2389                         {
2390                             flog(LOG_WARNING, "bug? deflate() did not return Z_OK (but rather %i)", ret);
2391                             freedcpeer(peer);
2392                             return;
2393                         }
2394                         sockqueue(peer->sk, outbuf, sizeof(outbuf) - cstr->avail_out);
2395                     }
2396                 }
2397                 free(buf);
2398             }
2399             if(peer->state == PEER_SYNC)
2400             {
2401                 if(peer->compress == CPRS_ZLIB)
2402                 {
2403                     cstr = peer->cprsdata;
2404                     cstr->next_in = NULL;
2405                     cstr->avail_in = 0;
2406                     do
2407                     {
2408                         cstr->next_out = outbuf;
2409                         cstr->avail_out = sizeof(outbuf);
2410                         ret = deflate(cstr, Z_FINISH);
2411                         if((ret != Z_OK) && (ret != Z_STREAM_END))
2412                         {
2413                             flog(LOG_WARNING, "bug? deflate(Z_FINISH) did not return Z_OK (but rather %i)", ret);
2414                             freedcpeer(peer);
2415                             return;
2416                         }
2417                         sockqueue(peer->sk, outbuf, sizeof(outbuf) - cstr->avail_out);
2418                     } while(ret != Z_STREAM_END);
2419                 }
2420                 if(peer->ptclose)
2421                 {
2422                     freedcpeer(peer);
2423                 } else {
2424                     peer->state = PEER_CMD;
2425                     endcompress(peer);
2426                     transfersetstate(transfer, TRNS_HS);
2427                     socksettos(peer->sk, confgetint("fnet", "fnptos"));
2428                     peer->sk->writecb = NULL;
2429                 }
2430             }
2431         }
2432     }
2433 }
2434
2435 static void dctransendofdata(struct transfer *transfer, struct dcpeer *peer)
2436 {
2437     peer->state = PEER_SYNC;
2438     dctransgotdata(transfer, peer);
2439 }
2440
2441 static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
2442 {
2443     if(transferdatasize(transfer) < 65536)
2444         peer->sk->ignread = 0;
2445 }
2446
2447 static void transread(struct socket *sk, struct dcpeer *peer)
2448 {
2449     void *buf;
2450     size_t bufsize;
2451     struct transfer *transfer;
2452     
2453     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
2454         return;
2455     if(peer->transfer == NULL)
2456     {
2457         free(buf);
2458         freedcpeer(peer);
2459         return;
2460     }
2461     transferputdata(peer->transfer, buf, bufsize);
2462     free(buf);
2463     if(peer->transfer->curpos >= peer->transfer->size)
2464     {
2465         transfer = peer->transfer;
2466         transferdetach(transfer);
2467         transferendofdata(transfer);
2468         return;
2469     }
2470     if(transferdatasize(peer->transfer) > 65535)
2471         sk->ignread = 1;
2472 }
2473
2474 static void transerr(struct socket *sk, int err, struct dcpeer *peer)
2475 {
2476     struct transfer *transfer;
2477
2478     if((transfer = peer->transfer) == NULL)
2479     {
2480         freedcpeer(peer);
2481         return;
2482     }
2483     transferdetach(transfer);
2484     transferendofdata(transfer);
2485 }
2486
2487 static void transwrite(struct socket *sk, struct dcpeer *peer)
2488 {
2489     if((peer->state != PEER_TRNS) && (peer->state != PEER_SYNC))
2490         return;
2491     if(peer->transfer == NULL)
2492     {
2493         freedcpeer(peer);
2494         return;
2495     }
2496     dctransgotdata(peer->transfer, peer);
2497 }
2498
2499 static void udpread(struct socket *sk, void *data)
2500 {
2501     char *buf, *p, *p2, *hashbuf;
2502     size_t buflen, hashlen;
2503     char *nick, *filename, *hubname;
2504     int size, slots;
2505     struct fnetnode *fn, *myfn;
2506     struct dchub *hub;
2507     struct srchres *sr;
2508     wchar_t *wnick, *wfile;
2509     struct hash *hash;
2510     
2511     if((buf = sockgetinbuf(sk, &buflen)) == NULL)
2512         return;
2513     buf = srealloc(buf, buflen + 1);
2514     buf[buflen] = 0;
2515     if(!strncmp(buf, "$SR ", 4))
2516     {
2517         p = buf + 4;
2518         nick = p;
2519         if((p2 = strchr(p, ' ')) == NULL)
2520         {
2521             free(buf);
2522             return;
2523         }
2524         *p2 = 0;
2525         p = p2 + 1;
2526         filename = p;
2527         if((p2 = strchr(p, 5)) == NULL)
2528         {
2529             free(buf);
2530             return;
2531         }
2532         *p2 = 0;
2533         p = p2 + 1;
2534         if((p2 = strchr(p, ' ')) == NULL)
2535         {
2536             free(buf);
2537             return;
2538         }
2539         *p2 = 0;
2540         size = atoi(p);
2541         p = p2 + 1;
2542         if((p2 = strchr(p, '/')) == NULL)
2543         {
2544             free(buf);
2545             return;
2546         }
2547         *p2 = 0;
2548         slots = atoi(p);
2549         p = p2 + 1;
2550         if((p2 = strchr(p, 5)) == NULL)
2551         {
2552             free(buf);
2553             return;
2554         }
2555         p = p2 + 1;
2556         hubname = p;
2557         if((p2 = strstr(p, " (")) == NULL)
2558         {
2559             free(buf);
2560             return;
2561         }
2562         *p2 = 0;
2563         if((wnick = icmbstowcs(nick, DCCHARSET)) == NULL)
2564         {
2565             free(buf);
2566             return;
2567         }
2568         if((wfile = icmbstowcs(filename, DCCHARSET)) == NULL)
2569         {
2570             free(wnick);
2571             free(buf);
2572             return;
2573         }
2574         myfn = NULL;
2575         hash = NULL;
2576         if(!strncmp(hubname, "TTH:", 4))
2577         {
2578             if((hashbuf = base32decode(hubname + 4, &hashlen)) != NULL)
2579             {
2580                 if(hashlen == 24)
2581                     hash = newhash(L"TTH", 24, hashbuf);
2582                 free(hashbuf);
2583             }
2584         } else {
2585             for(fn = fnetnodes; fn != NULL; fn = fn->next)
2586             {
2587                 if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
2588                 {
2589                     if((hub->nativename != NULL) && !strcmp(hub->nativename, hubname))
2590                     {
2591                         if(myfn == NULL)
2592                         {
2593                             myfn = fn;
2594                         } else {
2595                             myfn = NULL;
2596                             break;
2597                         }
2598                     }
2599                 }
2600             }
2601         }
2602         sr = newsrchres(&dcnet, wfile, wnick);
2603         if(sr->peernick != NULL)
2604             free(sr->peernick);
2605         sr->peernick = swcsdup(wnick);
2606         sr->size = size;
2607         sr->slots = slots;
2608         free(wfile);
2609         free(wnick);
2610         if(myfn != NULL)
2611             getfnetnode(sr->fn = myfn);
2612         if(hash != NULL)
2613             sr->hash = hash;
2614         submitsrchres(sr);
2615         freesrchres(sr);
2616     }
2617     free(buf);
2618 }
2619
2620 static void hubread(struct socket *sk, struct fnetnode *fn)
2621 {
2622     struct dchub *hub;
2623     char *newbuf;
2624     size_t datalen;
2625     char *p;
2626     
2627     hub = (struct dchub *)fn->data;
2628     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
2629         return;
2630     if(hub->inbufdata > 500000) /* Discard possible malicious data */
2631         hub->inbufdata = 0;
2632     sizebuf2(hub->inbuf, hub->inbufdata + datalen, 1);
2633     memcpy(hub->inbuf + hub->inbufdata, newbuf, datalen);
2634     free(newbuf);
2635     p = hub->inbuf + hub->inbufdata;
2636     hub->inbufdata += datalen;
2637     while((datalen > 0) && ((p = memchr(p, '|', datalen)) != NULL))
2638     {
2639         *(p++) = 0;
2640         newqcmd(&hub->queue, hub->inbuf);
2641         memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
2642         datalen = hub->inbufdata;
2643         p = hub->inbuf;
2644     }
2645 }
2646
2647 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
2648 {
2649     killfnetnode(fn);
2650 }
2651
2652 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
2653 {
2654     struct dchub *hub;
2655     char *buf;
2656     
2657     hub = fn->data;
2658     if((buf = icwcstombs(newnick, DCCHARSET)) == NULL)
2659         return(1);
2660     if((strchr(buf, ' ') != NULL) || (strchr(buf, '|') != NULL) || (strchr(buf, '$') != NULL))
2661     {
2662         free(buf);
2663         return(1);
2664     }
2665     if(hub == NULL) /* Not yet connected */
2666     {
2667         free(buf);
2668         return(0);
2669     }
2670     if(hub->nativenick != NULL)
2671         free(hub->nativenick);
2672     hub->nativenick = buf;
2673     return(0);
2674 }
2675
2676 static struct dchub *newdchub(struct fnetnode *fn)
2677 {
2678     struct dchub *new;
2679     
2680     new = smalloc(sizeof(*new));
2681     memset(new, 0, sizeof(*new));
2682     fn->data = new;
2683     if(hubsetnick(fn, fn->mynick))
2684         fnetsetnick(fn, L"DoldaConnectUser-IN");
2685     /* IN as in Invalid Nick */
2686     return(new);
2687 }
2688
2689 static struct dcpeer *newdcpeer(struct socket *sk)
2690 {
2691     struct dcpeer *new;
2692     
2693     new = smalloc(sizeof(*new));
2694     memset(new, 0, sizeof(*new));
2695     new->transfer = NULL;
2696     getsock(sk);
2697     new->sk = sk;
2698     new->next = peers;
2699     new->prev = NULL;
2700     if(peers != NULL)
2701         peers->prev = new;
2702     peers = new;
2703     numdcpeers++;
2704     return(new);
2705 }
2706
2707 static void freedcpeer(struct dcpeer *peer)
2708 {
2709     int i;
2710     struct qcommand *qcmd;
2711     
2712     peer->freeing = 1;
2713     if(peers == peer)
2714         peers = peer->next;
2715     if(peer->next != NULL)
2716         peer->next->prev = peer->prev;
2717     if(peer->prev != NULL)
2718         peer->prev->next = peer->next;
2719     if(peer->transfer != NULL)
2720     {
2721         if(peer->transfer->dir == TRNSD_UP)
2722             peer->transfer->close = 1;
2723         if(peer->transfer->dir == TRNSD_DOWN)
2724             resettransfer(peer->transfer);
2725         transferdetach(peer->transfer);
2726     }
2727     if(peer->sk->data == peer)
2728         peer->sk->data = NULL;
2729     peer->sk->readcb = NULL;
2730     peer->sk->writecb = NULL;
2731     peer->sk->errcb = NULL;
2732     putsock(peer->sk);
2733     endcompress(peer);
2734     if(peer->supports != NULL)
2735     {
2736         for(i = 0; peer->supports[i] != NULL; i++)
2737             free(peer->supports[i]);
2738         free(peer->supports);
2739     }
2740     if(peer->mbspath != NULL)
2741         free(peer->mbspath);
2742     if(peer->inbuf != NULL)
2743         free(peer->inbuf);
2744     if(peer->key != NULL)
2745         free(peer->key);
2746     if(peer->wcsname != NULL)
2747         free(peer->wcsname);
2748     if(peer->nativename != NULL)
2749         free(peer->nativename);
2750     if(peer->fn != NULL)
2751         putfnetnode(peer->fn);
2752     while((qcmd = ulqcmd(&peer->queue)) != NULL)
2753         freeqcmd(qcmd);
2754     free(peer);
2755     numdcpeers--;
2756 }
2757
2758 static void hubconnect(struct fnetnode *fn)
2759 {
2760     fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
2761     fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
2762     getfnetnode(fn);
2763     fn->data = newdchub(fn);
2764     fn->sk->data = fn;
2765     return;
2766 }
2767
2768 static void hubdestroy(struct fnetnode *fn)
2769 {
2770     struct dchub *hub;
2771     struct qcommand *qcmd;
2772     
2773     hub = (struct dchub *)fn->data;
2774     if(fn->sk != NULL)
2775     {
2776         if(fn->sk->data == fn)
2777         {
2778             fn->sk->data = NULL;
2779             putfnetnode(fn);
2780         }
2781     }
2782     if(hub == NULL)
2783         return;
2784     while((qcmd = ulqcmd(&hub->queue)) != NULL)
2785         freeqcmd(qcmd);
2786     if(hub->nativename != NULL)
2787         free(hub->nativename);
2788     if(hub->nativenick != NULL)
2789         free(hub->nativenick);
2790     if(hub->inbuf != NULL)
2791         free(hub->inbuf);
2792     free(hub);
2793 }
2794
2795 static wchar_t *dcbasename(wchar_t *filename)
2796 {
2797     wchar_t *ret;
2798     
2799     if((ret = wcsrchr(filename, L'\\')) != NULL)
2800         return(ret + 1);
2801     return(filename);
2802 }
2803
2804 static struct transferiface dctransfer =
2805 {
2806     .detach = (void (*)(struct transfer *, void *))dctransdetach,
2807     .gotdata = (void (*)(struct transfer *, void *))dctransgotdata,
2808     .endofdata = (void (*)(struct transfer *, void *))dctransendofdata,
2809     .wantdata = (void (*)(struct transfer *, void *))dcwantdata
2810 };
2811
2812 static struct fnet dcnet =
2813 {
2814     .name = L"dc",
2815     .connect = hubconnect,
2816     .destroy = hubdestroy,
2817     .setnick = hubsetnick,
2818     .reqconn = hubreqconn,
2819     .sendchat = hubsendchat,
2820     .search = hubsearch,
2821     .filebasename = dcbasename
2822 };
2823
2824 static void peerread(struct socket *sk, struct dcpeer *peer)
2825 {
2826     char *newbuf, *p;
2827     size_t datalen;
2828
2829     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
2830         return;
2831     sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
2832     memcpy(peer->inbuf + peer->inbufdata, newbuf, datalen);
2833     free(newbuf);
2834     p = peer->inbuf + peer->inbufdata;
2835     peer->inbufdata += datalen;
2836     while((datalen > 0) && (p = memchr(p, '|', datalen)) != NULL)
2837     {
2838         *(p++) = 0;
2839         newqcmd(&peer->queue, peer->inbuf);
2840         memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
2841         datalen = peer->inbufdata;
2842         p = peer->inbuf;
2843     }
2844 }
2845
2846 static void peererror(struct socket *sk, int err, struct dcpeer *peer)
2847 {
2848     freedcpeer(peer);
2849 }
2850
2851 static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
2852 {
2853     struct dcpeer *peer;
2854     
2855     if(err != 0)
2856     {
2857         putfnetnode(fn);
2858         return;
2859     }
2860     peer = newdcpeer(sk);
2861     peer->fn = fn;
2862     peer->accepted = 0;
2863     sk->readcb = (void (*)(struct socket *, void *))peerread;
2864     sk->errcb = (void (*)(struct socket *, int, void *))peererror;
2865     sk->data = peer;
2866     socksettos(sk, confgetint("fnet", "fnptos"));
2867     peerhandleaction(sk, peer, NULL, NULL);
2868     putsock(sk);
2869 }
2870
2871 static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
2872 {
2873     struct dcpeer *peer;
2874     
2875     peer = newdcpeer(newsk);
2876     peer->accepted = 1;
2877     newsk->readcb = (void (*)(struct socket *, void *))peerread;
2878     newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
2879     newsk->data = peer;
2880     socksettos(newsk, confgetint("fnet", "fnptos"));
2881     peerhandleaction(sk, peer, NULL, NULL);
2882 }
2883
2884 static void updatehmlist(void)
2885 {
2886     int i, lev, ic, ret;
2887     struct sharecache *node;
2888     char *buf, *buf2, numbuf[32];
2889     size_t bufsize, bufdata;
2890     int fd, ibuf;
2891     
2892     bufdata = 0;
2893     buf = smalloc(bufsize = 65536);
2894     node = shareroot->child;
2895     lev = 0;
2896     while(1)
2897     {
2898         ic = 0;
2899         if((buf2 = icwcstombs(node->name, DCCHARSET)) != NULL)
2900         {
2901             for(i = 0; i < lev; i++)
2902                 addtobuf(buf, 9);
2903             bufcat(buf, buf2, strlen(buf2));
2904             free(buf2);
2905             if(node->f.b.type == FILE_REG)
2906             {
2907                 addtobuf(buf, '|');
2908                 sprintf(numbuf, "%i", node->size);
2909                 bufcat(buf, numbuf, strlen(numbuf));
2910             }
2911             addtobuf(buf, 13);
2912             addtobuf(buf, 10);
2913         } else {
2914             ic = 1;
2915         }
2916         if((node->child != NULL) && !ic)
2917         {
2918             lev++;
2919             node = node->child;
2920         } else if(node->next != NULL) {
2921             node = node->next;
2922         } else {
2923             while(node->next == NULL)
2924             {
2925                 lev--;
2926                 node = node->parent;
2927                 if(node == shareroot)
2928                     break;
2929             }
2930             if(node == shareroot)
2931                 break;
2932             node = node->next;
2933         }
2934     }
2935     if(hmlistname != NULL)
2936     {
2937         unlink(hmlistname);
2938         free(hmlistname);
2939     }
2940     hmlistname = sstrdup("/tmp/dc-filelist-hm-XXXXXX");
2941     if((fd = mkstemp(hmlistname)) < 0)
2942     {
2943         flog(LOG_WARNING, "could not create HM file list tempfile: %s", strerror(errno));
2944         free(hmlistname);
2945         hmlistname = NULL;
2946     } else {
2947         /*
2948          * I do not want to implement a good Huffman encoder, and it's not
2949          * like Huffman encoding actually yields any impressive results
2950          * for DC file lists anyway, so I'll just output a bogus
2951          * tree. Implement a good encoder if you want to.
2952          */
2953         write(fd, "HE3\r\0", 5);
2954         write(fd, &bufdata, 4);
2955         ibuf = 256;
2956         write(fd, &ibuf, 2);
2957         ibuf = 8;
2958         for(i = 0; i < 256; i++)
2959         {
2960             write(fd, &i, 1);
2961             write(fd, &ibuf, 1);
2962         }
2963         for(i = 0; i < 256; i++)
2964             write(fd, &i, 1);
2965         for(buf2 = buf; bufdata > 0;)
2966         {
2967             if((ret = write(fd, buf2, bufdata)) < 0)
2968             {
2969                 flog(LOG_WARNING, "could not write file list: %s", strerror(errno));
2970                 break;
2971             }
2972             bufdata -= ret;
2973             buf2 += ret;
2974         }
2975         close(fd);
2976     }
2977     free(buf);
2978 }
2979
2980 static struct xmlent
2981 {
2982     wchar_t c;
2983     wchar_t *ent;
2984 } entities[] = {
2985     {L'&', L"&amp;"},
2986     {L'\"', L"&#34;"},
2987 /*  {L'\'', L"&quot;"},  Emulate DC++ escaping by omitting this. */
2988     {L'\0', NULL}
2989 };
2990
2991 static wchar_t *escapexml(wchar_t *src)
2992 {
2993     static wchar_t *buf = NULL;;
2994     int ret, c;
2995     wchar_t nbuf[32];
2996     size_t bufsize, bufdata;
2997     struct xmlent *ent;
2998     
2999     if(buf != NULL)
3000         free(buf);
3001     buf = NULL;
3002     bufsize = bufdata = 0;
3003     for(; *src != L'\0'; src++)
3004     {
3005         c = wctob(*src);
3006         if((c > 0) && (c < 32))
3007         {
3008             bufcat(buf, L"&#", 2);
3009             ret = swprintf(nbuf, (sizeof(nbuf) / sizeof(*nbuf)), L"%i", c);
3010             bufcat(buf, nbuf, ret);
3011             addtobuf(buf, L';');
3012         } else {
3013             for(ent = entities; ent->ent != NULL; ent++)
3014             {
3015                 if(ent->c == *src)
3016                 {
3017                     bufcat(buf, ent->ent, wcslen(ent->ent));
3018                     break;
3019                 }
3020             }
3021             if(ent->ent == NULL)
3022                 addtobuf(buf, *src);
3023         }
3024     }
3025     addtobuf(buf, L'\0');
3026     return(buf);
3027 }
3028
3029 static void updatexmllist(void)
3030 {
3031     int i, fd, lev;
3032     FILE *fs;
3033     char cidbuf[14], *namebuf;
3034     char *hashbuf;
3035     struct sharecache *node;
3036     
3037     if(xmllistname != NULL)
3038     {
3039         unlink(xmllistname);
3040         free(xmllistname);
3041     }
3042     xmllistname = sstrdup("/tmp/dc-filelist-dcxml-XXXXXX");
3043     if((fd = mkstemp(xmllistname)) < 0)
3044     {
3045         flog(LOG_WARNING, "could not create XML file list tempfile: %s", strerror(errno));
3046         free(xmllistname);
3047         xmllistname = NULL;
3048         return;
3049     }
3050     if((fs = fdopen(fd, "w")) == NULL)
3051     {
3052         flog(LOG_WARNING, "could not fdopen XML list fd %i: %s", fd, strerror(errno));
3053         unlink(xmllistname);
3054         free(xmllistname);
3055         xmllistname = NULL;
3056         close(fd);
3057         return;
3058     }
3059     fprintf(fs, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n");
3060     for(i = 0; i < sizeof(cidbuf) - 1; i++)
3061         cidbuf[i] = (rand() % ('Z' - 'A' + 1)) + 'A';
3062     cidbuf[i] = 0;
3063     fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, DCIDFULL);
3064     
3065     node = shareroot->child;
3066     lev = 0;
3067     while(1)
3068     {
3069         if((namebuf = icswcstombs(escapexml(node->name), "UTF-8", NULL)) != NULL)
3070         {
3071             for(i = 0; i < lev; i++)
3072                 fputc('\t', fs);
3073             if(node->child != NULL)
3074             {
3075                 fprintf(fs, "<Directory Name=\"%s\">\r\n", namebuf);
3076                 node = node->child;
3077                 lev++;
3078                 continue;
3079             } else {
3080                 fprintf(fs, "<File Name=\"%s\" Size=\"%i\"", namebuf, node->size);
3081                 if(node->f.b.hastth)
3082                 {
3083                     hashbuf = base32encode(node->hashtth, 24);
3084                     fprintf(fs, " TTH=\"%.39s\"", hashbuf);
3085                     free(hashbuf);
3086                 }
3087                 fprintf(fs, "/>\r\n");
3088             }
3089             while(node->next == NULL)
3090             {
3091                 node = node->parent;
3092                 if(node == shareroot)
3093                 {
3094                     break;
3095                 } else {
3096                     lev--;
3097                     for(i = 0; i < lev; i++)
3098                         fputc('\t', fs);
3099                     fprintf(fs, "</Directory>\r\n");
3100                 }
3101             }
3102             if(node == shareroot)
3103                 break;
3104             node = node->next;
3105         }
3106     }
3107     
3108 #ifdef DCPP_MASQUERADE
3109     fprintf(fs, "</FileListing>");
3110 #else
3111     fprintf(fs, "</FileListing>\r\n");
3112 #endif
3113     fclose(fs);
3114 }
3115
3116 static void updatexmlbz2list(void)
3117 {
3118     int err, fd;
3119     FILE *in;
3120     FILE *real;
3121     BZFILE *out;
3122     char buf[1024];
3123     size_t bufdata;
3124     
3125     if(xmllistname == NULL)
3126         return;
3127     if((in = fopen(xmllistname, "r")) == NULL)
3128     {
3129         flog(LOG_WARNING, "could not open XML file list for bzipping: %s", strerror(errno));
3130         return;
3131     }
3132     if(xmlbz2listname != NULL)
3133     {
3134         unlink(xmlbz2listname);
3135         free(xmlbz2listname);
3136     }
3137     xmlbz2listname = sstrdup("/tmp/dc-filelist-dcxmlbz2-XXXXXX");
3138     if((fd = mkstemp(xmlbz2listname)) < 0)
3139     {
3140         flog(LOG_WARNING, "could not create bzipped XML file list tempfile: %s", strerror(errno));
3141         free(xmlbz2listname);
3142         xmlbz2listname = NULL;
3143         fclose(in);
3144         return;
3145     }
3146     if((real = fdopen(fd, "w")) == NULL)
3147     {
3148         flog(LOG_WARNING, "could not fdopen bzipped XML list fd %i: %s", fd, strerror(errno));
3149         close(fd);
3150         unlink(xmlbz2listname);
3151         free(xmlbz2listname);
3152         xmlbz2listname = NULL;
3153         fclose(in);
3154         return;
3155     }
3156     out = BZ2_bzWriteOpen(&err, real, 9, 0, 0);
3157     if(err != BZ_OK)
3158     {
3159         flog(LOG_WARNING, "could not open bzip2 stream from XML list");
3160         fclose(real);
3161         unlink(xmlbz2listname);
3162         free(xmlbz2listname);
3163         xmlbz2listname = NULL;
3164         fclose(in);
3165         return;
3166     }
3167     while(!feof(in))
3168     {
3169         bufdata = fread(buf, 1, sizeof(buf), in);
3170         BZ2_bzWrite(&err, out, buf, bufdata);
3171     }
3172     fclose(in);
3173     BZ2_bzWriteClose(&err, out, 0, NULL, NULL);
3174     fclose(real);
3175 }
3176
3177 static int shareupdate(unsigned long long uusharesize, void *data)
3178 {
3179     updatehmlist();
3180     updatexmllist();
3181     updatexmlbz2list();
3182     return(0);
3183 }
3184
3185 static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, struct socket *sk, void *data)
3186 {
3187     char *p;
3188     struct command *cmd;
3189     
3190     if((p = strchr(qcmd->string, ' ')) != NULL)
3191         *(p++) = 0;
3192     for(cmd = cmdlist; cmd->handler != NULL; cmd++)
3193     {
3194         if(!strcmp(cmd->name, qcmd->string))
3195             break;
3196     }
3197     if(cmd->handler != NULL)
3198         cmd->handler(sk, data, qcmd->string, p);
3199 /*
3200     else
3201         flog(LOG_DEBUG, "Unimplemented DC command: %s \"%s\"", qcmd->string, p?p:"noargs");
3202 */
3203 }
3204
3205 static int run(void)
3206 {
3207     struct fnetnode *fn, *nextfn;
3208     struct dchub *hub;
3209     struct dcpeer *peer, *nextpeer;
3210     struct qcommand *qcmd;
3211     int ret;
3212     
3213     ret = 0;
3214     for(fn = fnetnodes; fn != NULL; fn = nextfn)
3215     {
3216         nextfn = fn->next;
3217         if(fn->fnet != &dcnet)
3218             continue;
3219         if(fn->data == NULL)
3220             continue;
3221         hub = (struct dchub *)fn->data;
3222         if((qcmd = ulqcmd(&hub->queue)) != NULL)
3223         {
3224             if(*qcmd->string == '$')
3225             {
3226                 if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
3227                     dispatchcommand(qcmd, hubcmds, fn->sk, fn);
3228             } else if(*qcmd->string != 0) {
3229                 hubrecvchat(fn->sk, fn, NULL, qcmd->string);
3230             }
3231             freeqcmd(qcmd);
3232             ret = 1;
3233             break;
3234         }
3235     }
3236     for(peer = peers; peer != NULL; peer = nextpeer)
3237     {
3238         nextpeer = peer->next;
3239         if((qcmd = ulqcmd(&peer->queue)) != NULL)
3240         {
3241             if(*qcmd->string == '$')
3242                 dispatchcommand(qcmd, peercmds, peer->sk, peer);
3243             freeqcmd(qcmd);
3244             ret = 1;
3245             break;
3246         }
3247     }
3248     return(ret);
3249 }
3250
3251 static void preinit(int hup)
3252 {
3253     if(hup)
3254         return;
3255     regfnet(&dcnet);
3256 }
3257
3258 static int updateudpport(struct configvar *var, void *uudata)
3259 {
3260     struct sockaddr_in addr;
3261     struct socket *newsock;
3262     
3263     memset(&addr, 0, sizeof(addr));
3264     addr.sin_family = AF_INET;
3265     addr.sin_port = htons(var->val.num);
3266     if((newsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3267     {
3268         flog(LOG_WARNING, "could not create new DC UDP socket, reverting to old: %s", strerror(errno));
3269         return(0);
3270     }
3271     newsock->readcb = udpread;
3272     if(udpsock != NULL)
3273         putsock(udpsock);
3274     udpsock = newsock;
3275     return(0);
3276 }
3277
3278 static int updatetcpport(struct configvar *var, void *uudata)
3279 {
3280     struct sockaddr_in addr;
3281     struct socket *newsock;
3282     
3283     memset(&addr, 0, sizeof(addr));
3284     addr.sin_family = AF_INET;
3285     addr.sin_port = htons(var->val.num);
3286     if((newsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3287         flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3288     if(tcpsock != NULL)
3289         putsock(tcpsock);
3290     tcpsock = newsock;
3291     return(0);
3292 }
3293
3294 static int init(int hup)
3295 {
3296     struct sockaddr_in addr;
3297     
3298     if(!hup)
3299     {
3300         GCBREG(sharechangecb, shareupdate, NULL);
3301         if(udpsock != NULL)
3302             putsock(udpsock);
3303         if(tcpsock != NULL)
3304             putsock(tcpsock);
3305         addr.sin_family = AF_INET;
3306         memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
3307         addr.sin_port = htons(confgetint("dc", "udpport"));
3308         if((udpsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3309         {
3310             flog(LOG_CRIT, "could not create DC UDP socket: %s", strerror(errno));
3311             return(1);
3312         }
3313         udpsock->readcb = udpread;
3314         addr.sin_port = htons(confgetint("dc", "tcpport"));
3315         if((tcpsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3316             flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3317         CBREG(confgetvar("dc", "udpport"), conf_update, updateudpport, NULL, NULL);
3318         CBREG(confgetvar("dc", "tcpport"), conf_update, updatetcpport, NULL, NULL);
3319         CBREG(confgetvar("net", "mode"), conf_update, updatetcpport, NULL, NULL);
3320     }
3321     return(0);
3322 }
3323
3324 static void terminate(void)
3325 {
3326     if(hmlistname != NULL)
3327     {
3328         unlink(hmlistname);
3329         free(hmlistname);
3330     }
3331     if(xmllistname != NULL)
3332     {
3333         unlink(xmllistname);
3334         free(xmllistname);
3335     }
3336     if(xmlbz2listname != NULL)
3337     {
3338         unlink(xmlbz2listname);
3339         free(xmlbz2listname);
3340     }
3341 }
3342
3343 static struct configvar myvars[] =
3344 {
3345     {CONF_VAR_STRING, "desc", {.str = L""}},
3346     {CONF_VAR_STRING, "speedstring", {.str = L"LAN(T1)"}},
3347     {CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
3348     {CONF_VAR_INT, "udpport", {.num = 0}},
3349     {CONF_VAR_INT, "tcpport", {.num = 0}},
3350     {CONF_VAR_END}
3351 };
3352
3353 static struct module me =
3354 {
3355     .conf =
3356     {
3357         .vars = myvars
3358     },
3359     .preinit = preinit,
3360     .init = init,
3361     .run = run,
3362     .terminate = terminate,
3363     .name = "dc"
3364 };
3365
3366 MODULE(me)