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