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