Try to hide join/leave messages.
[doldaconnect.git] / daemon / fnet-adc.c
CommitLineData
58e83d6a 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19#include <stdlib.h>
20#include <stdio.h>
21#include <unistd.h>
22#include <stdarg.h>
23#include <string.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <wctype.h>
27#include <iconv.h>
28#include <errno.h>
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33#include "filenet.h"
34#include "log.h"
35#include "module.h"
36#include "utils.h"
37#include "client.h"
38#include "transfer.h"
39#include "sysevents.h"
40#include "net.h"
24a794c2 41#include <tiger.h>
58e83d6a 42
43/* Protocol states */
44#define ADC_PROTOCOL 0
45#define ADC_IDENTIFY 1
46#define ADC_VERIFY 2
47#define ADC_NORMAL 3
48#define ADC_DATA 4
49
50struct command {
51 wchar_t *name;
52 int minargs, needsender;
53 void (*func)(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv);
54};
55
56struct qcmd {
57 struct qcmd *next;
58 wchar_t **args;
59};
60
61struct adchub {
62 char *inbuf;
63 size_t inbufdata, inbufsize;
64 wchar_t *sid;
65 wchar_t *cb;
66 size_t cbdata, cbsize;
67 wchar_t **sup;
68 iconv_t ich;
69 int state;
24a794c2 70 struct wcspair *hubinf;
58e83d6a 71 struct qcmd *queue;
72};
73
74static wchar_t *eoc;
75/* I've never understood why both of these are necessary, but... */
76static wchar_t *privid, *cid;
77
78static wchar_t **parseadc(wchar_t *cmdline)
79{
80 wchar_t **ret;
81 size_t retsize, retdata;
82 wchar_t *p, *p2, *ep;
83 wchar_t r;
84 ssize_t len;
85
86 ret = NULL;
87 retsize = retdata = 0;
88 p = cmdline;
89 do {
90 if((p2 = wcschr(p, L' ')) != NULL)
91 *(p2++) = L'\0';
92 len = wcslen(p);
93 for(ep = p; len > 0; ep++, len--) {
94 if(*ep == L'\\') {
95 if(ep[1] == L's') {
96 r = L' ';
97 } else if(ep[1] == L'n') {
98 r = L'\n';
99 } else if(ep[1] == L'\\') {
100 r = L'\\';
101 } else {
102 memmove(ep, ep + 2, (len -= 2) * sizeof(*ep));
103 ep--;
104 continue;
105 }
106 memmove(ep, ep + 1, --len * sizeof(*ep));
107 *ep = r;
108 }
109 }
110 *ep = L'\0';
111 addtobuf(ret, swcsdup(p));
112 p = p2;
113 } while(p2 != NULL);
114 addtobuf(ret, NULL);
115 return(ret);
116}
117
118static void sendeoc(struct socket *sk)
119{
120 sockqueue(sk, "\n", 1);
121}
122
123static void sendadc1(struct socket *sk, int sep, wchar_t *arg)
124{
125 char *mbsbuf;
126 wchar_t *buf;
127 size_t bufsize, bufdata;
128
129 buf = NULL;
130 bufsize = bufdata = 0;
131 if(sep)
132 addtobuf(buf, L' ');
133 for(; *arg != L'\0'; arg++) {
134 if(*arg == L' ')
135 bufcat(buf, L"\\s", 2);
136 else if(*arg == L'\n')
137 bufcat(buf, L"\\n", 2);
138 else if(*arg == L'\\')
139 bufcat(buf, L"\\\\", 2);
140 else
141 addtobuf(buf, *arg);
142 }
143 addtobuf(buf, L'\0');
144 mbsbuf = icwcstombs(buf, "utf-8");
145 sockqueue(sk, mbsbuf, strlen(mbsbuf));
146 free(mbsbuf);
147}
148
149static void sendadc(struct socket *sk, int sep, ...)
150{
151 va_list args;
152 wchar_t *arg;
153 int i;
154
155 va_start(args, sep);
156 for(i = 0; (arg = va_arg(args, wchar_t *)) != NULL; i++) {
157 if(arg == eoc)
158 sendeoc(sk);
159 else
160 sendadc1(sk, (i == 0)?sep:1, arg);
161 }
162 va_end(args);
163}
164
165static struct qcmd *newqcmd(struct qcmd **queue, wchar_t **args)
166{
167 struct qcmd *new;
168
169 while(*queue != NULL)
170 queue = &((*queue)->next);
171 new = smalloc(sizeof(*new));
172 new->next = NULL;
173 new->args = args;
174 *queue = new;
175 return(new);
176}
177
178static struct qcmd *ulqcmd(struct qcmd **queue)
179{
180 struct qcmd *ret;
181
182 if((ret = *queue) == NULL)
183 return(NULL);
184 *queue = ret->next;
185 return(ret);
186}
187
188static void freeqcmd(struct qcmd *qcmd)
189{
190 freeparr(qcmd->args);
191 free(qcmd);
192}
193
194#define ADC_CMDFN(name) static void name(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv)
0d6c5363 195#ifdef __GNUC__
9db779f4 196#define UNUSED __attribute__ ((unused))
0d6c5363 197#else
9db779f4 198#define UNUSED
0d6c5363 199#endif
9db779f4 200#define ADC_CMDCOM \
201 struct socket *sk UNUSED = fn->sk; \
202 struct adchub *hub UNUSED = fn->data;
58e83d6a 203
204ADC_CMDFN(cmd_sup)
205{
206 ADC_CMDCOM;
207 int i, o, f;
208
209 for(i = 1; i < argc; i++) {
210 if(wcslen(argv[i]) < 3)
211 continue;
212 for(o = 0, f = 0; hub->sup[o]; o++) {
213 if(!wcscmp(argv[i] + 2, hub->sup[o])) {
214 f = 1;
215 break;
216 }
217 }
218 if(!wcsncmp(argv[i], L"AD", 2)) {
219 if(f)
220 continue;
221 hub->sup = srealloc(hub->sup, sizeof(*(hub->sup)) * (o + 1));
222 hub->sup[o] = swcsdup(argv[i] + 2);
223 } else if(!wcsncmp(argv[i], L"RM", 2)) {
224 if(!f)
225 continue;
24a794c2 226 free(hub->sup[o]);
58e83d6a 227 memmove(hub->sup[o], hub->sup[o + 1], parrlen(hub->sup) - o);
228 }
229 }
230}
231
232ADC_CMDFN(cmd_sid)
233{
234 ADC_CMDCOM;
235
236 if(hub->sid != NULL)
237 free(hub->sid);
238 hub->sid = swcsdup(argv[1]);
239 if(hub->state == ADC_PROTOCOL) {
240 hub->state = ADC_IDENTIFY;
241 }
242}
243
24a794c2 244ADC_CMDFN(cmd_inf)
245{
246 ADC_CMDCOM;
247
248 if(sender == NULL) {
249
250 }
251}
252
58e83d6a 253static struct command hubcmds[] = {
254 {L"SUP", 1, 0, cmd_sup},
255 {L"SID", 2, 0, cmd_sid},
24a794c2 256 {L"INF", 0, 0, cmd_inf},
58e83d6a 257 {NULL, 0, 0, NULL}
258};
259
260static void dispatch(struct qcmd *qcmd, struct fnetnode *fn)
261{
262 struct command *cmd;
263 int argc;
264 wchar_t *cmdnm, *sender, **argv;
265
266 if((argc = parrlen(argv = qcmd->args)) < 1)
267 return;
268 cmdnm = *(argv++);
269 argc--;
270 if(wcslen(cmdnm) < 2)
271 return;
272 sender = NULL;
273 if((*cmdnm == L'B') || (*cmdnm == L'F')) {
274 if(argc < 1)
275 return;
276 sender = *(argv++);
277 argc--;
278 } else if((*cmdnm == L'D') || (*cmdnm == L'E')) {
279 if(argc < 2)
280 return;
281 sender = *argv;
282 argv += 2;
283 argc -= 2;
284 }
285 for(cmd = hubcmds; cmd->func != NULL; cmd++) {
286 if(!wcscmp(cmd->name, qcmd->args[0] + 1)) {
287 if(argc < cmd->minargs)
288 return;
289 cmd->func(fn, cmdnm, sender, argc, qcmd->args);
290 return;
291 }
292 }
293 flog(LOG_DEBUG, "unknown adc command: %ls", qcmd->args[0]);
294}
295
cab0b442 296static void hubread(struct socket *sk, struct fnetnode *fn)
58e83d6a 297{
298 int ret;
299 struct adchub *hub;
300 char *newbuf, *p1, *p2;
301 wchar_t *p;
302 size_t datalen, cblen;
303
304 hub = fn->data;
305 if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
cab0b442 306 return;
58e83d6a 307 if(hub->inbufdata > 1024)
308 hub->inbufdata = 0;
309 bufcat(hub->inbuf, newbuf, datalen);
310 free(newbuf);
311 /* Memory eating protection */
312 if(hub->cbdata > 65536)
313 hub->cbdata = 0;
314 while(hub->inbufdata > 0) {
315 if(hub->cbdata == hub->cbsize)
316 sizebuf2(hub->cb, hub->cbdata + datalen, 1);
317 p1 = hub->inbuf;
318 p2 = (char *)(hub->cb + hub->cbdata);
319 cblen = sizeof(*(hub->cb)) * (hub->cbsize - hub->cbdata);
320 ret = iconv(hub->ich, &p1, &hub->inbufdata, &p2, &cblen);
321 memmove(hub->inbuf, p1, hub->inbufdata);
322 if(((p2 - ((char *)hub->cb)) % sizeof(*hub->cb)) != 0) {
323 flog(LOG_CRIT, "Spotted a dismembered wchar_t!");
324 abort();
325 }
326 hub->cbdata = hub->cbsize - (cblen / sizeof(*(hub->cb)));
327 if(ret < 0) {
328 if(errno == EILSEQ) {
329 flog(LOG_DEBUG, "adc fnetnode %i sent illegal utf-8 sequence", fn->id);
330 killfnetnode(fn);
cab0b442 331 return;
58e83d6a 332 } else if(errno == EINVAL) {
333 break;
334 } else if(errno == E2BIG) {
335 /* continue; */
336 } else {
337 flog(LOG_WARNING, "bug? iconv returned unexpected error: %s", strerror(errno));
cab0b442 338 return;
58e83d6a 339 }
340 }
341 }
342 while((p = wmemchr(hub->cb, L'\n', hub->cbdata)) != NULL) {
343 *(p++) = L'\0';
344 newqcmd(&hub->queue, parseadc(hub->cb));
345 memmove(hub->cb, p, (hub->cbdata -= (p - hub->cb)) * sizeof(*(hub->cb)));
346 }
347}
348
cab0b442 349static void huberr(struct socket *sk, int err, struct fnetnode *fn)
58e83d6a 350{
351 killfnetnode(fn);
352}
353
354static void hubconnect(struct fnetnode *fn)
355{
356 struct adchub *hub;
357
cab0b442 358 fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
359 fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
360 fn->sk->data = fn;
361 getfnetnode(fn);
58e83d6a 362
363 hub = smalloc(sizeof(*hub));
364 memset(hub, 0, sizeof(*hub));
365 if((hub->ich = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1) {
366 flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
367 killfnetnode(fn);
368 return;
369 }
370 fn->data = hub;
371 sendadc(fn->sk, 0, L"HSUP", L"ADBASE", eoc, NULL);
372}
373
374static void hubdestroy(struct fnetnode *fn)
375{
376 struct adchub *hub;
377
378 if((hub = fn->data) == NULL)
379 return;
380 iconv_close(hub->ich);
381 if(hub->inbuf != NULL)
382 free(hub->inbuf);
383 if(hub->sup != NULL)
384 freeparr(hub->sup);
385 free(hub);
386}
387
388static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
389{
390 return(0);
391}
392
393static int hubreqconn(struct fnetpeer *peer)
394{
395 return(0);
396}
397
3ad44ea7 398static struct fnet adcnet_store = {
58e83d6a 399 .connect = hubconnect,
400 .destroy = hubdestroy,
401 .setnick = hubsetnick,
402 .reqconn = hubreqconn,
403 .name = L"adc"
404};
405
3ad44ea7 406static struct fnet *adcnet = &adcnet_store;
407
58e83d6a 408static int run(void)
409{
410 int ret;
411 struct fnetnode *fn, *nextfn;
412 struct adchub *hub;
413 struct qcmd *qcmd;
414
415 ret = 0;
416 for(fn = fnetnodes; fn != NULL; fn = nextfn) {
417 nextfn = fn->next;
3ad44ea7 418 if(fn->fnet != adcnet)
58e83d6a 419 continue;
420 if((hub = fn->data) == NULL)
421 continue;
422 if((qcmd = ulqcmd(&hub->queue)) != NULL) {
423 if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
424 dispatch(qcmd, fn);
425 freeqcmd(qcmd);
426 ret = 1;
427 break;
428 }
429 }
430 return(ret);
431}
432
433static void preinit(int hup)
434{
435 if(hup)
436 return;
3ad44ea7 437 regfnet(adcnet);
58e83d6a 438}
439
440static int init(int hup)
441{
442 int i;
443 char idbuf[24], *id32;
444 struct tigerhash th;
445
446 if(!hup) {
447 eoc = swcsdup(L"");
448
449 if((privid = fetchvar("adc.pid", NULL)) == NULL) {
450 for(i = 0; i < sizeof(idbuf); i++)
451 idbuf[i] = rand() % 256;
452 id32 = base32encode(idbuf, sizeof(idbuf));
453 id32[39] = 0;
454 privid = icmbstowcs(id32, "us-ascii");
455 free(id32);
456 storevar("adc.pid", privid, sizeof(*privid) * (wcslen(privid) + 1));
457 }
458
459 id32 = base32decode(icswcstombs(privid, "us-ascii", NULL), NULL);
460 inittiger(&th);
461 dotiger(&th, id32, 24);
462 synctiger(&th);
463 free(id32);
464 restiger(&th, idbuf);
465 id32 = base32encode(idbuf, sizeof(idbuf));
466 id32[39] = 0;
467 cid = icmbstowcs(id32, "us-ascii");
468 free(id32);
469 }
470 return(0);
471}
472
473static void terminate(void)
474{
475
476}
477
478static struct configvar myvars[] = {
d9f89ef5 479 /** Specifies a specific UDP port to use for ADC search
480 * results. If left unspecified, a port is allocated
481 * dynamically. Useful for NAT routers (see also the
482 * net.visibleipv4 address for those cases). */
58e83d6a 483 {CONF_VAR_INT, "udpport", {.num = 0}},
d9f89ef5 484 /** Specifies a specific TCP port to use for ADC peer
485 * connections. If left unspecified, a port is allocated
486 * dynamically. Useful for NAT routers (see also the
487 * net.visibleipv4 address for those cases). */
58e83d6a 488 {CONF_VAR_INT, "tcpport", {.num = 0}},
489 {CONF_VAR_END}
490};
491
492static struct module me = {
493 .conf = {
494 .vars = myvars
495 },
496 .preinit = preinit,
497 .init = init,
498 .run = run,
499 .terminate = terminate,
500 .name = "adc"
501};
502
503MODULE(me)