5e00ad8220e78013d8d498e6e2d6de8afafb907e
[doldaconnect.git] / daemon / ui.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 #define _GNU_SOURCE
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <malloc.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <netinet/ip6.h>
26 #include <arpa/inet.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <wchar.h>
31 #include <wctype.h>
32 #include <iconv.h>
33 #include <pwd.h>
34 #include <time.h>
35 #include <fcntl.h>
36 #include <signal.h>
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41 #include "conf.h"
42 #include "auth.h"
43 #include "utils.h"
44 #include "net.h"
45 #include "module.h"
46 #include "sysevents.h"
47 #include "filenet.h"
48 #include "transfer.h"
49 #include "search.h"
50 #include "client.h"
51
52 #define PERM_DISALLOW 1
53 #define PERM_ADMIN 2
54 #define PERM_FNETCTL 4
55 #define PERM_TRANS 8
56 #define PERM_TRANSCU 16
57 #define PERM_CHAT 32
58 #define PERM_SRCH 64
59
60 #define NOTIF_END 0
61 #define NOTIF_INT 1
62 #define NOTIF_STR 2
63 #define NOTIF_FLOAT 3
64 #define NOTIF_ID 4
65 #define NOTIF_PEND 0
66 #define NOTIF_WAIT 1
67
68 struct uidata;
69
70 struct command
71 {
72     wchar_t *name;
73     void (*handler)(struct socket *sk, struct uidata *data, int argc, wchar_t **argv);
74 };
75
76 struct qcommand
77 {
78     struct qcommand *next;
79     struct command *cmd;
80     int argc;
81     wchar_t **argv;
82 };
83
84 struct uiuser
85 {
86     struct uiuser *next, *prev;
87     int used;
88     wchar_t *name;
89     unsigned long perms;
90     int delete;
91 };
92
93 struct notif
94 {
95     struct notif *next, *prev;
96     struct uidata *ui;
97     int state;
98     int code;
99     double rlimit;
100     size_t argc;
101     struct timer *exptimer;
102     struct notifarg
103     {
104         int dt;
105         union
106         {
107             int n;
108             wchar_t *s;
109             double d;
110         } d;
111     } *argv;
112 };
113
114 struct uidata
115 {
116     struct uidata *next, *prev;
117     struct socket *sk;
118     struct qcommand *queue, *queuelast;
119     struct authhandle *auth;
120     int close;
121     union
122     {
123         struct
124         {
125             int fnact:1;
126             int fnchat:1;
127             int fnpeer:1;
128             int tract:1;
129             int trprog:1;
130             int srch:1;
131         } b;
132         int w;
133     } notify;
134     wchar_t *username;
135     struct uiuser *userinfo;
136     uid_t uid;
137     struct notif *fnotif, *lnotif;
138     char *fcmdbuf;
139     size_t fcmdbufdata, fcmdbufsize;
140     pid_t fcmdpid;
141     struct socket *fcmdsk;
142     /* Read buffer */
143     char *inbuf;
144     size_t inbufsize, indata;
145     /* Wordset storage */
146     wchar_t **argv;
147     int argc, args;
148     /* WCS conversation stuff */
149     wchar_t *cb; /* Conversation buffer */
150     size_t cbsize, cbdata;
151     iconv_t ichandle;
152     /* Parser data */
153     int ps; /* Parser state */
154     wchar_t *pp; /* Current parse pointer */
155     wchar_t *cw; /* Current word (building storage) */
156     size_t cwsize, cwdata;
157 };
158
159 static int srcheta(struct search *srch, void *uudata);
160 static int srchcommit(struct search *srch, void *uudata);
161 static int srchres(struct search *srch, struct srchres *sr, void *uudata);
162
163 struct uiuser *users = NULL;
164 struct uidata *actives = NULL;
165 struct socket *uisocket = NULL;
166
167 static wchar_t *quoteword(wchar_t *word)
168 {
169     wchar_t *wp, *buf, *bp;
170     int dq, numbs, numc;
171     
172     dq = 0;
173     numbs = 0;
174     numc = 0;
175     if(*word == L'\0')
176     {
177         dq = 1;
178     } else {
179         for(wp = word; *wp != L'\0'; wp++)
180         {
181             if(!dq && iswspace(*wp))
182                 dq = 1;
183             if((*wp == L'\\') || (*wp == L'\"'))
184                 numbs++;
185             numc++;
186         }
187     }
188     if(!dq && !numbs)
189         return(NULL);
190     bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
191     if(dq)
192         *(bp++) = L'\"';
193     for(wp = word; *wp != L'\0'; wp++)
194     {
195         if((*wp == L'\\') || (*wp == L'\"'))
196             *(bp++) = L'\\';
197         *(bp++) = *wp;
198     }
199     if(dq)
200         *(bp++) = L'\"';
201     *(bp++) = L'\0';
202     return(buf);
203 }
204
205 static void sq(struct socket *sk, int cont, ...)
206 {
207     int num, freepart;
208     va_list al;
209     char *final;
210     wchar_t *buf;
211     wchar_t *part, *tpart;
212     size_t bufsize, bufdata;
213     
214     buf = NULL;
215     bufsize = bufdata = 0;
216     num = 0;
217     va_start(al, cont);
218     while((part = va_arg(al, wchar_t *)) != NULL)
219     {
220         if(*part == L'%')
221         {
222             /*
223              * This kludge demands that all arguments that you call it
224              * with are the size of an int. That happens to be the
225              * case for most datatypes on most platforms and
226              * compilers, but I don't know exactly which ones, and
227              * also a long long is a notable candidate of an arg that
228              * is not the size of an int on 32-bit archs. If it breaks
229              * some existing call on your architecture, please tell
230              * me.
231              */
232             part = vswprintf2(tpart = (part + 1), al);
233             for(; *tpart != L'\0'; tpart++)
234             {
235                 if(*tpart == L'%')
236                 {
237                     if(tpart[1] == L'%')
238                         tpart++;
239                     else
240                         va_arg(al, int);
241                 }
242             }
243             freepart = 1;
244         } else {
245             freepart = 0;
246         }
247         if((tpart = quoteword(part)) != NULL)
248         {
249             if(freepart)
250                 free(part);
251             part = tpart;
252             freepart = 1;
253         }
254         if((num > 1) || ((num == 1) && !(cont & 1)))
255             addtobuf(buf, L' ');
256         bufcat(buf, part, wcslen(part));
257         if((num == 0) && (cont & 1))
258             addtobuf(buf, L'-');
259         num++;
260         if(freepart)
261             free(part);
262     }
263     if(cont & 2)
264         bufcat(buf, L" \0", 2);
265     else
266         bufcat(buf, L"\r\n\0", 3);
267     if((final = icwcstombs(buf, "utf-8")) == NULL)
268     {
269         flog(LOG_CRIT, "could not convert \"%ls\" into utf-8: %s", buf, strerror(errno));
270         free(buf);
271         return;
272     }
273     va_end(al);
274     free(buf);
275     sockqueue(sk, final, strlen(final));
276     free(final);
277 }
278
279 struct uiuser *finduser(wchar_t *name)
280 {
281     struct uiuser *user;
282     
283     for(user = users; user != NULL; user = user->next)
284     {
285         if(!wcscmp(user->name, name))
286             break;
287     }
288     return(user);
289 }
290
291 static void logout(struct uidata *data)
292 {
293     data->userinfo = NULL;
294     if(data->username != NULL)
295         free(data->username);
296     data->username = NULL;
297     if(data->auth != NULL)
298         authputhandle(data->auth);
299     data->auth = NULL;
300 }
301
302 static int haspriv(struct uidata *data, int perm)
303 {
304     if(data->userinfo == NULL)
305         return(0);
306     if(data->userinfo->perms & perm)
307         return(1);
308     else
309         return(0);
310 }
311
312 /* Useful macros for the command functions: */
313 #define haveargs(n) do { if(argc < n) { sq(sk, 0, L"501", L"Wrong number of arguments", NULL); return; } } while(0)
314 #define havepriv(p) do { if((data->userinfo == NULL) || ((data->userinfo->perms & (p)) != (p))) { sq(sk, 0, L"502", L"%Unauthorized request - %x needed", (p), NULL); return; } } while(0)
315
316 static void cmd_connect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
317 {
318     int valid;
319     struct in6_addr mv4lo;
320     
321     if(confgetint("ui", "onlylocal"))
322     {
323         switch(sk->remote->sa_family)
324         {
325         case AF_INET:
326             valid = ((struct sockaddr_in *)sk->remote)->sin_addr.s_addr == INADDR_LOOPBACK;
327             break;
328         case AF_INET6:
329             inet_pton(AF_INET6, "::ffff:127.0.0.1", &mv4lo);
330             valid = 0;
331             if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))
332                 valid = 1;
333             if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback)))
334                 valid = 1;
335             break;
336         default:
337             valid = 0;
338             break;
339         }
340         if(!valid)
341         {
342             sq(sk, 0, L"502", L"Only localhost connections allowed to this host", NULL);
343             sk->close = 1;
344             data->close = 1;
345             return;
346         }
347     }
348     sq(sk, 0, L"200", L"%Dolda Connect daemon v%s", VERSION, NULL);
349 }
350
351 static void cmd_notfound(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
352 {
353     if((argv != NULL) && (argv[0] != NULL))
354         sq(sk, 0, L"500", L"%Command not found: %ls", argv[0], NULL);
355     else
356         sq(sk, 0, L"500", L"No command", NULL);
357 }
358
359 static void cmd_shutdown(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
360 {
361     extern volatile int running;
362     
363     havepriv(PERM_ADMIN);
364     flog(LOG_NOTICE, "UI shutdown request from %ls, shutting down", data->username);
365     running = 0;
366     sq(sk, 0, L"200", L"Daemon shutting down", NULL);
367 }
368
369 static void cmd_quit(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
370 {
371     sq(sk, 0, L"200", L"Closing connection", NULL);
372     data->close = 1;
373 }
374
375 static void cmd_lsauth(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
376 {
377     struct authmech *mech, *prev;
378     
379     prev = NULL;
380     for(mech = mechs; mech != NULL; mech = mech->next)
381     {
382         if(mech->enabled)
383         {
384             if(prev != NULL)
385                 sq(sk, 1, L"200", prev->name, NULL);
386             prev = mech;
387         }
388     }
389     if(prev == NULL)
390         sq(sk, 0, L"201", L"No authentication methods supported", NULL);
391     else
392         sq(sk, 0, L"200", prev->name, NULL);
393 }
394
395 static void cmd_login(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
396 {
397     char *buf;
398     int code;
399     struct passwd *pwd;
400     
401     haveargs(3);
402     if(data->username != NULL)
403     {
404         if(data->userinfo != NULL)
405             sq(sk, 0, L"503", L"Already logged in", NULL);
406         else
407             sq(sk, 0, L"503", L"Already logging in", NULL);
408         return;
409     }
410     if((buf = icwcstombs(argv[2], NULL)) == NULL)
411     {
412         sq(sk, 0, L"504", L"Could not convert username to locale charset", NULL);
413         return;
414     }
415     data->username = swcsdup(argv[2]);
416     if((pwd = getpwnam(buf)) == NULL)
417         data->uid = -1;
418     else
419         data->uid = pwd->pw_uid;
420     if((data->auth = initauth(argv[1], buf)) == NULL)
421     {
422         if(errno == ENOENT)
423             sq(sk, 0, L"508", L"No such authentication mechanism", NULL);
424         else
425             sq(sk, 0, L"505", L"Could not initialize authentication system", L"%%s", strerror(errno), NULL);
426         free(buf);
427         logout(data);
428         return;
429     }
430     free(buf);
431     switch(authenticate(data->auth, NULL))
432     {
433     case AUTH_SUCCESS:
434         data->userinfo = finduser(data->username);
435         if(data->userinfo == NULL)
436             data->userinfo = finduser(L"default");
437         if(data->uid == -1)
438         {
439             sq(sk, 0, L"506", L"Authentication error", NULL);
440             flog(LOG_INFO, "user %ls authenticated successfully, but no account existed", data->username);
441             logout(data);
442         } else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) {
443             sq(sk, 0, L"506", L"Authentication error", NULL);
444             flog(LOG_INFO, "user %ls authenticated successfully, but was not authorized", data->username);
445             logout(data);
446         } else {
447             sq(sk, 0, L"200", L"Welcome", NULL);
448             flog(LOG_INFO, "%ls (UID %i) logged in", data->username, data->uid);
449         }
450         break;
451     case AUTH_DENIED:
452         sq(sk, 0, L"506", L"Authentication error", L"%%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
453         logout(data);
454         break;
455     case AUTH_PASS:
456         switch(data->auth->prompt)
457         {
458         case AUTH_PR_AUTO:
459             code = 300;
460             break;
461         case AUTH_PR_NOECHO:
462             code = 301;
463             break;
464         case AUTH_PR_ECHO:
465             code = 302;
466             break;
467         case AUTH_PR_INFO:
468             code = 303;
469             break;
470         case AUTH_PR_ERROR:
471             code = 304;
472             break;
473         }
474         sq(sk, 0, L"%%i", code, data->auth->text, NULL);
475         break;
476     case AUTH_ERR:
477         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
478         logout(data);
479         break;
480     default:
481         flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_login");
482         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
483         logout(data);
484         break;
485     }
486 }
487
488 static void cmd_pass(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
489 {
490     char *buf;
491     int code;
492
493     haveargs(2);
494     if((buf = icwcstombs(argv[1], NULL)) == NULL)
495     {
496         sq(sk, 0, L"504", L"Could not convert data to locale charset", NULL);
497         return;
498     }
499     if((data->auth == NULL) || (data->userinfo != NULL))
500     {
501         sq(sk, 0, L"507", L"Data not expected", NULL);
502         return;
503     }
504     switch(authenticate(data->auth, buf))
505     {
506     case AUTH_SUCCESS:
507         data->userinfo = finduser(data->username);
508         if(data->userinfo == NULL)
509             data->userinfo = finduser(L"default");
510         if(data->uid == -1)
511         {
512             sq(sk, 0, L"506", L"Authentication error", NULL);
513             flog(LOG_INFO, "user %ls authenticated successfully, but no account existed", data->username);
514             logout(data);
515         } else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) {
516             sq(sk, 0, L"506", L"Authentication error", NULL);
517             flog(LOG_INFO, "user %ls authenticated successfully, but was not authorized", data->username);
518             logout(data);
519         } else {
520             sq(sk, 0, L"200", L"Welcome", NULL);
521             flog(LOG_INFO, "%ls (UID %i) logged in", data->username, data->uid);
522         }
523         break;
524     case AUTH_DENIED:
525         sq(sk, 0, L"506", L"Authentication error", L"%%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
526         logout(data);
527         break;
528     case AUTH_PASS:
529         switch(data->auth->prompt)
530         {
531         case AUTH_PR_AUTO:
532             code = 300;
533             break;
534         case AUTH_PR_NOECHO:
535             code = 301;
536             break;
537         case AUTH_PR_ECHO:
538             code = 302;
539             break;
540         case AUTH_PR_INFO:
541             code = 303;
542             break;
543         case AUTH_PR_ERROR:
544             code = 304;
545             break;
546         }
547         sq(sk, 0, L"%%i", code, data->auth->text, NULL);
548         break;
549     case AUTH_ERR:
550         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
551         logout(data);
552         break;
553     default:
554         flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_pass");
555         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
556         logout(data);
557         break;
558     }
559     free(buf);
560 }
561
562 static void cmd_fnetconnect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
563 {
564     int i;
565     char *buf;
566     int err;
567     struct fnetnode *fn;
568     struct wcspair *args;
569     
570     haveargs(3);
571     havepriv(PERM_FNETCTL);
572     if((buf = icwcstombs(argv[2], NULL)) == NULL)
573     {
574         sq(sk, 0, L"504", L"Could not convert data to locale charset", NULL);
575         return;
576     }
577     args = NULL;
578     for(i = 3; i < argc - 1; i += 2)
579         newwcspair(argv[i], argv[i + 1], &args);
580     fn = fnetinitconnect(argv[1], buf, args);
581     err = errno;
582     free(buf);
583     if(fn == NULL)
584     {
585         if(errno == EPROTONOSUPPORT)
586             sq(sk, 0, L"511", L"No such network name", NULL);
587         else
588             sq(sk, 0, L"509", L"Could not parse the address", L"%%s", strerror(err), NULL);
589         return;
590     }
591     linkfnetnode(fn);
592     fnetsetname(fn, argv[2]);
593     putfnetnode(fn);
594     sq(sk, 0, L"200", L"Connection under way", NULL);
595 }
596
597 static void cmd_lsnodes(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
598 {
599     struct fnetnode *fn;
600
601     if(fnetnodes == NULL)
602     {
603         sq(sk, 0, L"201", L"No connected nodes", NULL);
604         return;
605     }
606     for(fn = fnetnodes; fn != NULL; fn = fn->next)
607     {
608         sq(sk, (fn->next != NULL)?1:0, L"200", L"%%i", fn->id, fn->fnet->name, (fn->name == NULL)?L"":fn->name, L"%%i", fn->numpeers, L"%%i", fn->state, L"%%ls", fn->pubid, NULL);
609     }
610 }
611
612 static void cmd_disconnect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
613 {
614     struct fnetnode *fn;
615     int i;
616     
617     haveargs(2);
618     havepriv(PERM_FNETCTL);
619     /* Note - Programmatical user interfaces must only give one
620      * argument per command, the multiple argument form is only for
621      * convenience when manually controlling the daemon via
622      * eg. telnet. The reason is that the return codes aren't clear
623      * enough for the multiple argument form. */
624     for(i = 1; i < argc; i++)
625     {
626         if((fn = findfnetnode(wcstol(argv[i], NULL, 0))) == NULL)
627         {
628             sq(sk, 0, L"510", L"No such node", NULL);
629             return;
630         }
631         killfnetnode(fn);
632         unlinkfnetnode(fn);
633     }
634     sq(sk, 0, L"200", L"Node flagged for disconnection", NULL);
635 }
636
637 static void cmd_lspa(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
638 {
639     struct fnetnode *fn;
640     struct fnetpeerdatum *datum;
641     
642     haveargs(2);
643     if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
644     {
645         sq(sk, 0, L"510", L"No such node", NULL);
646         return;
647     }
648     if(fn->peerdata == NULL)
649     {
650         sq(sk, 0, L"201", L"No data available", NULL);
651     } else {
652         for(datum = fn->peerdata; datum != NULL; datum = datum->next)
653             sq(sk, (datum->next != NULL)?1:0, L"200", datum->id, L"%%i", datum->datatype, NULL);
654     }
655 }
656
657 static void cmd_lspeers(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
658 {
659     int i;
660     struct fnetnode *fn;
661     struct fnetpeer *peer;
662     wchar_t buf[40];
663     
664     haveargs(2);
665     if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
666     {
667         sq(sk, 0, L"510", L"No such node", NULL);
668         return;
669     }
670     if(fn->peers == NULL)
671     {
672         sq(sk, 0, L"201", L"No peers avaiable", NULL);
673     } else {
674         for(peer = fn->peers; peer != NULL; peer = peer->next)
675         {
676             sq(sk, 2 | ((peer->next != NULL)?1:0), L"200", peer->id, peer->nick, NULL);
677             for(i = 0; i < peer->dinum; i++)
678             {
679                 if(peer->peerdi[i].datum->datatype == FNPD_INT)
680                     sq(sk, 2, peer->peerdi[i].datum->id, L"%%i", peer->peerdi[i].data.num, NULL);
681                 /* Note: A long long is not the size of an int, so
682                  * sq() can't handle the conversion itself. */
683                 if(peer->peerdi[i].datum->datatype == FNPD_LL)
684                 {
685                     swprintf(buf, 40, L"%lli", peer->peerdi[i].data.lnum);
686                     sq(sk, 2, peer->peerdi[i].datum->id, buf, NULL);
687                 }
688                 if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
689                     sq(sk, 2, peer->peerdi[i].datum->id, peer->peerdi[i].data.str, NULL);
690             }
691             sq(sk, 0, NULL);
692         }
693     }
694 }
695
696 static void cmd_download(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
697 {
698     int i;
699     struct fnet *net;
700     struct fnetnode *fn;
701     struct transfer *transfer;
702     struct fnetpeer *peer;
703     
704     haveargs(4);
705     if((argc > 5) && ((argc % 2) == 0))
706     {
707         sq(sk, 0, L"501", L"Must have an even number of arguments", NULL);
708         return;
709     }
710     havepriv(PERM_TRANS);
711     if((*(argv[1]) >= L'0') && (*(argv[1]) <= L'9'))
712     {
713         if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
714         {
715             sq(sk, 0, L"510", L"No such node", NULL);
716             return;
717         }
718         net = fn->fnet;
719     } else {
720         fn = NULL;
721         if((net = findfnet(argv[1])) == NULL)
722         {
723             sq(sk, 0, L"511", L"No such network name", NULL);
724             return;
725         }
726     }
727     transfer = newtransfer();
728     authgethandle(transfer->auth = data->auth);
729     transfer->fnet = net;
730     transfer->peerid = swcsdup(argv[2]);
731     transfer->path = swcsdup(argv[3]);
732     transfer->dir = TRNSD_DOWN;
733     transfer->owner = data->uid;
734     if(fn != NULL)
735     {
736         transfer->fn = fn;
737         getfnetnode(fn);
738         linktransfer(transfer);
739         if(((peer = fnetfindpeer(fn, transfer->peerid)) != NULL) && (peer->nick != NULL))
740             transfersetnick(transfer, peer->nick);
741     } else {
742         linktransfer(transfer);
743     }
744     if(argc > 4)
745         transfersetsize(transfer, wcstol(argv[4], NULL, 0));
746     if(argc > 5)
747     {
748         for(i = 5; i < argc; i += 2)
749         {
750             if(!wcscmp(argv[i], L"hash"))
751             {
752                 transfersethash(transfer, parsehash(argv[i + 1]));
753             } else {
754                 newwcspair(argv[i], argv[i + 1], &transfer->args);
755             }
756         }
757     }
758     sq(sk, 0, L"200", L"%%i", transfer->id, L"Download queued", NULL);
759     transfersetactivity(transfer, L"create");
760 }
761
762 static void cmd_lstrans(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
763 {
764     struct transfer *transfer, *pt;
765     
766     havepriv(PERM_TRANS);
767     pt = NULL;
768     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
769     {
770         if((transfer->dir != TRNSD_DOWN) || (transfer->owner == data->uid))
771         {
772             if(pt != NULL)
773                 sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->dir,
774                    L"%%i", pt->state, pt->peerid,
775                    (pt->peernick == NULL)?L"":(pt->peernick),
776                    (pt->path == NULL)?L"":(pt->path),
777                    L"%%i", pt->size, L"%%i", pt->curpos,
778                    (pt->hash == NULL)?L"":unparsehash(pt->hash),
779                    NULL);
780             pt = transfer;
781         }
782     }
783     if(pt == NULL)
784         sq(sk, 0, L"201", L"No transfers", NULL);
785     else
786         sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->dir,
787            L"%%i", pt->state, pt->peerid,
788            (pt->peernick == NULL)?L"":(pt->peernick),
789            (pt->path == NULL)?L"":(pt->path),
790            L"%%i", pt->size, L"%%i", pt->curpos,
791            (pt->hash == NULL)?L"":unparsehash(pt->hash),
792            NULL);
793 }
794
795 static void cmd_cancel(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
796 {
797     struct transfer *transfer;
798     
799     haveargs(2);
800     havepriv(PERM_TRANS);
801     if((transfer = findtransfer(wcstol(argv[1], NULL, 0))) == NULL)
802     {
803         sq(sk, 0, L"512", L"No such transfer", NULL);
804         return;
805     }
806     if((transfer->dir == TRNSD_UP) && !(data->userinfo->perms & PERM_TRANSCU))
807     {
808         sq(sk, 0, L"502", L"You are not allowed to cancel uploads", NULL);
809         return;
810     }
811     if((transfer->dir == TRNSD_DOWN) && (transfer->owner != data->uid))
812     {
813         sq(sk, 0, L"502", L"You do not own that transfer", NULL);
814         return;
815     }
816     transfer->close = 1;
817     sq(sk, 0, L"200", L"Transfer cancelled", NULL);
818 }
819
820 static void cmd_notify(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
821 {
822     int i, val;
823     
824     if((argc % 2) != 1)
825     {
826         sq(sk, 0, L"501", L"Must have an even number of arguments", NULL);
827         return;
828     }
829     for(i = 1; i < argc; i += 2)
830     {
831         if(!wcscasecmp(argv[i + 1], L"on"))
832             val = 1;
833         else
834             val = 0;
835         if(!wcscasecmp(argv[i], L"all"))
836         {
837             if(val)
838                 data->notify.w = ~0;
839             else
840                 data->notify.w = 0;
841         } else if(!wcscasecmp(argv[i], L"fn:chat")) {
842             data->notify.b.fnchat = val;
843         } else if(!wcscasecmp(argv[i], L"fn:act")) {
844             data->notify.b.fnact = val;
845         } else if(!wcscasecmp(argv[i], L"fn:peer")) {
846             data->notify.b.fnpeer = val;
847         } else if(!wcscasecmp(argv[i], L"trans:act")) {
848             data->notify.b.tract = val;
849         } else if(!wcscasecmp(argv[i], L"trans:prog")) {
850             data->notify.b.trprog = val;
851         } else if(!wcscasecmp(argv[i], L"srch:act")) {
852             data->notify.b.srch = val;
853         }
854     }
855     sq(sk, 0, L"200", L"Notification alteration succeeded", NULL);
856 }
857
858 static void cmd_sendchat(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
859 {
860     struct fnetnode *fn;
861     int public;
862     
863     haveargs(5);
864     havepriv(PERM_CHAT);
865     if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
866     {
867         sq(sk, 0, L"510", L"No such node", NULL);
868         return;
869     }
870     public = wcstol(argv[2], NULL, 0);
871     if((public != 0) && (public != 1))
872     {
873         sq(sk, 0, L"509", L"Second argument must be 0 or 1", NULL);
874         return;
875     }
876     if(fn->state != FNN_EST)
877     {
878         sq(sk, 0, L"513", L"Hub is in state FNN_EST", NULL);
879         return;
880     }
881     if(fnetsendchat(fn, public, argv[3], argv[4]))
882     {
883         if(errno == ENOTSUP)
884             sq(sk, 0, L"513", L"This network does not support chatting", NULL);
885         else if(errno == EPERM)
886             sq(sk, 0, L"502", L"This node does not allow you to chat", NULL);
887         else if(errno == EILSEQ)
888             sq(sk, 0, L"504", L"This network could not support all the characters in that message", NULL);
889         else
890             sq(sk, 0, L"505", L"Could not chat", NULL);
891         return;
892     }
893     sq(sk, 0, L"200", L"Chat string sent", NULL);
894 }
895
896 static void cmd_search(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
897 {
898     struct search *srch;
899     struct fnetnode *fn;
900     struct sexpr *sexpr;
901     int i;
902     
903     haveargs(3);
904     havepriv(PERM_SRCH);
905     srch = newsearch(data->username, NULL);
906     for(i = 1; i < argc; i++)
907     {
908         if(!wcscmp(argv[i], L"all"))
909         {
910             for(fn = fnetnodes; fn != NULL; fn = fn->next)
911             {
912                 if(fn->state == FNN_EST)
913                     searchaddfn(srch, fn);
914             }
915             i++;
916             break;
917         } else if(!wcscmp(argv[i], L"prio")) {
918             if(++i == argc)
919             {
920                 sq(sk, 0, L"501", L"No argument to prio", NULL);
921                 freesearch(srch);
922                 return;
923             }
924             srch->prio = wcstol(argv[i], NULL, 0);
925         } else if(iswdigit(*argv[i])) {
926             if((fn = findfnetnode(wcstol(argv[i], NULL, 0))) == NULL)
927             {
928                 sq(sk, 0, L"510", L"No such node", NULL);
929                 freesearch(srch);
930                 return;
931             }
932             searchaddfn(srch, fn);
933         } else {
934             break;
935         }
936     }
937     if(srch->fnl == NULL)
938     {
939         sq(sk, 0, L"501", L"No fnetnodes to search found on line", NULL);
940         freesearch(srch);
941         return;
942     }
943     if(i == argc)
944     {
945         sq(sk, 0, L"501", L"No search expression found on line", NULL);
946         freesearch(srch);
947         return;
948     }
949     if((sexpr = parsesexpr(argc - i, argv + i)) == NULL)
950     {
951         sq(sk, 0, L"509", L"Could not parse search expression", NULL);
952         freesearch(srch);
953         return;
954     }
955     optsexpr(sexpr);
956     getsexpr(srch->sexpr = sexpr);
957     queuesearch(srch);
958     CBREG(srch, search_eta, srcheta, NULL, NULL);
959     CBREG(srch, search_commit, srchcommit, NULL, NULL);
960     CBREG(srch, search_result, srchres, NULL, NULL);
961     sq(sk, 0, L"200", L"%%i", srch->id, L"%%i", srch->eta - time(NULL), NULL);
962     putsexpr(sexpr);
963 }
964
965 static void cmd_lssrch(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
966 {
967     struct search *srch, *pt;
968     time_t now;
969     
970     havepriv(PERM_SRCH);
971     pt = NULL;
972     now = time(NULL);
973     for(srch = searches; srch != NULL; srch = srch->next)
974     {
975         if(!wcscmp(srch->owner, data->username))
976         {
977             if(pt != NULL)
978                 sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL);
979             pt = srch;
980         }
981     }
982     if(pt == NULL)
983         sq(sk, 0, L"201", L"No searches", NULL);
984     else
985         sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL);
986 }
987
988 static void cmd_lssr(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
989 {
990     struct search *srch;
991     struct srchres *sr;
992     wchar_t buf[64];
993     
994     haveargs(2);
995     havepriv(PERM_SRCH);
996     if((srch = findsearch(wcstol(argv[1], NULL, 0))) == NULL)
997     {
998         sq(sk, 0, L"514", L"No such search", NULL);
999         return;
1000     }
1001     if(srch->results == NULL)
1002     {
1003         sq(sk, 0, L"201", L"No results", NULL);
1004     } else {
1005         for(sr = srch->results; sr != NULL; sr = sr->next)
1006         {
1007             swprintf(buf, 64, L"%f", sr->time);
1008             sq(sk, (sr->next != NULL)?1:0, L"200", L"%%ls", sr->filename, sr->fnet->name, L"%%ls", sr->peerid, L"%%i", sr->size, L"%%i", sr->slots, L"%%i", (sr->fn == NULL)?-1:(sr->fn->id), buf, L"%%ls", (sr->hash == NULL)?L"":unparsehash(sr->hash), NULL);
1009         }
1010     }
1011 }
1012
1013 static void cmd_cansrch(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1014 {
1015     struct search *srch;
1016     int i;
1017     
1018     haveargs(2);
1019     havepriv(PERM_SRCH);
1020     /* Note - Programmatical user interfaces must only give one
1021      * argument per command, the multiple argument form is only for
1022      * convenience when manually controlling the daemon via
1023      * eg. telnet. The reason is that the return codes aren't clear
1024      * enough for the multiple argument form. */
1025     for(i = 1; i < argc; i++)
1026     {
1027         if((srch = findsearch(wcstol(argv[i], NULL, 0))) == NULL)
1028         {
1029             sq(sk, 0, L"514", L"No such search", NULL);
1030             return;
1031         }
1032         freesearch(srch);
1033     }
1034     sq(sk, 0, L"200", L"Search cancelled", NULL);
1035 }
1036
1037 static void fcmdread(struct socket *sk, struct uidata *data)
1038 {
1039     char *buf;
1040     size_t bufsize;
1041     
1042     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
1043         return;
1044     bufcat(data->fcmdbuf, buf, bufsize);
1045     free(buf);
1046 }
1047
1048 static void fcmderr(struct socket *sk, int err, struct uidata *data)
1049 {
1050     wchar_t *wbuf, *p, *p2;
1051     
1052     if(err)
1053     {
1054         flog(LOG_WARNING, "error occurred on filtercmd pipe socket: %s", strerror(err));
1055         kill(-data->fcmdpid, SIGHUP);
1056         putsock(data->fcmdsk);
1057         data->fcmdsk = NULL;
1058         if(data->fcmdbuf != NULL)
1059         {
1060             free(data->fcmdbuf);
1061             data->fcmdbuf = NULL;
1062         }
1063         data->fcmdbufsize = data->fcmdbufdata = 0;
1064         sq(data->sk, 0, L"505", L"An error occurred on the pipe to the filtercmd", L"%%s", strerror(err), NULL);
1065         return;
1066     }
1067     putsock(data->fcmdsk);
1068     data->fcmdsk = NULL;
1069     data->fcmdpid = 0;
1070     if(data->fcmdbuf == NULL)
1071     {
1072         wbuf = swcsdup(L"");
1073     } else {
1074         addtobuf(data->fcmdbuf, 0);
1075         wbuf = icmbstowcs(data->fcmdbuf, NULL);
1076         free(data->fcmdbuf);
1077     }
1078     data->fcmdbuf = NULL;
1079     data->fcmdbufsize = data->fcmdbufdata = 0;
1080     if(wbuf == NULL)
1081     {
1082         sq(data->sk, 0, L"504", L"Filtercmd sent data which could not be converted from the local charset", NULL);
1083         return;
1084     }
1085     p = wbuf;
1086     for(p2 = wcschr(p, L'\n'); p2 != NULL; p2 = wcschr(p, L'\n'))
1087     {
1088         *(p2++) = L'\0';
1089         sq(data->sk, (*p2 == L'\0')?0:1, L"200", L"%%ls", p, NULL);
1090         p = p2;
1091     }
1092     if(*p == L'\0')
1093     {
1094         if(p == wbuf)
1095             sq(data->sk, 0, L"201", L"No data returned", NULL);
1096     } else {
1097         sq(data->sk, 0, L"200", L"%%ls", p, NULL);
1098     }
1099     free(wbuf);
1100 }
1101
1102 static void cmd_filtercmd(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1103 {
1104     int i;
1105     pid_t pid;
1106     int pipe;
1107     char **cargv, **pp;
1108     char *filtercmd, *argbuf;
1109     size_t cargvsize, cargvdata;
1110     struct passwd *pwent;
1111     
1112     haveargs(2);
1113     havepriv(PERM_TRANS);
1114     if((pwent = getpwuid(data->uid)) == NULL)
1115     {
1116         flog(LOG_WARNING, "no passwd entry for UI user %i", data->uid);
1117         sq(sk, 0, L"505", L"System error - Could not fork session", "Internal error", NULL);
1118         return;
1119     }
1120     if((filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), "dcdl-filtercmd", pwent->pw_dir)) == NULL)
1121     {
1122         flog(LOG_WARNING, "could not find filtercmd executable for user %s", pwent->pw_name);
1123         sq(sk, 0, L"505", L"System error - Could not fork session", L"Could not find filtercmd executable", NULL);
1124         return;
1125     }
1126     cargv = NULL;
1127     cargvsize = cargvdata = 0;
1128     addtobuf(cargv, filtercmd);
1129     for(i = 1; i < argc; i++)
1130     {
1131         if((argbuf = icwcstombs(argv[i], NULL)) == NULL)
1132         {
1133             for(i = 0; i < cargvdata; i++)
1134                 free(cargv[i]);
1135             free(cargv);
1136             sq(sk, 0, L"504", L"%Could not convert argument %i into local character set", i, L"%%s", strerror(errno), NULL);
1137             return;
1138         }
1139         addtobuf(cargv, argbuf);
1140     }
1141     addtobuf(cargv, NULL);
1142     if((pid = forksess(data->uid, data->auth, NULL, NULL, FD_FILE, 0, O_RDWR, "/dev/null", FD_PIPE, 1, O_RDONLY, &pipe, FD_FILE, 2, O_RDWR, "/dev/null", FD_END)) < 0)
1143     {
1144         flog(LOG_WARNING, "could not fork session in filtercmd: %s", strerror(errno));
1145         sq(sk, 0, L"505", L"System error - Could not fork session", L"%%s", strerror(errno), NULL);
1146         return;
1147     }
1148     if(pid == 0)
1149     {
1150         execv(filtercmd, cargv);
1151         flog(LOG_WARNING, "could not exec filtercmd %s: %s", filtercmd, strerror(errno));
1152         exit(127);
1153     }
1154     for(pp = cargv; *pp; pp++)
1155         free(*pp);
1156     free(cargv);
1157     data->fcmdsk = wrapsock(pipe);
1158     data->fcmdpid = pid;
1159     if(data->fcmdbuf != NULL)
1160     {
1161         free(data->fcmdbuf);
1162         data->fcmdbuf = NULL;
1163     }
1164     data->fcmdbufsize = data->fcmdbufdata = 0;
1165     data->fcmdsk->data = data;
1166     data->fcmdsk->readcb = (void (*)(struct socket *, void *))fcmdread;
1167     data->fcmdsk->errcb = (void (*)(struct socket *, int, void *))fcmderr;
1168 }
1169
1170 static void cmd_lstrarg(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1171 {
1172     struct transfer *transfer;
1173     struct wcspair *ta;
1174     
1175     haveargs(2);
1176     havepriv(PERM_TRANS);
1177     if((transfer = findtransfer(wcstol(argv[1], NULL, 0))) == NULL)
1178     {
1179         sq(sk, 0, L"512", L"No such transfer", NULL);
1180         return;
1181     }
1182     if((transfer->dir == TRNSD_DOWN) && (transfer->owner != data->uid))
1183     {
1184         sq(sk, 0, L"502", L"You do not own that transfer", NULL);
1185         return;
1186     }
1187     if(transfer->args == NULL)
1188     {
1189         sq(sk, 0, L"201", L"Transfer has no arguments", NULL);
1190     } else {
1191         for(ta = transfer->args; ta != NULL; ta = ta->next)
1192             sq(sk, ta->next != NULL, L"200", L"%%ls", ta->key, L"%%ls", ta->val, NULL);
1193     }
1194 }
1195
1196 static void cmd_hashstatus(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1197 {
1198     struct sharecache *node;
1199     int total, hashed;
1200     
1201     total = hashed = 0;
1202     for(node = shareroot->child; node != NULL; node = nextscnode(node))
1203     {
1204         if(node->f.b.type == FILE_REG)
1205         {
1206             total++;
1207             if(node->f.b.hastth)
1208                 hashed++;
1209         }
1210     }
1211     sq(sk, 0, L"200", L"%%i", total, L"tth", L"%%i", hashed, NULL);
1212 }
1213
1214 static void cmd_transstatus(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1215 {
1216     wchar_t *buf1, *buf2;
1217     
1218     havepriv(PERM_TRANS);
1219     buf1 = swprintf2(L"%lli", bytesdownload);
1220     buf2 = swprintf2(L"%lli", bytesupload);
1221     sq(sk, 0, L"200", L"%%ls", buf1, L"%%ls", buf2, NULL);
1222     free(buf1);
1223     free(buf2);
1224 }
1225
1226 #undef haveargs
1227 #undef havepriv
1228
1229 /*
1230  * Reserved command numbers for nameless commands:
1231  *  0: Issued when a client has connected
1232  *  1: Issued when a named command couldn't be found
1233  */
1234
1235 static struct command commands[] =
1236 {
1237     {NULL, cmd_connect},
1238     {NULL, cmd_notfound},
1239     {L"shutdown", cmd_shutdown},
1240     {L"quit", cmd_quit},
1241     {L"lsauth", cmd_lsauth},
1242     {L"login", cmd_login},
1243     {L"pass", cmd_pass},
1244     {L"cnct", cmd_fnetconnect},
1245     {L"lsnodes", cmd_lsnodes},
1246     {L"dcnct", cmd_disconnect},
1247     {L"lspa", cmd_lspa},
1248     {L"lspeers", cmd_lspeers},
1249     {L"download", cmd_download},
1250     {L"lstrans", cmd_lstrans},
1251     {L"cancel", cmd_cancel},
1252     {L"notify", cmd_notify},
1253     {L"sendchat", cmd_sendchat},
1254     {L"search", cmd_search},
1255     {L"lssrch", cmd_lssrch},
1256     {L"lssr", cmd_lssr},
1257     {L"cansrch", cmd_cansrch},
1258     {L"filtercmd", cmd_filtercmd},
1259     {L"lstrarg", cmd_lstrarg},
1260     {L"hashstatus", cmd_hashstatus},
1261     {L"transstatus", cmd_transstatus},
1262     {NULL, NULL}
1263 };
1264
1265 static void freequeuecmd(struct qcommand *qcmd)
1266 {
1267     int i;
1268     
1269     if(qcmd->argv != NULL)
1270     {
1271         for(i = 0; i < qcmd->argc; i++)
1272             free(qcmd->argv[i]);
1273         free(qcmd->argv);
1274     }
1275     free(qcmd);
1276 }
1277
1278 static struct qcommand *unlinkqcmd(struct uidata *data)
1279 {
1280     struct qcommand *qcmd;
1281     
1282     qcmd = data->queue;
1283     if(qcmd != NULL)
1284     {
1285         data->queue = qcmd->next;
1286         if(qcmd == data->queuelast)
1287             data->queuelast = qcmd->next;
1288     }
1289     return(qcmd);
1290 }
1291
1292 static void notifappendv(struct notif *notif, va_list args)
1293 {
1294     int dt, ca;
1295     
1296     while((dt = va_arg(args, int)) != NOTIF_END)
1297     {
1298         ca = notif->argc;
1299         notif->argv = realloc(notif->argv, sizeof(*notif->argv) * ++notif->argc);
1300         notif->argv[ca].dt = dt;
1301         switch(dt)
1302         {
1303         case NOTIF_INT:
1304         case NOTIF_ID:
1305             notif->argv[ca].d.n = va_arg(args, int);
1306             break;
1307         case NOTIF_STR:
1308             notif->argv[ca].d.s = wcsdup(va_arg(args, wchar_t *));
1309             break;
1310         case NOTIF_FLOAT:
1311             notif->argv[ca].d.d = va_arg(args, double);
1312             break;
1313         }
1314     }
1315 }
1316
1317 static void notifappend(struct notif *notif, ...)
1318 {
1319     va_list args;
1320     
1321     va_start(args, notif);
1322     notifappendv(notif, args);
1323     va_end(args);
1324 }
1325
1326 static struct notif *newnotif(struct uidata *data, int code, ...)
1327 {
1328     struct notif *notif;
1329     va_list args;
1330     
1331     notif = smalloc(sizeof(*notif));
1332     memset(notif, 0, sizeof(*notif));
1333     notif->rlimit = 0.0;
1334     notif->ui = data;
1335     notif->code = code;
1336     va_start(args, code);
1337     notifappendv(notif, args);
1338     va_end(args);
1339     notif->next = NULL;
1340     notif->prev = data->lnotif;
1341     if(data->lnotif != NULL)
1342         data->lnotif->next = notif;
1343     else
1344         data->fnotif = notif;
1345     data->lnotif = notif;
1346     return(notif);
1347 }
1348
1349 static void freenotif(struct notif *notif)
1350 {
1351     int i;
1352     
1353     if(notif->next != NULL)
1354         notif->next->prev = notif->prev;
1355     if(notif->prev != NULL)
1356         notif->prev->next = notif->next;
1357     if(notif == notif->ui->fnotif)
1358         notif->ui->fnotif = notif->next;
1359     if(notif == notif->ui->lnotif)
1360         notif->ui->lnotif = notif->prev;
1361     if(notif->exptimer != NULL)
1362         canceltimer(notif->exptimer);
1363     for(i = 0; i < notif->argc; i++)
1364     {
1365         if(notif->argv[i].dt == NOTIF_STR)
1366             free(notif->argv[i].d.s);
1367     }
1368     if(notif->argv != NULL)
1369         free(notif->argv);
1370     free(notif);
1371 }
1372
1373 static void notifexpire(int cancelled, struct notif *notif)
1374 {
1375     notif->exptimer = NULL;
1376     if(!cancelled)
1377         freenotif(notif);
1378 }
1379
1380 static struct notif *findnotif(struct notif *notif, int dir, int state, int code, int id)
1381 {
1382     int i, cont;
1383     
1384     for(; notif != NULL; notif = (dir?notif->next:notif->prev))
1385     {
1386         if((notif->code == code) && ((state < 0) || (state == notif->state)))
1387         {
1388             cont = 0;
1389             if(id >= 0)
1390             {
1391                 for(i = 0; i < notif->argc; i++)
1392                 {
1393                     if((notif->argv[i].dt == NOTIF_ID) && (notif->argv[i].d.n != id))
1394                     {
1395                         cont = 1;
1396                         break;
1397                     }
1398                 }
1399             }
1400             if(cont)
1401                 continue;
1402             break;
1403         }
1404     }
1405     return(notif);
1406 }
1407
1408 static void freeuidata(struct uidata *data)
1409 {
1410     int i;
1411     struct qcommand *qcmd;
1412     
1413     if(data->next != NULL)
1414         data->next->prev = data->prev;
1415     if(data->prev != NULL)
1416         data->prev->next = data->next;
1417     if(data == actives)
1418         actives = data->next;
1419     data->sk->readcb = NULL;
1420     data->sk->errcb = NULL;
1421     putsock(data->sk);
1422     while((qcmd = unlinkqcmd(data)) != NULL)
1423         freequeuecmd(qcmd);
1424     iconv_close(data->ichandle);
1425     if(data->cw != NULL)
1426         free(data->cw);
1427     if(data->cb != NULL)
1428         free(data->cb);
1429     if(data->argv != NULL)
1430     {
1431         for(i = 0; i < data->argc; i++)
1432             free(data->argv[i]);
1433         free(data->argv);
1434     }
1435     if(data->auth != NULL)
1436         authputhandle(data->auth);
1437     if(data->username != NULL)
1438     {
1439         if(data->userinfo != NULL)
1440             flog(LOG_INFO, "%ls logged out", data->username);
1441         free(data->username);
1442     }
1443     free(data->inbuf);
1444     while(data->fnotif != NULL)
1445         freenotif(data->fnotif);
1446     if(data->fcmdbuf != NULL)
1447         free(data->fcmdbuf);
1448     if(data->fcmdpid != 0)
1449         kill(-data->fcmdpid, SIGHUP);
1450     if(data->fcmdsk != NULL)
1451         putsock(data->fcmdsk);
1452     free(data);
1453 }
1454
1455 static void queuecmd(struct uidata *data, struct command *cmd, int argc, wchar_t **argv)
1456 {
1457     struct qcommand *new;
1458     
1459     new = smalloc(sizeof(*new));
1460     new->cmd = cmd;
1461     new->argc = argc;
1462     new->argv = argv;
1463     new->next = NULL;
1464     if(data->queuelast != NULL)
1465         data->queuelast->next = new;
1466     data->queuelast = new;
1467     if(data->queue == NULL)
1468         data->queue = new;
1469 }
1470
1471 static struct uidata *newuidata(struct socket *sk)
1472 {
1473     struct uidata *data;
1474     
1475     data = smalloc(sizeof(*data));
1476     memset(data, 0, sizeof(*data));
1477     data->sk = sk;
1478     getsock(sk);
1479     data->inbuf = smalloc(1024);
1480     data->uid = -1;
1481     if((data->ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
1482     {
1483         flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
1484         return(NULL);
1485     }
1486     data->next = actives;
1487     data->prev = NULL;
1488     if(actives != NULL)
1489         actives->prev = data;
1490     actives = data;
1491     return(data);
1492 }
1493
1494 static void uiread(struct socket *sk, struct uidata *data)
1495 {
1496     int ret, done;
1497     char *newbuf;
1498     char *p1, *p2;
1499     wchar_t *porig;
1500     size_t datalen, len2;
1501     struct command *cur;
1502     
1503     if(data->indata > 1024)
1504         data->indata = 0;
1505     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
1506         return;
1507     sizebuf(&data->inbuf, &data->inbufsize, data->indata + datalen, 1, 1);
1508     memcpy(data->inbuf + data->indata, newbuf, datalen);
1509     free(newbuf);
1510     data->indata += datalen;
1511     if(data->cb == NULL)
1512     {
1513         data->cb = smalloc(sizeof(wchar_t) * (data->cbsize = 64));
1514         data->cbdata = 0;
1515         data->pp = data->cb;
1516     }
1517     done = 0;
1518     while(!done)
1519     {
1520         if(data->cbsize == data->cbdata)
1521         {
1522             len2 = data->pp - data->cb;
1523             data->cb = srealloc(data->cb, sizeof(wchar_t) * (data->cbsize *= 2));
1524             data->pp = data->cb + len2;
1525         }
1526         p1 = data->inbuf;
1527         p2 = (char *)(porig = (data->cb + data->cbdata));
1528         len2 = sizeof(wchar_t) * (data->cbsize - data->cbdata);
1529         ret = iconv(data->ichandle, &p1, &data->indata, &p2, &len2);
1530         memmove(data->inbuf, p1, data->indata);
1531         /* Just a sanity check */
1532         if(((p2 - ((char *)data->cb)) % sizeof(wchar_t)) != 0)
1533         {
1534             flog(LOG_CRIT, "Aiya! iconv does strange things to our wchar_t's!");
1535             abort();
1536         }
1537         data->cbdata += (((wchar_t *)p2) - porig);
1538         if(ret < 0)
1539         {
1540             switch(errno)
1541             {
1542             case EILSEQ:
1543                 /* XXX: Should this really just ignore it? */
1544                 data->indata = 0;
1545                 done = 1;
1546                 break;
1547             case EINVAL:
1548                 done = 1;
1549                 break;
1550             case E2BIG:
1551                 /* Just a sanity check */
1552                 if(data->cbsize != data->cbdata)
1553                 {
1554                     flog(LOG_CRIT, "Aiya! iconv doesn't give us wchar_t's!");
1555                     abort();
1556                 }
1557                 break;
1558             default:
1559                 flog(LOG_WARNING, "bug: strange error from iconv in uiread: %s", strerror(errno));
1560                 break;
1561             }
1562         } else {
1563             done = 1;
1564         }
1565     }
1566     done = 0;
1567     while(!done && (data->pp - data->cb < data->cbdata))
1568     {
1569         switch(data->ps)
1570         {
1571         case 0:
1572             if(iswspace(*data->pp))
1573             {
1574                 if(*data->pp == L'\r')
1575                 {
1576                     if(data->pp == data->cb + data->cbdata - 1)
1577                     {
1578                         done = 1;
1579                         break;
1580                     }
1581                     if(*(++data->pp) == L'\n')
1582                     {
1583                         if((data->argv != NULL) && (data->argv[0] != NULL))
1584                         {
1585                             for(cur = commands; cur->handler != NULL; cur++)
1586                             {
1587                                 if(cur->name == NULL)
1588                                     continue;
1589                                 if(!wcscasecmp(cur->name, data->argv[0]))
1590                                 {
1591                                     queuecmd(data, cur, data->argc, data->argv);
1592                                     break;
1593                                 }
1594                             }
1595                             if(cur->handler == NULL)
1596                                 queuecmd(data, &commands[1], data->argc, data->argv);
1597                         } else {
1598                             queuecmd(data, &commands[1], data->argc, data->argv);
1599                         }
1600                         data->argv = NULL;
1601                         data->args = 0;
1602                         data->argc = 0;
1603                         wmemmove(data->cb, data->pp, data->cbdata -= (data->pp - data->cb));
1604                         data->pp = data->cb;
1605                     } else {
1606                         data->pp++;
1607                     }
1608                 } else {
1609                     data->pp++;
1610                 }
1611             } else {
1612                 data->ps = 1;
1613                 data->cwdata = 0;
1614             }
1615             break;
1616         case 1:
1617             if(iswspace(*data->pp))
1618             {
1619                 addtobuf(data->cw, L'\0');
1620                 sizebuf(&data->argv, &data->args, data->argc + 1, sizeof(*data->argv), 1);
1621                 data->argv[data->argc++] = data->cw;
1622                 data->cw = NULL;
1623                 data->cwsize = 0;
1624                 data->cwdata = 0;
1625                 data->ps = 0;
1626             } else if(*data->pp == L'\"') {
1627                 data->ps = 2;
1628                 data->pp++;
1629             } else if(*data->pp == L'\\') {
1630                 if(data->pp == data->cb + data->cbdata - 1)
1631                 {
1632                     done = 1;
1633                     break;
1634                 }
1635                 addtobuf(data->cw, *(++data->pp));
1636                 data->pp++;
1637             } else {
1638                 addtobuf(data->cw, *(data->pp++));
1639             }
1640             break;
1641         case 2:
1642             if(*data->pp == L'\"') 
1643             {
1644                 data->ps = 1;
1645             } else if(*data->pp == L'\\') {
1646                 if(data->pp == data->cb + data->cbdata - 1)
1647                 {
1648                     done = 1;
1649                     break;
1650                 }
1651                 addtobuf(data->cw, *(++(data->pp)));
1652             } else {
1653                 addtobuf(data->cw, *data->pp);
1654             }
1655             data->pp++;
1656             break;
1657         }
1658     }
1659 }
1660
1661 static void uierror(struct socket *sk, int err, struct uidata *data)
1662 {
1663     if(err)
1664         flog(LOG_WARNING, "error occurred on UI socket: %s", strerror(err));
1665     freeuidata(data);
1666 }
1667
1668 static void uiaccept(struct socket *sk, struct socket *newsk, void *data)
1669 {
1670     struct uidata *uidata;
1671     
1672     newsk->data = uidata = newuidata(newsk);
1673     socksettos(newsk, confgetint("ui", "uitos"));
1674     if(uidata == NULL)
1675         return;
1676     newsk->errcb = (void (*)(struct socket *, int, void *))uierror;
1677     newsk->readcb = (void (*)(struct socket *, void *))uiread;
1678     queuecmd(uidata, &commands[0], 0, NULL);
1679 }
1680
1681 static int srcheta(struct search *srch, void *uudata)
1682 {
1683     struct uidata *data;
1684     
1685     for(data = actives; data != NULL; data = data->next)
1686     {
1687         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1688             newnotif(data, 620, NOTIF_ID, srch->id, NOTIF_INT, srch->eta - time(NULL), NOTIF_END);
1689     }
1690     return(0);
1691 }
1692
1693 static int srchcommit(struct search *srch, void *uudata)
1694 {
1695     struct uidata *data;
1696
1697     for(data = actives; data != NULL; data = data->next)
1698     {
1699         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1700             newnotif(data, 621, NOTIF_ID, srch->id, NOTIF_END);
1701     }
1702     return(0);
1703 }
1704
1705 static int srchres(struct search *srch, struct srchres *sr, void *uudata)
1706 {
1707     struct uidata *data;
1708
1709     for(data = actives; data != NULL; data = data->next)
1710     {
1711         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1712         {
1713             newnotif(data, 622, NOTIF_ID, srch->id, NOTIF_STR, sr->filename, NOTIF_STR, sr->fnet->name, NOTIF_STR, sr->peerid, NOTIF_INT, sr->size,
1714                      NOTIF_INT, sr->slots, NOTIF_INT, (sr->fn == NULL)?-1:(sr->fn->id), NOTIF_FLOAT, sr->time, NOTIF_STR, (sr->hash == NULL)?L"":unparsehash(sr->hash), NOTIF_END);
1715         }
1716     }
1717     return(0);
1718 }
1719
1720 static int recvchat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *string, void *uudata)
1721 {
1722     struct uidata *data;
1723     
1724     for(data = actives; data != NULL; data = data->next)
1725     {
1726         if(haspriv(data, PERM_CHAT) && data->notify.b.fnchat)
1727             newnotif(data, 600, NOTIF_ID, fn->id, NOTIF_INT, public, NOTIF_STR, name, NOTIF_STR, peer, NOTIF_STR, string, NOTIF_END);
1728     }
1729     return(0);
1730 }
1731
1732 static int fnactive(struct fnetnode *fn, wchar_t *attrib, void *uudata)
1733 {
1734     struct uidata *data;
1735     struct notif *notif;
1736     
1737     if(!wcscmp(attrib, L"state"))
1738     {
1739         for(data = actives; data != NULL; data = data->next)
1740         {
1741             if(data->notify.b.fnact)
1742                 newnotif(data, 601, NOTIF_ID, fn->id, NOTIF_INT, fn->state, NOTIF_END);
1743         }
1744     } else if(!wcscmp(attrib, L"name")) {
1745         for(data = actives; data != NULL; data = data->next)
1746         {
1747             if(data->notify.b.fnact)
1748                 newnotif(data, 602, NOTIF_ID, fn->id, NOTIF_STR, fn->name, NOTIF_END);
1749         }
1750     } else if(!wcscmp(attrib, L"numpeers")) {
1751         for(data = actives; data != NULL; data = data->next)
1752         {
1753             if(data->notify.b.fnact)
1754             {
1755                 if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 605, fn->id)) != NULL)
1756                     notif->argv[1].d.n = fn->numpeers;
1757                 else
1758                     newnotif(data, 605, NOTIF_ID, fn->id, NOTIF_INT, fn->numpeers, NOTIF_END)->rlimit = 0.5;
1759             }
1760         }
1761     }
1762     return(0);
1763 }
1764
1765 static int fnunlink(struct fnetnode *fn, void *uudata)
1766 {
1767     struct uidata *data;
1768     
1769     for(data = actives; data != NULL; data = data->next)
1770     {
1771         if(data->notify.b.fnact)
1772             newnotif(data, 603, NOTIF_ID, fn->id, NOTIF_END);
1773     }
1774     return(0);
1775 }
1776
1777 static int peernew(struct fnetnode *fn, struct fnetpeer *peer, void *uudata)
1778 {
1779     struct uidata *data;
1780     
1781     for(data = actives; data != NULL; data = data->next)
1782     {
1783         if(data->notify.b.fnpeer)
1784             newnotif(data, 630, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_STR, peer->nick, NOTIF_END);
1785     }
1786     return(0);
1787 }
1788
1789 static int peerdel(struct fnetnode *fn, struct fnetpeer *peer, void *uudata)
1790 {
1791     struct uidata *data;
1792     
1793     for(data = actives; data != NULL; data = data->next)
1794     {
1795         if(data->notify.b.fnpeer)
1796             newnotif(data, 631, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_END);
1797     }
1798     return(0);
1799 }
1800
1801 static int peerchange(struct fnetnode *fn, struct fnetpeer *peer, struct fnetpeerdi *di, void *uudata)
1802 {
1803     struct uidata *data;
1804     struct notif *notif;
1805     wchar_t buf[32];
1806     
1807     for(data = actives; data != NULL; data = data->next)
1808     {
1809         if(data->notify.b.fnpeer)
1810         {
1811             for(notif = data->fnotif; notif != NULL; notif = notif->next)
1812             {
1813                 if((notif->code == 632) && (notif->state == NOTIF_PEND) && (notif->argv[0].d.n == fn->id) && !wcscmp(notif->argv[1].d.s, peer->id))
1814                     break;
1815             }
1816             if(notif == NULL)
1817                 notif = newnotif(data, 632, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_STR, peer->nick, NOTIF_END);
1818             notifappend(notif, NOTIF_STR, di->datum->id, NOTIF_INT, di->datum->datatype, NOTIF_END);
1819             switch(di->datum->datatype)
1820             {
1821             case FNPD_INT:
1822                 notifappend(notif, NOTIF_INT, di->data.num, NOTIF_END);
1823                 break;
1824             case FNPD_STR:
1825                 notifappend(notif, NOTIF_STR, di->data.str, NOTIF_END);
1826                 break;
1827             case FNPD_LL:
1828                 swprintf(buf, sizeof(buf) / sizeof(*buf), L"%lli", di->data.lnum);
1829                 notifappend(notif, NOTIF_STR, buf, NOTIF_END);
1830                 break;
1831             }
1832         }
1833     }
1834     return(0);
1835 }
1836
1837 static int newfnetnode(struct fnetnode *fn, void *uudata)
1838 {
1839     struct uidata *data;
1840     
1841     for(data = actives; data != NULL; data = data->next)
1842     {
1843         if(data->notify.b.fnact)
1844             newnotif(data, 604, NOTIF_ID, fn->id, NOTIF_STR, fn->fnet->name, NOTIF_END);
1845     }
1846     CBREG(fn, fnetnode_ac, fnactive, NULL, NULL);
1847     CBREG(fn, fnetnode_chat, recvchat, NULL, NULL);
1848     CBREG(fn, fnetnode_unlink, fnunlink, NULL, NULL);
1849     CBREG(fn, fnetpeer_new, peernew, NULL, NULL);
1850     CBREG(fn, fnetpeer_del, peerdel, NULL, NULL);
1851     CBREG(fn, fnetpeer_chdi, peerchange, NULL, NULL);
1852     return(0);
1853 }
1854
1855 static int transferchattr(struct transfer *transfer, wchar_t *attrib, void *uudata)
1856 {
1857     struct uidata *data;
1858     
1859     if(!wcscmp(attrib, L"state"))
1860     {
1861         for(data = actives; data != NULL; data = data->next)
1862         {
1863             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1864                 newnotif(data, 611, NOTIF_ID, transfer->id, NOTIF_INT, transfer->state, NOTIF_END);
1865         }
1866     } else if(!wcscmp(attrib, L"nick")) {
1867         for(data = actives; data != NULL; data = data->next)
1868         {
1869             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1870                 newnotif(data, 612, NOTIF_ID, transfer->id, NOTIF_STR, transfer->peernick, NOTIF_END);
1871         }
1872     } else if(!wcscmp(attrib, L"size")) {
1873         for(data = actives; data != NULL; data = data->next)
1874         {
1875             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1876                 newnotif(data, 613, NOTIF_ID, transfer->id, NOTIF_INT, transfer->size, NOTIF_END);
1877         }
1878     } else if(!wcscmp(attrib, L"error")) {
1879         for(data = actives; data != NULL; data = data->next)
1880         {
1881             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1882                 newnotif(data, 614, NOTIF_ID, transfer->id, NOTIF_INT, transfer->error, NOTIF_END);
1883         }
1884     } else if(!wcscmp(attrib, L"path")) {
1885         for(data = actives; data != NULL; data = data->next)
1886         {
1887             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1888                 newnotif(data, 616, NOTIF_ID, transfer->id, NOTIF_STR, transfer->path, NOTIF_END);
1889         }
1890     } else if(!wcscmp(attrib, L"hash")) {
1891         for(data = actives; data != NULL; data = data->next)
1892         {
1893             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1894                 newnotif(data, 618, NOTIF_ID, transfer->id, NOTIF_STR, (transfer->hash == NULL)?L"":unparsehash(transfer->hash), NOTIF_END);
1895         }
1896     }
1897     return(0);
1898 }
1899
1900 static int transferprog(struct transfer *transfer, void *uudata)
1901 {
1902     struct uidata *data;
1903     struct notif *notif;
1904     
1905     for(data = actives; data != NULL; data = data->next)
1906     {
1907         if(haspriv(data, PERM_TRANS) && data->notify.b.trprog && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1908         {
1909             if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 615, transfer->id)) != NULL)
1910                 notif->argv[1].d.n = transfer->curpos;
1911             else
1912                 newnotif(data, 615, NOTIF_ID, transfer->id, NOTIF_INT, transfer->curpos, NOTIF_END)->rlimit = 0.5;
1913         }
1914     }
1915     return(0);
1916 }
1917
1918 static int transferdestroyed(struct transfer *transfer, void *uudata)
1919 {
1920     struct uidata *data;
1921     
1922     for(data = actives; data != NULL; data = data->next)
1923     {
1924         if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1925             newnotif(data, 617, NOTIF_ID, transfer->id, NOTIF_END);
1926     }
1927     return(0);
1928 }
1929
1930 static int newtransfernotify(struct transfer *transfer, void *uudata)
1931 {
1932     struct uidata *data;
1933     
1934     for(data = actives; data != NULL; data = data->next)
1935     {
1936         if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1937             newnotif(data, 610, NOTIF_ID, transfer->id, NOTIF_INT, transfer->dir, NOTIF_STR, transfer->peerid, NOTIF_STR, (transfer->path == NULL)?L"":transfer->path, NOTIF_END);
1938     }
1939     CBREG(transfer, trans_ac, transferchattr, NULL, NULL);
1940     CBREG(transfer, trans_p, transferprog, NULL, NULL);
1941     CBREG(transfer, trans_destroy, transferdestroyed, NULL, NULL);
1942     return(0);
1943 }
1944
1945 static struct uiuser *newuser(wchar_t *name, unsigned long perms)
1946 {
1947     struct uiuser *new;
1948     
1949     new = smalloc(sizeof(*new));
1950     new->used = 0;
1951     new->name = swcsdup(name);
1952     new->perms = perms;
1953     new->delete = 0;
1954     new->next = users;
1955     new->prev = NULL;
1956     if(users != NULL)
1957         users->prev = new;
1958     users = new;
1959     return(new);
1960 }
1961
1962 static void freeuser(struct uiuser *user)
1963 {
1964     if(user->next != NULL)
1965         user->next->prev = user->prev;
1966     if(user->prev != NULL)
1967         user->prev->next = user->next;
1968     if(user == users)
1969         users = user->next;
1970     free(user->name);
1971     free(user);
1972 }
1973
1974 static int conf_user(int argc, wchar_t **argv)
1975 {
1976     int i, perms, permmod;
1977     struct uiuser *user;
1978     wchar_t *p;
1979     
1980     if(argc < 3)
1981     {
1982         flog(LOG_WARNING, "not enough arguments given for user command");
1983         return(1);
1984     }
1985     perms = 0;
1986     for(i = 2; i < argc; i++)
1987     {
1988         if(!iswalpha(argv[i][0]))
1989             p = argv[i] + 1;
1990         else
1991             p = argv[i];
1992         if(!wcscmp(p, L"disallow"))
1993             permmod = PERM_DISALLOW;
1994         if(!wcscmp(p, L"admin"))
1995             permmod = PERM_ADMIN;
1996         if(!wcscmp(p, L"fnetctl"))
1997             permmod = PERM_FNETCTL;
1998         if(!wcscmp(p, L"trans"))
1999             permmod = PERM_TRANS;
2000         if(!wcscmp(p, L"transcu"))
2001             permmod = PERM_TRANSCU;
2002         if(!wcscmp(p, L"chat"))
2003             permmod = PERM_CHAT;
2004         if(!wcscmp(p, L"srch"))
2005             permmod = PERM_SRCH;
2006         if(!wcscmp(p, L"all"))
2007             permmod = ~0;
2008         if(argv[i][0] == L'-')
2009             perms &= ~permmod;
2010         else
2011             perms |= permmod;
2012     }
2013     if((user = finduser(argv[1])) == NULL)
2014     {
2015         newuser(argv[1], perms);
2016     } else {
2017         user->delete = 0;
2018         user->perms = perms;
2019     }
2020     return(0);
2021 }
2022
2023 static void preinit(int hup)
2024 {
2025     struct uiuser *user;
2026     
2027     if(!hup)
2028     {
2029         newuser(L"default", 0);
2030     } else {
2031         for(user = users; user != NULL; user = user->next)
2032         {
2033             if(!wcscmp(user->name, L"default"))
2034                 user->delete = 1;
2035         }
2036     }
2037 }
2038
2039 #ifdef HAVE_IPV6
2040 static struct sockaddr *getnameforport(int port, socklen_t *len)
2041 {
2042     static struct sockaddr_in6 addr;
2043     
2044     memset(&addr, 0, sizeof(addr));
2045     addr.sin6_family = AF_INET6;
2046     addr.sin6_port = htons(port);
2047     addr.sin6_addr = in6addr_any;
2048     if(len != NULL)
2049         *len = sizeof(addr);
2050     return((struct sockaddr *)&addr);
2051 }
2052 #else
2053 static struct sockaddr *getnameforport(int port, socklen_t *len)
2054 {
2055     static struct sockaddr_in addr;
2056     
2057     memset(&addr, 0, sizeof(addr));
2058     addr.sin_family = AF_INET;
2059     addr.sin_port = htons(port);
2060     if(len != NULL)
2061         *len = sizeof(addr);
2062     return((struct sockaddr *)&addr);
2063 }
2064 #endif
2065
2066 static int portupdate(struct configvar *var, void *uudata)
2067 {
2068     struct sockaddr *addr;
2069     socklen_t addrlen;
2070     struct socket *newsock;
2071     
2072     addr = getnameforport(var->val.num, &addrlen);
2073     if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL)
2074     {
2075         flog(LOG_WARNING, "could not create new UI socket, reverting to old: %s", strerror(errno));
2076         return(0);
2077     }
2078     if(uisocket != NULL)
2079         putsock(uisocket);
2080     uisocket = newsock;
2081     return(0);
2082 }
2083
2084 static int init(int hup)
2085 {
2086     struct sockaddr *addr;
2087     socklen_t addrlen;
2088     struct uiuser *user, *next;
2089     
2090     if(hup)
2091     {
2092         for(user = users; user != NULL; user = next)
2093         {
2094             next = user->next;
2095             if(user->delete)
2096                 freeuser(user);
2097         }
2098     }
2099     if(!hup)
2100     {
2101         if(uisocket != NULL)
2102             putsock(uisocket);
2103         addr = getnameforport(confgetint("ui", "port"), &addrlen);
2104         if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL)
2105         {
2106             flog(LOG_CRIT, "could not create UI socket: %s", strerror(errno));
2107             return(1);
2108         }
2109         CBREG(confgetvar("ui", "port"), conf_update, portupdate, NULL, NULL);
2110         GCBREG(newfncb, newfnetnode, NULL);
2111         GCBREG(newtransfercb, newtransfernotify, NULL);
2112     }
2113     return(0);
2114 }
2115
2116 static int run(void)
2117 {
2118     int i, id;
2119     struct uidata *data, *next;
2120     struct qcommand *qcmd;
2121     struct notif *notif, *nnotif;
2122     wchar_t buf[64];
2123     
2124     for(data = actives; data != NULL; data = next)
2125     {
2126         next = data->next;
2127         if(data->close)
2128             freeuidata(data);
2129     }
2130     for(data = actives; data != NULL; data = data->next)
2131     {
2132         for(notif = data->fnotif; notif != NULL; notif = nnotif)
2133         {
2134             nnotif = notif->next;
2135             if(notif->state == NOTIF_WAIT)
2136                 continue;
2137             id = -1;
2138             for(i = 0; i < notif->argc; i++)
2139             {
2140                 if(notif->argv[i].dt == NOTIF_ID)
2141                 {
2142                     id = notif->argv[i].d.n;
2143                     break;
2144                 }
2145             }
2146             if(findnotif(notif->prev, 0, -1, notif->code, id) != NULL)
2147                 continue;
2148             sq(data->sk, 2, L"%%i", notif->code, NULL);
2149             for(i = 0; i < notif->argc; i++)
2150             {
2151                 switch(notif->argv[i].dt)
2152                 {
2153                 case NOTIF_INT:
2154                 case NOTIF_ID:
2155                     sq(data->sk, 2, L"%%i", notif->argv[i].d.n, NULL);
2156                     break;
2157                 case NOTIF_STR:
2158                     if(notif->argv[i].d.s[0] == L'%')
2159                         sq(data->sk, 2, L"%%s", notif->argv[i].d.s, NULL);
2160                     else
2161                         sq(data->sk, 2, notif->argv[i].d.s, NULL);
2162                     break;
2163                 case NOTIF_FLOAT:
2164                     swprintf(buf, 64, L"%f", notif->argv[i].d.d);
2165                     sq(data->sk, 2, buf, NULL);
2166                     break;
2167                 }
2168             }
2169             sq(data->sk, 0, NULL);
2170             if(notif->rlimit != 0)
2171             {
2172                 notif->state = NOTIF_WAIT;
2173                 notif->exptimer = timercallback(ntime() + notif->rlimit, (void (*)(int, void *))notifexpire, notif);
2174             } else {
2175                 freenotif(notif);
2176             }
2177         }
2178         if((qcmd = unlinkqcmd(data)) != NULL)
2179         {
2180             qcmd->cmd->handler(data->sk, data, qcmd->argc, qcmd->argv);
2181             freequeuecmd(qcmd);
2182             return(1);
2183         }
2184     }
2185     return(0);
2186 }
2187
2188 static void terminate(void)
2189 {
2190     while(users != NULL)
2191         freeuser(users);
2192 }
2193
2194 static struct configvar myvars[] =
2195 {
2196     {CONF_VAR_BOOL, "onlylocal", {.num = 1}},
2197     {CONF_VAR_INT, "port", {.num = 1500}},
2198     {CONF_VAR_INT, "uitos", {.num = SOCK_TOS_MINDELAY}},
2199     {CONF_VAR_STRING, "filtercmd", {.str = L"dc-filtercmd"}},
2200     {CONF_VAR_END}
2201 };
2202
2203 static struct configcmd mycmds[] =
2204 {
2205     {"user", conf_user},
2206     {NULL}
2207 };
2208
2209 static struct module me =
2210 {
2211     .name = "ui",
2212     .conf =
2213     {
2214         .vars = myvars,
2215         .cmds = mycmds
2216     },
2217     .preinit = preinit,
2218     .init = init,
2219     .run = run,
2220     .terminate = terminate
2221 };
2222
2223 MODULE(me)