Merge branch 'master' of git.dolda2000.com:/srv/git/r/doldaconnect
[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;
d3372da9 3016}
3017
3018static void huberr(struct socket *sk, int err, struct fnetnode *fn)
3019{
3020 killfnetnode(fn);
3021}
3022
3023static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
3024{
3025 struct dchub *hub;
3026 char *buf;
3027
3028 hub = fn->data;
1d85f24b 3029 if((buf = icwcstombs(newnick, (hub == NULL)?DCCHARSET:(hub->charset))) == NULL)
d3372da9 3030 return(1);
3031 if((strchr(buf, ' ') != NULL) || (strchr(buf, '|') != NULL) || (strchr(buf, '$') != NULL))
3032 {
3033 free(buf);
3034 return(1);
3035 }
3036 if(hub == NULL) /* Not yet connected */
3037 {
3038 free(buf);
3039 return(0);
3040 }
3041 if(hub->nativenick != NULL)
3042 free(hub->nativenick);
3043 hub->nativenick = buf;
3044 return(0);
3045}
3046
3047static struct dchub *newdchub(struct fnetnode *fn)
3048{
3049 struct dchub *new;
1c4b02cd 3050 wchar_t *emu;
1d85f24b 3051 wchar_t *wcharset;
3052 char *charset;
d3372da9 3053
3054 new = smalloc(sizeof(*new));
3055 memset(new, 0, sizeof(*new));
3056 fn->data = new;
1c4b02cd 3057 if(confgetint("dc", "dcppemu"))
3058 new->dcppemu = 1;
3059 if((emu = wpfind(fn->args, L"dcppemu")) != NULL)
3060 {
3061 if(*emu == L'y')
3062 new->dcppemu = 1;
3063 if(*emu == L'n')
3064 new->dcppemu = 0;
3065 }
1d85f24b 3066 charset = NULL;
3067 if((wcharset = wpfind(fn->args, L"charset")) != NULL)
3068 {
3069 if((charset = icwcstombs(wcharset, "US-ASCII")) != NULL)
3070 {
3071 if(!havecharset(charset))
3072 {
3073 free(charset);
3074 charset = NULL;
3075 }
3076 }
3077 }
3078 if(charset != NULL)
3079 new->charset = charset;
3080 else
3081 new->charset = sstrdup(DCCHARSET);
d3372da9 3082 if(hubsetnick(fn, fn->mynick))
3083 fnetsetnick(fn, L"DoldaConnectUser-IN");
3084 /* IN as in Invalid Nick */
3085 return(new);
3086}
3087
3088static struct dcpeer *newdcpeer(struct socket *sk)
3089{
3090 struct dcpeer *new;
3091
3092 new = smalloc(sizeof(*new));
3093 memset(new, 0, sizeof(*new));
3094 new->transfer = NULL;
3095 getsock(sk);
3096 new->sk = sk;
1c4b02cd 3097 if(confgetint("dc", "dcppemu"))
3098 new->dcppemu = 1;
d3372da9 3099 new->next = peers;
3100 new->prev = NULL;
3101 if(peers != NULL)
3102 peers->prev = new;
3103 peers = new;
3104 numdcpeers++;
3105 return(new);
3106}
3107
3108static void freedcpeer(struct dcpeer *peer)
3109{
3110 int i;
3111 struct qcommand *qcmd;
3112
3113 peer->freeing = 1;
3114 if(peers == peer)
3115 peers = peer->next;
3116 if(peer->next != NULL)
3117 peer->next->prev = peer->prev;
3118 if(peer->prev != NULL)
3119 peer->prev->next = peer->next;
3120 if(peer->transfer != NULL)
3121 {
3122 if(peer->transfer->dir == TRNSD_UP)
3123 peer->transfer->close = 1;
3124 if(peer->transfer->dir == TRNSD_DOWN)
3125 resettransfer(peer->transfer);
3126 transferdetach(peer->transfer);
3127 }
1e9d82b1 3128 if(peer->timeout != NULL)
3129 canceltimer(peer->timeout);
cab0b442 3130 if(peer->sk->data == peer)
3131 peer->sk->data = NULL;
3132 peer->sk->readcb = NULL;
3133 peer->sk->writecb = NULL;
3134 peer->sk->errcb = NULL;
d3372da9 3135 putsock(peer->sk);
3136 endcompress(peer);
3137 if(peer->supports != NULL)
3138 {
3139 for(i = 0; peer->supports[i] != NULL; i++)
3140 free(peer->supports[i]);
3141 free(peer->supports);
3142 }
d3372da9 3143 if(peer->inbuf != NULL)
3144 free(peer->inbuf);
3145 if(peer->key != NULL)
3146 free(peer->key);
3147 if(peer->wcsname != NULL)
3148 free(peer->wcsname);
3149 if(peer->nativename != NULL)
3150 free(peer->nativename);
1d85f24b 3151 if(peer->charset != NULL)
3152 free(peer->charset);
d3372da9 3153 if(peer->fn != NULL)
3154 putfnetnode(peer->fn);
3155 while((qcmd = ulqcmd(&peer->queue)) != NULL)
3156 freeqcmd(qcmd);
3157 free(peer);
3158 numdcpeers--;
3159}
3160
ffa81d5f 3161static void hubconnect(struct fnetnode *fn, struct socket *sk)
d3372da9 3162{
ffa81d5f
FT
3163 struct dchub *hub;
3164
3165 sk->readcb = (void (*)(struct socket *, void *))hubread;
3166 sk->errcb = (void (*)(struct socket *, int, void *))huberr;
3167 fn->data = hub = newdchub(fn);
3168 sk->data = fn;
3169 getsock(hub->sk = sk);
d3372da9 3170 return;
3171}
3172
3173static void hubdestroy(struct fnetnode *fn)
3174{
08b74f8c 3175 int i;
d3372da9 3176 struct dchub *hub;
3177 struct qcommand *qcmd;
3178
3179 hub = (struct dchub *)fn->data;
ffa81d5f 3180 putsock(hub->sk);
d3372da9 3181 while((qcmd = ulqcmd(&hub->queue)) != NULL)
3182 freeqcmd(qcmd);
08b74f8c
FT
3183 if(hub->supports != NULL)
3184 {
3185 for(i = 0; hub->supports[i] != NULL; i++)
3186 free(hub->supports[i]);
3187 free(hub->supports);
3188 }
d3372da9 3189 if(hub->nativename != NULL)
3190 free(hub->nativename);
3191 if(hub->nativenick != NULL)
3192 free(hub->nativenick);
1d85f24b 3193 if(hub->charset != NULL)
3194 free(hub->charset);
d3372da9 3195 if(hub->inbuf != NULL)
3196 free(hub->inbuf);
3197 free(hub);
3198}
3199
ffa81d5f
FT
3200static void hubkill(struct fnetnode *fn)
3201{
3202 struct dchub *hub;
3203
3204 hub = (struct dchub *)fn->data;
3205 hub->sk->close = 1;
3206}
3207
d3372da9 3208static wchar_t *dcbasename(wchar_t *filename)
3209{
3210 wchar_t *ret;
3211
3212 if((ret = wcsrchr(filename, L'\\')) != NULL)
3213 return(ret + 1);
3214 return(filename);
3215}
3216
3217static struct transferiface dctransfer =
3218{
3219 .detach = (void (*)(struct transfer *, void *))dctransdetach,
3220 .gotdata = (void (*)(struct transfer *, void *))dctransgotdata,
3221 .endofdata = (void (*)(struct transfer *, void *))dctransendofdata,
3222 .wantdata = (void (*)(struct transfer *, void *))dcwantdata
3223};
3224
3225static struct fnet dcnet =
3226{
3227 .name = L"dc",
3228 .connect = hubconnect,
3229 .destroy = hubdestroy,
ffa81d5f 3230 .kill = hubkill,
d3372da9 3231 .setnick = hubsetnick,
3232 .reqconn = hubreqconn,
3233 .sendchat = hubsendchat,
3234 .search = hubsearch,
3235 .filebasename = dcbasename
3236};
3237
cab0b442 3238static void peerread(struct socket *sk, struct dcpeer *peer)
d3372da9 3239{
cab0b442 3240 char *newbuf, *p;
8cb2d75b 3241 size_t datalen, cnlen;
cab0b442 3242 struct command *cmd;
5ec60e42 3243
d3372da9 3244 if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
cab0b442 3245 return;
d3372da9 3246 sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
3247 memcpy(peer->inbuf + peer->inbufdata, newbuf, datalen);
3248 free(newbuf);
d3372da9 3249 peer->inbufdata += datalen;
9a2d6cb8 3250 if(peer->state == PEER_CMD)
d3372da9 3251 {
cab0b442 3252 p = peer->inbuf;
3253 while((peer->inbufdata > 0) && (p = memchr(peer->inbuf, '|', peer->inbufdata)) != NULL)
3254 {
3255 *(p++) = 0;
cab0b442 3256 for(cmd = peercmds; cmd->handler != NULL; cmd++)
3257 {
8cb2d75b
FT
3258 cnlen = strlen(cmd->name);
3259 if(!strncmp(peer->inbuf, cmd->name, cnlen) && ((peer->inbuf[cnlen] == ' ') || (peer->inbuf[cnlen] == 0)))
cab0b442 3260 break;
3261 }
8cb2d75b
FT
3262 if((cmd->limit == 0) || (peer->queue.size < cmd->limit))
3263 newqcmd(&peer->queue, peer->inbuf);
cab0b442 3264 memmove(peer->inbuf, p, peer->inbufdata -= p - peer->inbuf);
3265 if(cmd->stop)
3266 {
3267 peer->state = PEER_STOP;
3268 break;
8cb2d75b 3269 } else {
e0731d8c 3270 if(peer->queue.size > 50)
8cb2d75b 3271 sk->ignread = 1;
cab0b442 3272 }
3273 }
9a2d6cb8 3274 } else if(peer->state == PEER_TTHL) {
3275 handletthl(peer);
d3372da9 3276 }
e0731d8c
FT
3277 if(peer->inbufdata > 500000)
3278 sk->ignread = 1;
d3372da9 3279}
3280
cab0b442 3281static void peererror(struct socket *sk, int err, struct dcpeer *peer)
d3372da9 3282{
3283 freedcpeer(peer);
3284}
3285
cab0b442 3286static void peerconnect(struct socket *sk, int err, struct fnetnode *fn)
d3372da9 3287{
3288 struct dcpeer *peer;
1c4b02cd 3289 struct dchub *hub;
d3372da9 3290
3291 if(err != 0)
3292 {
3293 putfnetnode(fn);
ff625695 3294 putsock(sk);
cab0b442 3295 return;
d3372da9 3296 }
1c4b02cd 3297 hub = fn->data;
d3372da9 3298 peer = newdcpeer(sk);
3299 peer->fn = fn;
3300 peer->accepted = 0;
1c4b02cd 3301 peer->dcppemu = hub->dcppemu;
cab0b442 3302 sk->readcb = (void (*)(struct socket *, void *))peerread;
3303 sk->errcb = (void (*)(struct socket *, int, void *))peererror;
3304 sk->data = peer;
d3372da9 3305 socksettos(sk, confgetint("fnet", "fnptos"));
d3372da9 3306 putsock(sk);
1e9d82b1 3307 peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
9a2d6cb8 3308 sendmynick(peer);
3309 sendpeerlock(peer);
d3372da9 3310}
3311
cab0b442 3312static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
d3372da9 3313{
3314 struct dcpeer *peer;
3315
3316 peer = newdcpeer(newsk);
3317 peer->accepted = 1;
cab0b442 3318 newsk->readcb = (void (*)(struct socket *, void *))peerread;
3319 newsk->errcb = (void (*)(struct socket *, int, void *))peererror;
3320 newsk->data = peer;
d3372da9 3321 socksettos(newsk, confgetint("fnet", "fnptos"));
1e9d82b1 3322 peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
d3372da9 3323}
3324
3325static void updatehmlist(void)
3326{
3327 int i, lev, ic, ret;
3328 struct sharecache *node;
3329 char *buf, *buf2, numbuf[32];
3330 size_t bufsize, bufdata;
3331 int fd, ibuf;
3332
3333 bufdata = 0;
3334 buf = smalloc(bufsize = 65536);
3335 node = shareroot->child;
3336 lev = 0;
3337 while(1)
3338 {
3339 ic = 0;
1d85f24b 3340 /* Use DCCHARSET in $Get paths until further researched... */
d3372da9 3341 if((buf2 = icwcstombs(node->name, DCCHARSET)) != NULL)
3342 {
3343 for(i = 0; i < lev; i++)
3344 addtobuf(buf, 9);
3345 bufcat(buf, buf2, strlen(buf2));
3346 free(buf2);
3347 if(node->f.b.type == FILE_REG)
3348 {
3349 addtobuf(buf, '|');
dcf7a1a2 3350 sprintf(numbuf, "%ji", (intmax_t)node->size);
d3372da9 3351 bufcat(buf, numbuf, strlen(numbuf));
3352 }
3353 addtobuf(buf, 13);
3354 addtobuf(buf, 10);
3355 } else {
3356 ic = 1;
3357 }
3358 if((node->child != NULL) && !ic)
3359 {
3360 lev++;
3361 node = node->child;
3362 } else if(node->next != NULL) {
3363 node = node->next;
3364 } else {
3365 while(node->next == NULL)
3366 {
3367 lev--;
3368 node = node->parent;
3369 if(node == shareroot)
3370 break;
3371 }
3372 if(node == shareroot)
3373 break;
3374 node = node->next;
3375 }
3376 }
3377 if(hmlistname != NULL)
3378 {
3379 unlink(hmlistname);
3380 free(hmlistname);
3381 }
3382 hmlistname = sstrdup("/tmp/dc-filelist-hm-XXXXXX");
3383 if((fd = mkstemp(hmlistname)) < 0)
3384 {
3385 flog(LOG_WARNING, "could not create HM file list tempfile: %s", strerror(errno));
3386 free(hmlistname);
3387 hmlistname = NULL;
3388 } else {
3389 /*
3390 * I do not want to implement a good Huffman encoder, and it's not
3391 * like Huffman encoding actually yields any impressive results
3392 * for DC file lists anyway, so I'll just output a bogus
3393 * tree. Implement a good encoder if you want to.
3394 */
3395 write(fd, "HE3\r\0", 5);
3396 write(fd, &bufdata, 4);
3397 ibuf = 256;
3398 write(fd, &ibuf, 2);
3399 ibuf = 8;
3400 for(i = 0; i < 256; i++)
3401 {
3402 write(fd, &i, 1);
3403 write(fd, &ibuf, 1);
3404 }
3405 for(i = 0; i < 256; i++)
3406 write(fd, &i, 1);
3407 for(buf2 = buf; bufdata > 0;)
3408 {
3409 if((ret = write(fd, buf2, bufdata)) < 0)
3410 {
3411 flog(LOG_WARNING, "could not write file list: %s", strerror(errno));
3412 break;
3413 }
3414 bufdata -= ret;
3415 buf2 += ret;
3416 }
3417 close(fd);
3418 }
3419 free(buf);
3420}
3421
3422static struct xmlent
3423{
3424 wchar_t c;
3425 wchar_t *ent;
3426} entities[] = {
3427 {L'&', L"&amp;"},
3428 {L'\"', L"&#34;"},
3429/* {L'\'', L"&quot;"}, Emulate DC++ escaping by omitting this. */
3430 {L'\0', NULL}
3431};
3432
3433static wchar_t *escapexml(wchar_t *src)
3434{
3435 static wchar_t *buf = NULL;;
3436 int ret, c;
3437 wchar_t nbuf[32];
3438 size_t bufsize, bufdata;
3439 struct xmlent *ent;
3440
3441 if(buf != NULL)
3442 free(buf);
3443 buf = NULL;
3444 bufsize = bufdata = 0;
3445 for(; *src != L'\0'; src++)
3446 {
3447 c = wctob(*src);
3448 if((c > 0) && (c < 32))
3449 {
3450 bufcat(buf, L"&#", 2);
3451 ret = swprintf(nbuf, (sizeof(nbuf) / sizeof(*nbuf)), L"%i", c);
3452 bufcat(buf, nbuf, ret);
3453 addtobuf(buf, L';');
3454 } else {
3455 for(ent = entities; ent->ent != NULL; ent++)
3456 {
3457 if(ent->c == *src)
3458 {
3459 bufcat(buf, ent->ent, wcslen(ent->ent));
3460 break;
3461 }
3462 }
3463 if(ent->ent == NULL)
3464 addtobuf(buf, *src);
3465 }
3466 }
3467 addtobuf(buf, L'\0');
3468 return(buf);
3469}
3470
3471static void updatexmllist(void)
3472{
3473 int i, fd, lev;
3474 FILE *fs;
3475 char cidbuf[14], *namebuf;
3476 char *hashbuf;
3477 struct sharecache *node;
3478
3479 if(xmllistname != NULL)
3480 {
3481 unlink(xmllistname);
3482 free(xmllistname);
3483 }
3484 xmllistname = sstrdup("/tmp/dc-filelist-dcxml-XXXXXX");
3485 if((fd = mkstemp(xmllistname)) < 0)
3486 {
3487 flog(LOG_WARNING, "could not create XML file list tempfile: %s", strerror(errno));
3488 free(xmllistname);
3489 xmllistname = NULL;
3490 return;
3491 }
3492 if((fs = fdopen(fd, "w")) == NULL)
3493 {
3494 flog(LOG_WARNING, "could not fdopen XML list fd %i: %s", fd, strerror(errno));
3495 unlink(xmllistname);
3496 free(xmllistname);
3497 xmllistname = NULL;
3498 close(fd);
3499 return;
3500 }
3501 fprintf(fs, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n");
3502 for(i = 0; i < sizeof(cidbuf) - 1; i++)
3503 cidbuf[i] = (rand() % ('Z' - 'A' + 1)) + 'A';
3504 cidbuf[i] = 0;
1c4b02cd 3505 if(confgetint("dc", "dcppemu"))
3506 fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"DC++ 0.674\">\r\n", cidbuf);
3507 else
3508 fprintf(fs, "<FileListing Version=\"1\" CID=\"%s\" Base=\"/\" Generator=\"%s\">\r\n", cidbuf, "DoldaConnect" VERSION);
d3372da9 3509
3510 node = shareroot->child;
3511 lev = 0;
3512 while(1)
3513 {
3514 if((namebuf = icswcstombs(escapexml(node->name), "UTF-8", NULL)) != NULL)
3515 {
3516 for(i = 0; i < lev; i++)
3517 fputc('\t', fs);
3518 if(node->child != NULL)
3519 {
3520 fprintf(fs, "<Directory Name=\"%s\">\r\n", namebuf);
3521 node = node->child;
3522 lev++;
3523 continue;
3524 } else {
dcf7a1a2 3525 fprintf(fs, "<File Name=\"%s\" Size=\"%ji\"", namebuf, (intmax_t)node->size);
d3372da9 3526 if(node->f.b.hastth)
3527 {
3528 hashbuf = base32encode(node->hashtth, 24);
3529 fprintf(fs, " TTH=\"%.39s\"", hashbuf);
3530 free(hashbuf);
3531 }
3532 fprintf(fs, "/>\r\n");
3533 }
3534 while(node->next == NULL)
3535 {
3536 node = node->parent;
3537 if(node == shareroot)
3538 {
3539 break;
3540 } else {
3541 lev--;
3542 for(i = 0; i < lev; i++)
3543 fputc('\t', fs);
3544 fprintf(fs, "</Directory>\r\n");
3545 }
3546 }
3547 if(node == shareroot)
3548 break;
3549 node = node->next;
3550 }
3551 }
3552
1c4b02cd 3553 if(confgetint("dc", "dcppemu"))
3554 fprintf(fs, "</FileListing>");
3555 else
3556 fprintf(fs, "</FileListing>\r\n");
d3372da9 3557 fclose(fs);
3558}
3559
3560static void updatexmlbz2list(void)
3561{
3562 int err, fd;
3563 FILE *in;
3564 FILE *real;
3565 BZFILE *out;
3566 char buf[1024];
3567 size_t bufdata;
3568
3569 if(xmllistname == NULL)
3570 return;
3571 if((in = fopen(xmllistname, "r")) == NULL)
3572 {
3573 flog(LOG_WARNING, "could not open XML file list for bzipping: %s", strerror(errno));
3574 return;
3575 }
3576 if(xmlbz2listname != NULL)
3577 {
3578 unlink(xmlbz2listname);
3579 free(xmlbz2listname);
3580 }
3581 xmlbz2listname = sstrdup("/tmp/dc-filelist-dcxmlbz2-XXXXXX");
3582 if((fd = mkstemp(xmlbz2listname)) < 0)
3583 {
3584 flog(LOG_WARNING, "could not create bzipped XML file list tempfile: %s", strerror(errno));
3585 free(xmlbz2listname);
3586 xmlbz2listname = NULL;
3587 fclose(in);
3588 return;
3589 }
3590 if((real = fdopen(fd, "w")) == NULL)
3591 {
3592 flog(LOG_WARNING, "could not fdopen bzipped XML list fd %i: %s", fd, strerror(errno));
3593 close(fd);
3594 unlink(xmlbz2listname);
3595 free(xmlbz2listname);
3596 xmlbz2listname = NULL;
3597 fclose(in);
3598 return;
3599 }
3600 out = BZ2_bzWriteOpen(&err, real, 9, 0, 0);
3601 if(err != BZ_OK)
3602 {
3603 flog(LOG_WARNING, "could not open bzip2 stream from XML list");
3604 fclose(real);
3605 unlink(xmlbz2listname);
3606 free(xmlbz2listname);
3607 xmlbz2listname = NULL;
3608 fclose(in);
3609 return;
3610 }
3611 while(!feof(in))
3612 {
3613 bufdata = fread(buf, 1, sizeof(buf), in);
3614 BZ2_bzWrite(&err, out, buf, bufdata);
3615 }
3616 fclose(in);
3617 BZ2_bzWriteClose(&err, out, 0, NULL, NULL);
3618 fclose(real);
3619}
3620
6305c428 3621static void listtimercb(int cancelled, void *uudata)
3622{
3623 listwritetimer = NULL;
3624 if(!cancelled)
3625 updatelists(1);
3626}
3627
3628static void updatelists(int now)
d3372da9 3629{
000ca725 3630 if((hmlistname == NULL) || (xmllistname == NULL) || (xmlbz2listname == NULL))
3631 now = 1;
6305c428 3632 if(!now)
3633 {
3634 if(listwritetimer == NULL)
ddb3658e 3635 listwritetimer = timercallback(ntime() + confgetint("cli", "hashwritedelay"), listtimercb, NULL);
6305c428 3636 return;
3637 }
3638 if(listwritetimer != NULL)
3639 canceltimer(listwritetimer);
d3372da9 3640 updatehmlist();
3641 updatexmllist();
3642 updatexmlbz2list();
6305c428 3643}
3644
3645static int shareupdate(unsigned long long uusharesize, void *data)
3646{
3647 updatelists(0);
d3372da9 3648 return(0);
3649}
3650
7395a37f
FT
3651static char *quotestr(char *str)
3652{
3653 unsigned char *buf;
3654 unsigned char *p;
3655 size_t bufsize, bufdata;
3656 wchar_t *wbuf;
3657 static char *enc = NULL;
3658 size_t encsize, encdata;
3659
3660 buf = NULL;
3661 bufsize = bufdata = 0;
3662 for(p = (unsigned char *)str; *p; p++)
3663 {
3664 if(*p == '\b')
3665 bufcat(buf, "\\b", 2);
3666 else if(*p == '\t')
3667 bufcat(buf, "\\t", 2);
3668 else if(*p == '\n')
3669 bufcat(buf, "\\n", 2);
3670 else if(*p == '\r')
3671 bufcat(buf, "\\r", 2);
3672 else if(*p == '\\')
3673 bufcat(buf, "\\\\", 2);
3674 else if(*p >= 32)
3675 addtobuf(buf, *p);
3676 else
3677 bprintf(buf, "\\x%02x", *p);
3678 }
3679 addtobuf(buf, 0);
3680 if(enc != NULL)
3681 free(enc);
3682 enc = NULL;
3683 if((wbuf = icmbstowcs((char *)buf, DCCHARSET)) != NULL)
3684 {
3685 enc = icwcstombs(wbuf, NULL);
3686 free(wbuf);
3687 }
3688 if(enc == NULL)
3689 {
3690 encsize = encdata = 0;
3691 for(p = buf; *p; p++) {
3692 if(*p < 128)
3693 addtobuf(enc, *p);
3694 else
3695 bprintf(buf, "\\x%x", *p);
3696 }
3697 }
3698 free(buf);
3699 return(enc);
3700}
3701
c1928259 3702static void logunimpl(char *list, char *cmd, char *args)
7395a37f
FT
3703{
3704 FILE *log;
3705
3706 if((log = fopen("/tmp/dc-unimpl", "a")) == NULL)
3707 {
3708 flog(LOG_WARNING, "could not open unimpl log: %s", strerror(errno));
3709 return;
3710 }
c1928259
FT
3711 fputs(list, log);
3712 fputc('\t', log);
7395a37f
FT
3713 fputs(quotestr(cmd), log);
3714 if(args != NULL)
3715 {
3716 fputc('\t', log);
3717 fputs(quotestr(args), log);
3718 }
3719 fputc('\n', log);
3720 fclose(log);
3721}
3722
d3372da9 3723static void dispatchcommand(struct qcommand *qcmd, struct command *cmdlist, struct socket *sk, void *data)
3724{
3725 char *p;
3726 struct command *cmd;
3727
3728 if((p = strchr(qcmd->string, ' ')) != NULL)
3729 *(p++) = 0;
3730 for(cmd = cmdlist; cmd->handler != NULL; cmd++)
3731 {
3732 if(!strcmp(cmd->name, qcmd->string))
3733 break;
3734 }
3735 if(cmd->handler != NULL)
c1928259 3736 {
d3372da9 3737 cmd->handler(sk, data, qcmd->string, p);
c1928259
FT
3738 } else if(confgetint("dc", "logunimpl")) {
3739 if(cmdlist == hubcmds)
3740 logunimpl("hub", qcmd->string, p);
3741 else if(cmdlist == peercmds)
3742 logunimpl("peer", qcmd->string, p);
3743 else
3744 logunimpl("other?!", qcmd->string, p);
3745 }
d3372da9 3746}
3747
3748static int run(void)
3749{
3750 struct fnetnode *fn, *nextfn;
3751 struct dchub *hub;
3752 struct dcpeer *peer, *nextpeer;
3753 struct qcommand *qcmd;
e0731d8c 3754 int ret, quota;
d3372da9 3755
3756 ret = 0;
e0731d8c 3757 quota = 20;
d3372da9 3758 for(fn = fnetnodes; fn != NULL; fn = nextfn)
3759 {
3760 nextfn = fn->next;
3761 if(fn->fnet != &dcnet)
3762 continue;
3763 if(fn->data == NULL)
3764 continue;
3765 hub = (struct dchub *)fn->data;
e0731d8c 3766 while((quota > 0) && ((qcmd = ulqcmd(&hub->queue)) != NULL))
d3372da9 3767 {
3768 if(*qcmd->string == '$')
3769 {
ffa81d5f
FT
3770 if((hub->sk != NULL) && (hub->sk->state == SOCK_EST))
3771 dispatchcommand(qcmd, hubcmds, hub->sk, fn);
d3372da9 3772 } else if(*qcmd->string != 0) {
ffa81d5f 3773 hubrecvchat(hub->sk, fn, NULL, qcmd->string);
d3372da9 3774 }
3775 freeqcmd(qcmd);
3776 ret = 1;
e0731d8c 3777 quota--;
d3372da9 3778 }
e0731d8c
FT
3779 if(hub->queue.size < 1000)
3780 hub->sk->ignread = 0;
3781 if(quota < 1)
3782 break;
d3372da9 3783 }
e0731d8c 3784 quota = 20;
d3372da9 3785 for(peer = peers; peer != NULL; peer = nextpeer)
3786 {
3787 nextpeer = peer->next;
e0731d8c 3788 while((quota > 0) && ((qcmd = ulqcmd(&peer->queue)) != NULL))
d3372da9 3789 {
1e9d82b1 3790 if(peer->timeout != NULL)
3791 canceltimer(peer->timeout);
3792 peer->timeout = timercallback(ntime() + 180, (void (*)(int, void *))peertimeout, peer);
d3372da9 3793 if(*qcmd->string == '$')
3794 dispatchcommand(qcmd, peercmds, peer->sk, peer);
3795 freeqcmd(qcmd);
3796 ret = 1;
e0731d8c 3797 quota--;
d3372da9 3798 }
e0731d8c
FT
3799 if((peer->queue.size < 50) && (peer->inbufdata < 500000))
3800 peer->sk->ignread = 0;
3801 if(quota < 1)
3802 break;
d3372da9 3803 }
3804 return(ret);
3805}
3806
3807static void preinit(int hup)
3808{
3809 if(hup)
3810 return;
3811 regfnet(&dcnet);
3812}
3813
3814static int updateudpport(struct configvar *var, void *uudata)
3815{
3816 struct sockaddr_in addr;
3817 struct socket *newsock;
3818
3819 memset(&addr, 0, sizeof(addr));
3820 addr.sin_family = AF_INET;
3821 addr.sin_port = htons(var->val.num);
3822 if((newsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3823 {
3824 flog(LOG_WARNING, "could not create new DC UDP socket, reverting to old: %s", strerror(errno));
3825 return(0);
3826 }
cab0b442 3827 newsock->readcb = udpread;
d3372da9 3828 if(udpsock != NULL)
3829 putsock(udpsock);
3830 udpsock = newsock;
3831 return(0);
3832}
3833
3834static int updatetcpport(struct configvar *var, void *uudata)
3835{
3836 struct sockaddr_in addr;
3837 struct socket *newsock;
3838
3839 memset(&addr, 0, sizeof(addr));
3840 addr.sin_family = AF_INET;
3841 addr.sin_port = htons(var->val.num);
3842 if((newsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3843 flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3844 if(tcpsock != NULL)
3845 putsock(tcpsock);
3846 tcpsock = newsock;
3847 return(0);
3848}
3849
3850static int init(int hup)
3851{
3852 struct sockaddr_in addr;
3853
3854 if(!hup)
3855 {
3856 GCBREG(sharechangecb, shareupdate, NULL);
3857 if(udpsock != NULL)
3858 putsock(udpsock);
3859 if(tcpsock != NULL)
3860 putsock(tcpsock);
3861 addr.sin_family = AF_INET;
3862 memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
3863 addr.sin_port = htons(confgetint("dc", "udpport"));
3864 if((udpsock = netcsdgram((struct sockaddr *)&addr, sizeof(addr))) == NULL)
3865 {
3866 flog(LOG_CRIT, "could not create DC UDP socket: %s", strerror(errno));
3867 return(1);
3868 }
cab0b442 3869 udpsock->readcb = udpread;
d3372da9 3870 addr.sin_port = htons(confgetint("dc", "tcpport"));
3871 if((tcpsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
3872 flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
3873 CBREG(confgetvar("dc", "udpport"), conf_update, updateudpport, NULL, NULL);
3874 CBREG(confgetvar("dc", "tcpport"), conf_update, updatetcpport, NULL, NULL);
3875 CBREG(confgetvar("net", "mode"), conf_update, updatetcpport, NULL, NULL);
3876 }
3877 return(0);
3878}
3879
3880static void terminate(void)
3881{
3882 if(hmlistname != NULL)
3883 {
3884 unlink(hmlistname);
3885 free(hmlistname);
3886 }
3887 if(xmllistname != NULL)
3888 {
3889 unlink(xmllistname);
3890 free(xmllistname);
3891 }
3892 if(xmlbz2listname != NULL)
3893 {
3894 unlink(xmlbz2listname);
3895 free(xmlbz2listname);
3896 }
3897}
3898
3899static struct configvar myvars[] =
3900{
d9f89ef5 3901 /** Specifies the share description reported to other DC users. */
d3372da9 3902 {CONF_VAR_STRING, "desc", {.str = L""}},
d9f89ef5 3903 /** Specifies the speed reported to other DC users. Normal values
3904 * are 28.8Kbps, 33.6Kbps, 56Kbps, Satellite, ISDN, DSL, Cable,
3905 * LAN(T1) or LAN(T3)*/
d3372da9 3906 {CONF_VAR_STRING, "speedstring", {.str = L"LAN(T1)"}},
d9f89ef5 3907 /** The e-mail address to report to other DC users. */
d3372da9 3908 {CONF_VAR_STRING, "email", {.str = L"spam@spam.org"}},
d9f89ef5 3909 /** Specifies a specific UDP port to use for DC search results. If
3910 * left unspecified, a port is allocated dynamically. Useful for
3911 * NAT routers (see also the net.visibleipv4 address for those
3912 * cases). */
d3372da9 3913 {CONF_VAR_INT, "udpport", {.num = 0}},
d9f89ef5 3914 /** Specifies a specific TCP port to use for DC peer
3915 * connections. If left unspecified, a port is allocated
3916 * dynamically. Useful for NAT routers (see also the
3917 * net.visibleipv4 address for those cases). */
d3372da9 3918 {CONF_VAR_INT, "tcpport", {.num = 0}},
d9f89ef5 3919 /** If set to true, doldacond will do its best to emulate DC++
3920 * (currently v0.674). This should be left off if at all possible,
3921 * since turning it on will violate the rules of most hubs and
3922 * thus give hub owners an actual reason to kick you if it is
3923 * detected. It might be needed for some of the more bone-headed
3924 * hub owners, though. Note that DC++ emulation can also be turned
3925 * on or off for individual hubs, overriding this setting. */
1c4b02cd 3926 {CONF_VAR_BOOL, "dcppemu", {.num = 0}},
7395a37f
FT
3927 /** Use for debugging. If set to true, doldacond will log all
3928 * unknown commands it receives, and their arguments, to
3929 * /tmp/dc-unimpl. */
3930 {CONF_VAR_BOOL, "logunimpl", {.num = 0}},
4b544330
FT
3931 /** If set to true, doldacond will hide its support for deflate
3932 * compression of transfers from other clients, so that they will
3933 * not request compressed uploads. Compressed transfers may
3934 * consume a non-trivial amount of CPU time on slower machines. */
3935 {CONF_VAR_BOOL, "hidedeflate", {.num = 0}},
d3372da9 3936 {CONF_VAR_END}
3937};
3938
3939static struct module me =
3940{
3941 .conf =
3942 {
3943 .vars = myvars
3944 },
3945 .preinit = preinit,
3946 .init = init,
3947 .run = run,
3948 .terminate = terminate,
3949 .name = "dc"
3950};
3951
3952MODULE(me)