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