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