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