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