Major rework to use cbchains on sockets.
[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 #define ADC_CMDCOM struct socket *sk = fn->sk; struct adchub *hub = fn->data;
196
197 ADC_CMDFN(cmd_sup)
198 {
199     ADC_CMDCOM;
200     int i, o, f;
201     
202     for(i = 1; i < argc; i++) {
203         if(wcslen(argv[i]) < 3)
204             continue;
205         for(o = 0, f = 0; hub->sup[o]; o++) {
206             if(!wcscmp(argv[i] + 2, hub->sup[o])) {
207                 f = 1;
208                 break;
209             }
210         }
211         if(!wcsncmp(argv[i], L"AD", 2)) {
212             if(f)
213                 continue;
214             hub->sup = srealloc(hub->sup, sizeof(*(hub->sup)) * (o + 1));
215             hub->sup[o] = swcsdup(argv[i] + 2);
216         } else if(!wcsncmp(argv[i], L"RM", 2)) {
217             if(!f)
218                 continue;
219             free(hub->sup[o]);
220             memmove(hub->sup[o], hub->sup[o + 1], parrlen(hub->sup) - o);
221         }
222     }
223 }
224
225 ADC_CMDFN(cmd_sid)
226 {
227     ADC_CMDCOM;
228     
229     if(hub->sid != NULL)
230         free(hub->sid);
231     hub->sid = swcsdup(argv[1]);
232     if(hub->state == ADC_PROTOCOL) {
233         hub->state = ADC_IDENTIFY;
234     }
235 }
236
237 ADC_CMDFN(cmd_inf)
238 {
239     ADC_CMDCOM;
240     
241     if(sender == NULL) {
242         
243     }
244 }
245
246 static struct command hubcmds[] = {
247     {L"SUP", 1, 0, cmd_sup},
248     {L"SID", 2, 0, cmd_sid},
249     {L"INF", 0, 0, cmd_inf},
250     {NULL, 0, 0, NULL}
251 };
252
253 static void dispatch(struct qcmd *qcmd, struct fnetnode *fn)
254 {
255     struct command *cmd;
256     int argc;
257     wchar_t *cmdnm, *sender, **argv;
258     
259     if((argc = parrlen(argv = qcmd->args)) < 1)
260         return;
261     cmdnm = *(argv++);
262     argc--;
263     if(wcslen(cmdnm) < 2)
264         return;
265     sender = NULL;
266     if((*cmdnm == L'B') || (*cmdnm == L'F')) {
267         if(argc < 1)
268             return;
269         sender = *(argv++);
270         argc--;
271     } else if((*cmdnm == L'D') || (*cmdnm == L'E')) {
272         if(argc < 2)
273             return;
274         sender = *argv;
275         argv += 2;
276         argc -= 2;
277     }
278     for(cmd = hubcmds; cmd->func != NULL; cmd++) {
279         if(!wcscmp(cmd->name, qcmd->args[0] + 1)) {
280             if(argc < cmd->minargs)
281                 return;
282             cmd->func(fn, cmdnm, sender, argc, qcmd->args);
283             return;
284         }
285     }
286     flog(LOG_DEBUG, "unknown adc command: %ls", qcmd->args[0]);
287 }
288
289 static int hubread(struct socket *sk, struct fnetnode *fn)
290 {
291     int ret;
292     struct adchub *hub;
293     char *newbuf, *p1, *p2;
294     wchar_t *p;
295     size_t datalen, cblen;
296     
297     hub = fn->data;
298     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
299         return(0);
300     if(hub->inbufdata > 1024)
301         hub->inbufdata = 0;
302     bufcat(hub->inbuf, newbuf, datalen);
303     free(newbuf);
304     /* Memory eating protection */
305     if(hub->cbdata > 65536)
306         hub->cbdata = 0;
307     while(hub->inbufdata > 0) {
308         if(hub->cbdata == hub->cbsize)
309             sizebuf2(hub->cb, hub->cbdata + datalen, 1);
310         p1 = hub->inbuf;
311         p2 = (char *)(hub->cb + hub->cbdata);
312         cblen = sizeof(*(hub->cb)) * (hub->cbsize - hub->cbdata);
313         ret = iconv(hub->ich, &p1, &hub->inbufdata, &p2, &cblen);
314         memmove(hub->inbuf, p1, hub->inbufdata);
315         if(((p2 - ((char *)hub->cb)) % sizeof(*hub->cb)) != 0) {
316             flog(LOG_CRIT, "Spotted a dismembered wchar_t!");
317             abort();
318         }
319         hub->cbdata = hub->cbsize - (cblen / sizeof(*(hub->cb)));
320         if(ret < 0) {
321             if(errno == EILSEQ) {
322                 flog(LOG_DEBUG, "adc fnetnode %i sent illegal utf-8 sequence", fn->id);
323                 killfnetnode(fn);
324                 return(0);
325             } else if(errno == EINVAL) {
326                 break;
327             } else if(errno == E2BIG) {
328                 /* continue; */
329             } else {
330                 flog(LOG_WARNING, "bug? iconv returned unexpected error: %s", strerror(errno));
331                 return(0);
332             }
333         }
334     }
335     while((p = wmemchr(hub->cb, L'\n', hub->cbdata)) != NULL) {
336         *(p++) = L'\0';
337         newqcmd(&hub->queue, parseadc(hub->cb));
338         memmove(hub->cb, p, (hub->cbdata -= (p - hub->cb)) * sizeof(*(hub->cb)));
339     }
340     return(0);
341 }
342
343 static int huberr(struct socket *sk, int err, struct fnetnode *fn)
344 {
345     killfnetnode(fn);
346     return(0);
347 }
348
349 static void hubconnect(struct fnetnode *fn)
350 {
351     struct adchub *hub;
352     
353     CBREG(fn->sk, socket_read, (int (*)(struct socket *, void *))hubread, (void (*)(void *))putfnetnode, fn);
354     CBREG(fn->sk, socket_err, (int (*)(struct socket *, int, void *))huberr, (void (*)(void *))putfnetnode, fn);
355     
356     hub = smalloc(sizeof(*hub));
357     memset(hub, 0, sizeof(*hub));
358     if((hub->ich = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1) {
359         flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
360         killfnetnode(fn);
361         return;
362     }
363     fn->data = hub;
364     sendadc(fn->sk, 0, L"HSUP", L"ADBASE", eoc, NULL);
365 }
366
367 static void hubdestroy(struct fnetnode *fn)
368 {
369     struct adchub *hub;
370     
371     if((hub = fn->data) == NULL)
372         return;
373     iconv_close(hub->ich);
374     if(hub->inbuf != NULL)
375         free(hub->inbuf);
376     if(hub->sup != NULL)
377         freeparr(hub->sup);
378     free(hub);
379 }
380
381 static int hubsetnick(struct fnetnode *fn, wchar_t *newnick)
382 {
383     return(0);
384 }
385
386 static int hubreqconn(struct fnetpeer *peer)
387 {
388     return(0);
389 }
390
391 static struct fnet adcnet_store = {
392     .connect = hubconnect,
393     .destroy = hubdestroy,
394     .setnick = hubsetnick,
395     .reqconn = hubreqconn,
396     .name = L"adc"
397 };
398
399 static struct fnet *adcnet = &adcnet_store;
400
401 static int run(void)
402 {
403     int ret;
404     struct fnetnode *fn, *nextfn;
405     struct adchub *hub;
406     struct qcmd *qcmd;
407     
408     ret = 0;
409     for(fn = fnetnodes; fn != NULL; fn = nextfn) {
410         nextfn = fn->next;
411         if(fn->fnet != adcnet)
412             continue;
413         if((hub = fn->data) == NULL)
414             continue;
415         if((qcmd = ulqcmd(&hub->queue)) != NULL) {
416             if((fn->sk != NULL) && (fn->sk->state == SOCK_EST))
417                 dispatch(qcmd, fn);
418             freeqcmd(qcmd);
419             ret = 1;
420             break;
421         }
422     }
423     return(ret);
424 }
425
426 static void preinit(int hup)
427 {
428     if(hup)
429         return;
430     regfnet(adcnet);
431 }
432
433 static int init(int hup)
434 {
435     int i;
436     char idbuf[24], *id32;
437     struct tigerhash th;
438     
439     if(!hup) {
440         eoc = swcsdup(L"");
441         
442         if((privid = fetchvar("adc.pid", NULL)) == NULL) {
443             for(i = 0; i < sizeof(idbuf); i++)
444                 idbuf[i] = rand() % 256;
445             id32 = base32encode(idbuf, sizeof(idbuf));
446             id32[39] = 0;
447             privid = icmbstowcs(id32, "us-ascii");
448             free(id32);
449             storevar("adc.pid", privid, sizeof(*privid) * (wcslen(privid) + 1));
450         }
451         
452         id32 = base32decode(icswcstombs(privid, "us-ascii", NULL), NULL);
453         inittiger(&th);
454         dotiger(&th, id32, 24);
455         synctiger(&th);
456         free(id32);
457         restiger(&th, idbuf);
458         id32 = base32encode(idbuf, sizeof(idbuf));
459         id32[39] = 0;
460         cid = icmbstowcs(id32, "us-ascii");
461         free(id32);
462     }
463     return(0);
464 }
465
466 static void terminate(void)
467 {
468     
469 }
470
471 static struct configvar myvars[] = {
472     {CONF_VAR_INT, "udpport", {.num = 0}},
473     {CONF_VAR_INT, "tcpport", {.num = 0}},
474     {CONF_VAR_END}
475 };
476
477 static struct module me = {
478     .conf = {
479         .vars = myvars
480     },
481     .preinit = preinit,
482     .init = init,
483     .run = run,
484     .terminate = terminate,
485     .name = "adc"
486 };
487
488 MODULE(me)