Make adcnet a pointer.
[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 qcmd *queue;
71 };
72
73 static wchar_t *eoc;
74 /* I've never understood why both of these are necessary, but... */
75 static wchar_t *privid, *cid;
76
77 static wchar_t **parseadc(wchar_t *cmdline)
78 {
79     wchar_t **ret;
80     size_t retsize, retdata;
81     wchar_t *p, *p2, *ep;
82     wchar_t r;
83     ssize_t len;
84     
85     ret = NULL;
86     retsize = retdata = 0;
87     p = cmdline;
88     do {
89         if((p2 = wcschr(p, L' ')) != NULL)
90             *(p2++) = L'\0';
91         len = wcslen(p);
92         for(ep = p; len > 0; ep++, len--) {
93             if(*ep == L'\\') {
94                 if(ep[1] == L's') {
95                     r = L' ';
96                 } else if(ep[1] == L'n') {
97                     r = L'\n';
98                 } else if(ep[1] == L'\\') {
99                     r = L'\\';
100                 } else {
101                     memmove(ep, ep + 2, (len -= 2) * sizeof(*ep));
102                     ep--;
103                     continue;
104                 }
105                 memmove(ep, ep + 1, --len * sizeof(*ep));
106                 *ep = r;
107             }
108         }
109         *ep = L'\0';
110         addtobuf(ret, swcsdup(p));
111         p = p2;
112     } while(p2 != NULL);
113     addtobuf(ret, NULL);
114     return(ret);
115 }
116
117 static void sendeoc(struct socket *sk)
118 {
119     sockqueue(sk, "\n", 1);
120 }
121
122 static void sendadc1(struct socket *sk, int sep, wchar_t *arg)
123 {
124     char *mbsbuf;
125     wchar_t *buf;
126     size_t bufsize, bufdata;
127     
128     buf = NULL;
129     bufsize = bufdata = 0;
130     if(sep)
131         addtobuf(buf, L' ');
132     for(; *arg != L'\0'; arg++) {
133         if(*arg == L' ')
134             bufcat(buf, L"\\s", 2);
135         else if(*arg == L'\n')
136             bufcat(buf, L"\\n", 2);
137         else if(*arg == L'\\')
138             bufcat(buf, L"\\\\", 2);
139         else
140             addtobuf(buf, *arg);
141     }
142     addtobuf(buf, L'\0');
143     mbsbuf = icwcstombs(buf, "utf-8");
144     sockqueue(sk, mbsbuf, strlen(mbsbuf));
145     free(mbsbuf);
146 }
147
148 static void sendadc(struct socket *sk, int sep, ...)
149 {
150     va_list args;
151     wchar_t *arg;
152     int i;
153     
154     va_start(args, sep);
155     for(i = 0; (arg = va_arg(args, wchar_t *)) != NULL; i++) {
156         if(arg == eoc)
157             sendeoc(sk);
158         else
159             sendadc1(sk, (i == 0)?sep:1, arg);
160     }
161     va_end(args);
162 }
163
164 static struct qcmd *newqcmd(struct qcmd **queue, wchar_t **args)
165 {
166     struct qcmd *new;
167     
168     while(*queue != NULL)
169         queue = &((*queue)->next);
170     new = smalloc(sizeof(*new));
171     new->next = NULL;
172     new->args = args;
173     *queue = new;
174     return(new);
175 }
176
177 static struct qcmd *ulqcmd(struct qcmd **queue)
178 {
179     struct qcmd *ret;
180     
181     if((ret = *queue) == NULL)
182         return(NULL);
183     *queue = ret->next;
184     return(ret);
185 }
186
187 static void freeqcmd(struct qcmd *qcmd)
188 {
189     freeparr(qcmd->args);
190     free(qcmd);
191 }
192
193 #define ADC_CMDFN(name) static void name(struct fnetnode *fn, wchar_t *command, wchar_t *sender, int argc, wchar_t **argv)
194 #define ADC_CMDCOM struct socket *sk = fn->sk; struct adchub *hub = fn->data;
195
196 ADC_CMDFN(cmd_sup)
197 {
198     ADC_CMDCOM;
199     int i, o, f;
200     
201     for(i = 1; i < argc; i++) {
202         if(wcslen(argv[i]) < 3)
203             continue;
204         for(o = 0, f = 0; hub->sup[o]; o++) {
205             if(!wcscmp(argv[i] + 2, hub->sup[o])) {
206                 f = 1;
207                 break;
208             }
209         }
210         if(!wcsncmp(argv[i], L"AD", 2)) {
211             if(f)
212                 continue;
213             hub->sup = srealloc(hub->sup, sizeof(*(hub->sup)) * (o + 1));
214             hub->sup[o] = swcsdup(argv[i] + 2);
215         } else if(!wcsncmp(argv[i], L"RM", 2)) {
216             if(!f)
217                 continue;
218             memmove(hub->sup[o], hub->sup[o + 1], parrlen(hub->sup) - o);
219         }
220     }
221 }
222
223 ADC_CMDFN(cmd_sid)
224 {
225     ADC_CMDCOM;
226     
227     if(hub->sid != NULL)
228         free(hub->sid);
229     hub->sid = swcsdup(argv[1]);
230     if(hub->state == ADC_PROTOCOL) {
231         hub->state = ADC_IDENTIFY;
232     }
233 }
234
235 static struct command hubcmds[] = {
236     {L"SUP", 1, 0, cmd_sup},
237     {L"SID", 2, 0, cmd_sid},
238     {NULL, 0, 0, NULL}
239 };
240
241 static void dispatch(struct qcmd *qcmd, struct fnetnode *fn)
242 {
243     struct command *cmd;
244     int argc;
245     wchar_t *cmdnm, *sender, **argv;
246     
247     if((argc = parrlen(argv = qcmd->args)) < 1)
248         return;
249     cmdnm = *(argv++);
250     argc--;
251     if(wcslen(cmdnm) < 2)
252         return;
253     sender = NULL;
254     if((*cmdnm == L'B') || (*cmdnm == L'F')) {
255         if(argc < 1)
256             return;
257         sender = *(argv++);
258         argc--;
259     } else if((*cmdnm == L'D') || (*cmdnm == L'E')) {
260         if(argc < 2)
261             return;
262         sender = *argv;
263         argv += 2;
264         argc -= 2;
265     }
266     for(cmd = hubcmds; cmd->func != NULL; cmd++) {
267         if(!wcscmp(cmd->name, qcmd->args[0] + 1)) {
268             if(argc < cmd->minargs)
269                 return;
270             cmd->func(fn, cmdnm, sender, argc, qcmd->args);
271             return;
272         }
273     }
274     flog(LOG_DEBUG, "unknown adc command: %ls", qcmd->args[0]);
275 }
276
277 static void hubread(struct socket *sk, struct fnetnode *fn)
278 {
279     int ret;
280     struct adchub *hub;
281     char *newbuf, *p1, *p2;
282     wchar_t *p;
283     size_t datalen, cblen;
284     
285     hub = fn->data;
286     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
287         return;
288     if(hub->inbufdata > 1024)
289         hub->inbufdata = 0;
290     bufcat(hub->inbuf, newbuf, datalen);
291     free(newbuf);
292     /* Memory eating protection */
293     if(hub->cbdata > 65536)
294         hub->cbdata = 0;
295     while(hub->inbufdata > 0) {
296         if(hub->cbdata == hub->cbsize)
297             sizebuf2(hub->cb, hub->cbdata + datalen, 1);
298         p1 = hub->inbuf;
299         p2 = (char *)(hub->cb + hub->cbdata);
300         cblen = sizeof(*(hub->cb)) * (hub->cbsize - hub->cbdata);
301         ret = iconv(hub->ich, &p1, &hub->inbufdata, &p2, &cblen);
302         memmove(hub->inbuf, p1, hub->inbufdata);
303         if(((p2 - ((char *)hub->cb)) % sizeof(*hub->cb)) != 0) {
304             flog(LOG_CRIT, "Spotted a dismembered wchar_t!");
305             abort();
306         }
307         hub->cbdata = hub->cbsize - (cblen / sizeof(*(hub->cb)));
308         if(ret < 0) {
309             if(errno == EILSEQ) {
310                 flog(LOG_DEBUG, "adc fnetnode %i sent illegal utf-8 sequence", fn->id);
311                 killfnetnode(fn);
312                 return;
313             } else if(errno == EINVAL) {
314                 break;
315             } else if(errno == E2BIG) {
316                 /* continue; */
317             } else {
318                 flog(LOG_WARNING, "bug? iconv returned unexpected error: %s", strerror(errno));
319                 return;
320             }
321         }
322     }
323     while((p = wmemchr(hub->cb, L'\n', hub->cbdata)) != NULL) {
324         *(p++) = L'\0';
325         newqcmd(&hub->queue, parseadc(hub->cb));
326         memmove(hub->cb, p, (hub->cbdata -= (p - hub->cb)) * sizeof(*(hub->cb)));
327     }
328 }
329
330 static void huberr(struct socket *sk, int err, struct fnetnode *fn)
331 {
332     killfnetnode(fn);
333 }
334
335 static void hubconnect(struct fnetnode *fn)
336 {
337     struct adchub *hub;
338     
339     fn->sk->readcb = (void (*)(struct socket *, void *))hubread;
340     fn->sk->errcb = (void (*)(struct socket *, int, void *))huberr;
341     fn->sk->data = fn;
342     getfnetnode(fn);
343     
344     hub = smalloc(sizeof(*hub));
345     memset(hub, 0, sizeof(*hub));
346     if((hub->ich = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1) {
347         flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
348         killfnetnode(fn);
349         return;
350     }
351     fn->data = hub;
352     sendadc(fn->sk, 0, L"HSUP", L"ADBASE", eoc, NULL);
353 }
354
355 static void hubdestroy(struct fnetnode *fn)
356 {
357     struct adchub *hub;
358     
359     if((hub = fn->data) == NULL)
360         return;
361     iconv_close(hub->ich);
362     if(hub->inbuf != NULL)
363         free(hub->inbuf);
364     if(hub->sup != NULL)
365         freeparr(hub->sup);
366     free(hub);
367 }
368
369 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
370 {
371     return(0);
372 }
373
374 static int hubreqconn(struct fnetpeer *peer)
375 {
376     return(0);
377 }
378
379 static struct fnet adcnet_store = {
380     .connect = hubconnect,
381     .destroy = hubdestroy,
382     .setnick = hubsetnick,
383     .reqconn = hubreqconn,
384     .name = L"adc"
385 };
386
387 static struct fnet *adcnet = &adcnet_store;
388
389 static int run(void)
390 {
391     int ret;
392     struct fnetnode *fn, *nextfn;
393     struct adchub *hub;
394     struct qcmd *qcmd;
395     
396     ret = 0;
397     for(fn = fnetnodes; fn != NULL; fn = nextfn) {
398         nextfn = fn->next;
399         if(fn->fnet != adcnet)
400             continue;
401         if((hub = fn->data) == NULL)
402             continue;
403         if((qcmd = ulqcmd(&hub->queue)) != NULL) {
404             if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
405                 dispatch(qcmd, fn);
406             freeqcmd(qcmd);
407             ret = 1;
408             break;
409         }
410     }
411     return(ret);
412 }
413
414 static void preinit(int hup)
415 {
416     if(hup)
417         return;
418     regfnet(adcnet);
419 }
420
421 static int init(int hup)
422 {
423     int i;
424     char idbuf[24], *id32;
425     struct tigerhash th;
426     
427     if(!hup) {
428         eoc = swcsdup(L"");
429         
430         if((privid = fetchvar("adc.pid", NULL)) == NULL) {
431             for(i = 0; i < sizeof(idbuf); i++)
432                 idbuf[i] = rand() % 256;
433             id32 = base32encode(idbuf, sizeof(idbuf));
434             id32[39] = 0;
435             privid = icmbstowcs(id32, "us-ascii");
436             free(id32);
437             storevar("adc.pid", privid, sizeof(*privid) * (wcslen(privid) + 1));
438         }
439         
440         id32 = base32decode(icswcstombs(privid, "us-ascii", NULL), NULL);
441         inittiger(&th);
442         dotiger(&th, id32, 24);
443         synctiger(&th);
444         free(id32);
445         restiger(&th, idbuf);
446         id32 = base32encode(idbuf, sizeof(idbuf));
447         id32[39] = 0;
448         cid = icmbstowcs(id32, "us-ascii");
449         free(id32);
450     }
451     return(0);
452 }
453
454 static void terminate(void)
455 {
456     
457 }
458
459 static struct configvar myvars[] = {
460     {CONF_VAR_INT, "udpport", {.num = 0}},
461     {CONF_VAR_INT, "tcpport", {.num = 0}},
462     {CONF_VAR_END}
463 };
464
465 static struct module me = {
466     .conf = {
467         .vars = myvars
468     },
469     .preinit = preinit,
470     .init = init,
471     .run = run,
472     .terminate = terminate,
473     .name = "adc"
474 };
475
476 MODULE(me)