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