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