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