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