Shut up unless told otherwise.
[doldaconnect.git] / daemon / fnet-adc.c
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"
41 #include <tiger.h>
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
50 struct 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
56 struct qcmd {
57     struct qcmd *next;
58     wchar_t **args;
59 };
60
61 struct 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;
70     struct wcspair *hubinf;
71     struct qcmd *queue;
72 };
73
74 static wchar_t *eoc;
75 /* I've never understood why both of these are necessary, but... */
76 static wchar_t *privid, *cid;
77
78 static 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
118 static void sendeoc(struct socket *sk)
119 {
120     sockqueue(sk, "\n", 1);
121 }
122
123 static 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
149 static 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
165 static 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
178 static 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
188 static 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)
195 #ifdef __GNUC__
196 #define UNUSED __attribute__ ((unused))
197 #else
198 #define UNUSED
199 #endif
200 #define ADC_CMDCOM \
201         struct socket *sk UNUSED = fn->sk; \
202         struct adchub *hub UNUSED = fn->data;
203
204 ADC_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;
226             free(hub->sup[o]);
227             memmove(hub->sup[o], hub->sup[o + 1], parrlen(hub->sup) - o);
228         }
229     }
230 }
231
232 ADC_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
244 ADC_CMDFN(cmd_inf)
245 {
246     ADC_CMDCOM;
247     
248     if(sender == NULL) {
249         
250     }
251 }
252
253 static struct command hubcmds[] = {
254     {L"SUP", 1, 0, cmd_sup},
255     {L"SID", 2, 0, cmd_sid},
256     {L"INF", 0, 0, cmd_inf},
257     {NULL, 0, 0, NULL}
258 };
259
260 static 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
296 static void hubread(struct socket *sk, struct fnetnode *fn)
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)
306         return;
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);
331                 return;
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));
338                 return;
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
349 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
350 {
351     killfnetnode(fn);
352 }
353
354 static void hubconnect(struct fnetnode *fn)
355 {
356     struct adchub *hub;
357     
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);
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
374 static 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
388 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
389 {
390     return(0);
391 }
392
393 static int hubreqconn(struct fnetpeer *peer)
394 {
395     return(0);
396 }
397
398 static struct fnet adcnet_store = {
399     .connect = hubconnect,
400     .destroy = hubdestroy,
401     .setnick = hubsetnick,
402     .reqconn = hubreqconn,
403     .name = L"adc"
404 };
405
406 static struct fnet *adcnet = &adcnet_store;
407
408 static 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;
418         if(fn->fnet != adcnet)
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
433 static void preinit(int hup)
434 {
435     if(hup)
436         return;
437     regfnet(adcnet);
438 }
439
440 static 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
473 static void terminate(void)
474 {
475     
476 }
477
478 static struct configvar myvars[] = {
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). */
483     {CONF_VAR_INT, "udpport", {.num = 0}},
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). */
488     {CONF_VAR_INT, "tcpport", {.num = 0}},
489     {CONF_VAR_END}
490 };
491
492 static 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
503 MODULE(me)