Send id along with fn connections.
[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 from %s, but no account existed", data->username, formataddress(sk->remote, sk->remotelen));
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 from %s, but was not authorized", data->username, formataddress(sk->remote, sk->remotelen));
445             logout(data);
446         } else {
447             sq(sk, 0, L"200", L"Welcome", NULL);
448             flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formataddress(sk->remote, sk->remotelen));
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         flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formataddress(sk->remote, sk->remotelen));
454         logout(data);
455         break;
456     case AUTH_PASS:
457         switch(data->auth->prompt)
458         {
459         case AUTH_PR_AUTO:
460             code = 300;
461             break;
462         case AUTH_PR_NOECHO:
463             code = 301;
464             break;
465         case AUTH_PR_ECHO:
466             code = 302;
467             break;
468         case AUTH_PR_INFO:
469             code = 303;
470             break;
471         case AUTH_PR_ERROR:
472             code = 304;
473             break;
474         }
475         sq(sk, 0, L"%%i", code, data->auth->text, NULL);
476         break;
477     case AUTH_ERR:
478         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
479         logout(data);
480         break;
481     default:
482         flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_login");
483         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
484         logout(data);
485         break;
486     }
487 }
488
489 static void cmd_pass(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
490 {
491     char *buf;
492     int code;
493
494     haveargs(2);
495     if((buf = icwcstombs(argv[1], NULL)) == NULL)
496     {
497         sq(sk, 0, L"504", L"Could not convert data to locale charset", NULL);
498         return;
499     }
500     if((data->auth == NULL) || (data->userinfo != NULL))
501     {
502         sq(sk, 0, L"507", L"Data not expected", NULL);
503         return;
504     }
505     switch(authenticate(data->auth, buf))
506     {
507     case AUTH_SUCCESS:
508         data->userinfo = finduser(data->username);
509         if(data->userinfo == NULL)
510             data->userinfo = finduser(L"default");
511         if(data->uid == -1)
512         {
513             sq(sk, 0, L"506", L"Authentication error", NULL);
514             flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formataddress(sk->remote, sk->remotelen));
515             logout(data);
516         } else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) {
517             sq(sk, 0, L"506", L"Authentication error", NULL);
518             flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formataddress(sk->remote, sk->remotelen));
519             logout(data);
520         } else {
521             sq(sk, 0, L"200", L"Welcome", NULL);
522             flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formataddress(sk->remote, sk->remotelen));
523         }
524         break;
525     case AUTH_DENIED:
526         sq(sk, 0, L"506", L"Authentication error", L"%%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
527         flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formataddress(sk->remote, sk->remotelen));
528         logout(data);
529         break;
530     case AUTH_PASS:
531         switch(data->auth->prompt)
532         {
533         case AUTH_PR_AUTO:
534             code = 300;
535             break;
536         case AUTH_PR_NOECHO:
537             code = 301;
538             break;
539         case AUTH_PR_ECHO:
540             code = 302;
541             break;
542         case AUTH_PR_INFO:
543             code = 303;
544             break;
545         case AUTH_PR_ERROR:
546             code = 304;
547             break;
548         }
549         sq(sk, 0, L"%%i", code, data->auth->text, NULL);
550         break;
551     case AUTH_ERR:
552         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
553         logout(data);
554         break;
555     default:
556         flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_pass");
557         sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
558         logout(data);
559         break;
560     }
561     free(buf);
562 }
563
564 static void cmd_fnetconnect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
565 {
566     int i;
567     char *buf;
568     int err;
569     struct fnetnode *fn;
570     struct wcspair *args;
571     
572     haveargs(3);
573     havepriv(PERM_FNETCTL);
574     for(i = 0, fn = fnetnodes; fn != NULL; i++, fn = fn->next);
575     if((confgetint("fnet", "maxnodes") > 0) && (i >= confgetint("fnet", "maxnodes"))) {
576         sq(sk, 0, L"515", L"Too many fnetnodes connected already", NULL);
577         return;
578     }
579     if((buf = icwcstombs(argv[2], NULL)) == NULL)
580     {
581         sq(sk, 0, L"504", L"Could not convert data to locale charset", NULL);
582         return;
583     }
584     args = NULL;
585     for(i = 3; i < argc - 1; i += 2)
586         newwcspair(argv[i], argv[i + 1], &args);
587     fn = fnetinitconnect(argv[1], buf, args);
588     err = errno;
589     free(buf);
590     if(fn == NULL)
591     {
592         if(errno == EPROTONOSUPPORT)
593             sq(sk, 0, L"511", L"No such network name", NULL);
594         else
595             sq(sk, 0, L"509", L"Could not parse the address", L"%%s", strerror(err), NULL);
596         return;
597     }
598     linkfnetnode(fn);
599     fnetsetname(fn, argv[2]);
600     sq(sk, 0, L"200", L"%%i", fn->id, L"Connection under way", NULL);
601     putfnetnode(fn);
602 }
603
604 static void cmd_lsnodes(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
605 {
606     struct fnetnode *fn;
607
608     if(fnetnodes == NULL)
609     {
610         sq(sk, 0, L"201", L"No connected nodes", NULL);
611         return;
612     }
613     for(fn = fnetnodes; fn != NULL; fn = fn->next)
614     {
615         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);
616     }
617 }
618
619 static void cmd_disconnect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
620 {
621     struct fnetnode *fn;
622     int i;
623     
624     haveargs(2);
625     havepriv(PERM_FNETCTL);
626     /* Note - Programmatical user interfaces must only give one
627      * argument per command, the multiple argument form is only for
628      * convenience when manually controlling the daemon via
629      * eg. telnet. The reason is that the return codes aren't clear
630      * enough for the multiple argument form. */
631     for(i = 1; i < argc; i++)
632     {
633         if((fn = findfnetnode(wcstol(argv[i], NULL, 0))) == NULL)
634         {
635             sq(sk, 0, L"510", L"No such node", NULL);
636             return;
637         }
638         killfnetnode(fn);
639         unlinkfnetnode(fn);
640     }
641     sq(sk, 0, L"200", L"Node flagged for disconnection", NULL);
642 }
643
644 static void cmd_lspa(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
645 {
646     struct fnetnode *fn;
647     struct fnetpeerdatum *datum;
648     
649     haveargs(2);
650     if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
651     {
652         sq(sk, 0, L"510", L"No such node", NULL);
653         return;
654     }
655     if(fn->peerdata == NULL)
656     {
657         sq(sk, 0, L"201", L"No data available", NULL);
658     } else {
659         for(datum = fn->peerdata; datum != NULL; datum = datum->next)
660             sq(sk, (datum->next != NULL)?1:0, L"200", datum->id, L"%%i", datum->datatype, NULL);
661     }
662 }
663
664 static void cmd_lspeers(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
665 {
666     int i;
667     struct fnetnode *fn;
668     struct fnetpeer *peer;
669     wchar_t buf[40];
670     
671     haveargs(2);
672     if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
673     {
674         sq(sk, 0, L"510", L"No such node", NULL);
675         return;
676     }
677     if(fn->peers == NULL)
678     {
679         sq(sk, 0, L"201", L"No peers avaiable", NULL);
680     } else {
681         for(peer = fn->peers; peer != NULL; peer = peer->next)
682         {
683             sq(sk, 2 | ((peer->next != NULL)?1:0), L"200", peer->id, peer->nick, NULL);
684             for(i = 0; i < peer->dinum; i++)
685             {
686                 if(peer->peerdi[i].datum->datatype == FNPD_INT)
687                     sq(sk, 2, peer->peerdi[i].datum->id, L"%%i", peer->peerdi[i].data.num, NULL);
688                 /* Note: A long long is not the size of an int, so
689                  * sq() can't handle the conversion itself. */
690                 if(peer->peerdi[i].datum->datatype == FNPD_LL)
691                 {
692                     swprintf(buf, 40, L"%lli", peer->peerdi[i].data.lnum);
693                     sq(sk, 2, peer->peerdi[i].datum->id, buf, NULL);
694                 }
695                 if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
696                     sq(sk, 2, peer->peerdi[i].datum->id, peer->peerdi[i].data.str, NULL);
697             }
698             sq(sk, 0, NULL);
699         }
700     }
701 }
702
703 static void cmd_download(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
704 {
705     int i;
706     struct fnet *net;
707     struct fnetnode *fn;
708     struct transfer *transfer;
709     struct fnetpeer *peer;
710     
711     haveargs(4);
712     if((argc > 5) && ((argc % 2) == 0))
713     {
714         sq(sk, 0, L"501", L"Must have an even number of arguments", NULL);
715         return;
716     }
717     havepriv(PERM_TRANS);
718     if((*(argv[1]) >= L'0') && (*(argv[1]) <= L'9'))
719     {
720         if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
721         {
722             sq(sk, 0, L"510", L"No such node", NULL);
723             return;
724         }
725         net = fn->fnet;
726     } else {
727         fn = NULL;
728         if((net = findfnet(argv[1])) == NULL)
729         {
730             sq(sk, 0, L"511", L"No such network name", NULL);
731             return;
732         }
733     }
734     transfer = newtransfer();
735     authgethandle(transfer->auth = data->auth);
736     transfer->fnet = net;
737     transfer->peerid = swcsdup(argv[2]);
738     transfer->path = swcsdup(argv[3]);
739     transfer->dir = TRNSD_DOWN;
740     transfer->owner = data->uid;
741     if(fn != NULL)
742     {
743         transfer->fn = fn;
744         getfnetnode(fn);
745         linktransfer(transfer);
746         if(((peer = fnetfindpeer(fn, transfer->peerid)) != NULL) && (peer->nick != NULL))
747             transfersetnick(transfer, peer->nick);
748     } else {
749         linktransfer(transfer);
750     }
751     if(argc > 4)
752         transfersetsize(transfer, wcstol(argv[4], NULL, 0));
753     if(argc > 5)
754     {
755         for(i = 5; i < argc; i += 2)
756         {
757             if(!wcscmp(argv[i], L"hash"))
758             {
759                 transfersethash(transfer, parsehash(argv[i + 1]));
760             } else {
761                 newwcspair(argv[i], argv[i + 1], &transfer->args);
762             }
763         }
764     }
765     sq(sk, 0, L"200", L"%%i", transfer->id, L"Download queued", NULL);
766     transfersetactivity(transfer, L"create");
767 }
768
769 static void cmd_lstrans(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
770 {
771     struct transfer *transfer, *pt;
772     
773     havepriv(PERM_TRANS);
774     pt = NULL;
775     for(transfer = transfers; transfer != NULL; transfer = transfer->next)
776     {
777         if((transfer->dir != TRNSD_DOWN) || (transfer->owner == data->uid))
778         {
779             if(pt != NULL)
780                 sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->dir,
781                    L"%%i", pt->state, pt->peerid,
782                    (pt->peernick == NULL)?L"":(pt->peernick),
783                    (pt->path == NULL)?L"":(pt->path),
784                    L"%%i", pt->size, L"%%i", pt->curpos,
785                    (pt->hash == NULL)?L"":unparsehash(pt->hash),
786                    NULL);
787             pt = transfer;
788         }
789     }
790     if(pt == NULL)
791         sq(sk, 0, L"201", L"No transfers", NULL);
792     else
793         sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->dir,
794            L"%%i", pt->state, pt->peerid,
795            (pt->peernick == NULL)?L"":(pt->peernick),
796            (pt->path == NULL)?L"":(pt->path),
797            L"%%i", pt->size, L"%%i", pt->curpos,
798            (pt->hash == NULL)?L"":unparsehash(pt->hash),
799            NULL);
800 }
801
802 static void cmd_cancel(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
803 {
804     struct transfer *transfer;
805     
806     haveargs(2);
807     havepriv(PERM_TRANS);
808     if((transfer = findtransfer(wcstol(argv[1], NULL, 0))) == NULL)
809     {
810         sq(sk, 0, L"512", L"No such transfer", NULL);
811         return;
812     }
813     if((transfer->dir == TRNSD_UP) && !(data->userinfo->perms & PERM_TRANSCU))
814     {
815         sq(sk, 0, L"502", L"You are not allowed to cancel uploads", NULL);
816         return;
817     }
818     if((transfer->dir == TRNSD_DOWN) && (transfer->owner != data->uid))
819     {
820         sq(sk, 0, L"502", L"You do not own that transfer", NULL);
821         return;
822     }
823     transfer->close = 1;
824     sq(sk, 0, L"200", L"Transfer cancelled", NULL);
825 }
826
827 static void cmd_notify(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
828 {
829     int i, val;
830     
831     if((argc % 2) != 1)
832     {
833         sq(sk, 0, L"501", L"Must have an even number of arguments", NULL);
834         return;
835     }
836     for(i = 1; i < argc; i += 2)
837     {
838         if(!wcscasecmp(argv[i + 1], L"on"))
839             val = 1;
840         else
841             val = 0;
842         if(!wcscasecmp(argv[i], L"all"))
843         {
844             if(val)
845                 data->notify.w = ~0;
846             else
847                 data->notify.w = 0;
848         } else if(!wcscasecmp(argv[i], L"fn:chat")) {
849             data->notify.b.fnchat = val;
850         } else if(!wcscasecmp(argv[i], L"fn:act")) {
851             data->notify.b.fnact = val;
852         } else if(!wcscasecmp(argv[i], L"fn:peer")) {
853             data->notify.b.fnpeer = val;
854         } else if(!wcscasecmp(argv[i], L"trans:act")) {
855             data->notify.b.tract = val;
856         } else if(!wcscasecmp(argv[i], L"trans:prog")) {
857             data->notify.b.trprog = val;
858         } else if(!wcscasecmp(argv[i], L"srch:act")) {
859             data->notify.b.srch = val;
860         }
861     }
862     sq(sk, 0, L"200", L"Notification alteration succeeded", NULL);
863 }
864
865 static void cmd_sendchat(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
866 {
867     struct fnetnode *fn;
868     int public;
869     
870     haveargs(5);
871     havepriv(PERM_CHAT);
872     if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
873     {
874         sq(sk, 0, L"510", L"No such node", NULL);
875         return;
876     }
877     public = wcstol(argv[2], NULL, 0);
878     if((public != 0) && (public != 1))
879     {
880         sq(sk, 0, L"509", L"Second argument must be 0 or 1", NULL);
881         return;
882     }
883     if(fn->state != FNN_EST)
884     {
885         sq(sk, 0, L"513", L"Hub is in state FNN_EST", NULL);
886         return;
887     }
888     if(fnetsendchat(fn, public, argv[3], argv[4]))
889     {
890         if(errno == ENOTSUP)
891             sq(sk, 0, L"513", L"This network does not support chatting", NULL);
892         else if(errno == EPERM)
893             sq(sk, 0, L"502", L"This node does not allow you to chat", NULL);
894         else if(errno == EILSEQ)
895             sq(sk, 0, L"504", L"This network could not support all the characters in that message", NULL);
896         else
897             sq(sk, 0, L"505", L"Could not chat", NULL);
898         return;
899     }
900     sq(sk, 0, L"200", L"Chat string sent", NULL);
901 }
902
903 static void cmd_search(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
904 {
905     struct search *srch;
906     struct fnetnode *fn;
907     struct sexpr *sexpr;
908     int i;
909     
910     haveargs(3);
911     havepriv(PERM_SRCH);
912     srch = newsearch(data->username, NULL);
913     for(i = 1; i < argc; i++)
914     {
915         if(!wcscmp(argv[i], L"all"))
916         {
917             for(fn = fnetnodes; fn != NULL; fn = fn->next)
918             {
919                 if(fn->state == FNN_EST)
920                     searchaddfn(srch, fn);
921             }
922             i++;
923             break;
924         } else if(!wcscmp(argv[i], L"prio")) {
925             if(++i == argc)
926             {
927                 sq(sk, 0, L"501", L"No argument to prio", NULL);
928                 freesearch(srch);
929                 return;
930             }
931             srch->prio = wcstol(argv[i], NULL, 0);
932         } else if(iswdigit(*argv[i])) {
933             if((fn = findfnetnode(wcstol(argv[i], NULL, 0))) == NULL)
934             {
935                 sq(sk, 0, L"510", L"No such node", NULL);
936                 freesearch(srch);
937                 return;
938             }
939             searchaddfn(srch, fn);
940         } else {
941             break;
942         }
943     }
944     if(srch->fnl == NULL)
945     {
946         sq(sk, 0, L"501", L"No fnetnodes to search found on line", NULL);
947         freesearch(srch);
948         return;
949     }
950     if(i == argc)
951     {
952         sq(sk, 0, L"501", L"No search expression found on line", NULL);
953         freesearch(srch);
954         return;
955     }
956     if((sexpr = parsesexpr(argc - i, argv + i)) == NULL)
957     {
958         sq(sk, 0, L"509", L"Could not parse search expression", NULL);
959         freesearch(srch);
960         return;
961     }
962     optsexpr(sexpr);
963     getsexpr(srch->sexpr = sexpr);
964     queuesearch(srch);
965     CBREG(srch, search_eta, srcheta, NULL, NULL);
966     CBREG(srch, search_commit, srchcommit, NULL, NULL);
967     CBREG(srch, search_result, srchres, NULL, NULL);
968     sq(sk, 0, L"200", L"%%i", srch->id, L"%%i", srch->eta - time(NULL), NULL);
969     putsexpr(sexpr);
970 }
971
972 static void cmd_lssrch(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
973 {
974     struct search *srch, *pt;
975     time_t now;
976     
977     havepriv(PERM_SRCH);
978     pt = NULL;
979     now = time(NULL);
980     for(srch = searches; srch != NULL; srch = srch->next)
981     {
982         if(!wcscmp(srch->owner, data->username))
983         {
984             if(pt != NULL)
985                 sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL);
986             pt = srch;
987         }
988     }
989     if(pt == NULL)
990         sq(sk, 0, L"201", L"No searches", NULL);
991     else
992         sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL);
993 }
994
995 static void cmd_lssr(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
996 {
997     struct search *srch;
998     struct srchres *sr;
999     wchar_t buf[64];
1000     
1001     haveargs(2);
1002     havepriv(PERM_SRCH);
1003     if((srch = findsearch(wcstol(argv[1], NULL, 0))) == NULL)
1004     {
1005         sq(sk, 0, L"514", L"No such search", NULL);
1006         return;
1007     }
1008     if(srch->results == NULL)
1009     {
1010         sq(sk, 0, L"201", L"No results", NULL);
1011     } else {
1012         for(sr = srch->results; sr != NULL; sr = sr->next)
1013         {
1014             swprintf(buf, 64, L"%f", sr->time);
1015             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);
1016         }
1017     }
1018 }
1019
1020 static void cmd_cansrch(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1021 {
1022     struct search *srch;
1023     int i;
1024     
1025     haveargs(2);
1026     havepriv(PERM_SRCH);
1027     /* Note - Programmatical user interfaces must only give one
1028      * argument per command, the multiple argument form is only for
1029      * convenience when manually controlling the daemon via
1030      * eg. telnet. The reason is that the return codes aren't clear
1031      * enough for the multiple argument form. */
1032     for(i = 1; i < argc; i++)
1033     {
1034         if((srch = findsearch(wcstol(argv[i], NULL, 0))) == NULL)
1035         {
1036             sq(sk, 0, L"514", L"No such search", NULL);
1037             return;
1038         }
1039         freesearch(srch);
1040     }
1041     sq(sk, 0, L"200", L"Search cancelled", NULL);
1042 }
1043
1044 static void fcmdread(struct socket *sk, struct uidata *data)
1045 {
1046     char *buf;
1047     size_t bufsize;
1048     
1049     if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
1050         return;
1051     bufcat(data->fcmdbuf, buf, bufsize);
1052     free(buf);
1053 }
1054
1055 static void fcmderr(struct socket *sk, int err, struct uidata *data)
1056 {
1057     wchar_t *wbuf, *p, *p2;
1058     
1059     if(err)
1060     {
1061         flog(LOG_WARNING, "error occurred on filtercmd pipe socket: %s", strerror(err));
1062         kill(-data->fcmdpid, SIGHUP);
1063         putsock(data->fcmdsk);
1064         data->fcmdsk = NULL;
1065         if(data->fcmdbuf != NULL)
1066         {
1067             free(data->fcmdbuf);
1068             data->fcmdbuf = NULL;
1069         }
1070         data->fcmdbufsize = data->fcmdbufdata = 0;
1071         sq(data->sk, 0, L"505", L"An error occurred on the pipe to the filtercmd", L"%%s", strerror(err), NULL);
1072         return;
1073     }
1074     putsock(data->fcmdsk);
1075     data->fcmdsk = NULL;
1076     data->fcmdpid = 0;
1077     if(data->fcmdbuf == NULL)
1078     {
1079         wbuf = swcsdup(L"");
1080     } else {
1081         addtobuf(data->fcmdbuf, 0);
1082         wbuf = icmbstowcs(data->fcmdbuf, NULL);
1083         free(data->fcmdbuf);
1084     }
1085     data->fcmdbuf = NULL;
1086     data->fcmdbufsize = data->fcmdbufdata = 0;
1087     if(wbuf == NULL)
1088     {
1089         sq(data->sk, 0, L"504", L"Filtercmd sent data which could not be converted from the local charset", NULL);
1090         return;
1091     }
1092     p = wbuf;
1093     for(p2 = wcschr(p, L'\n'); p2 != NULL; p2 = wcschr(p, L'\n'))
1094     {
1095         *(p2++) = L'\0';
1096         sq(data->sk, (*p2 == L'\0')?0:1, L"200", L"%%ls", p, NULL);
1097         p = p2;
1098     }
1099     if(*p == L'\0')
1100     {
1101         if(p == wbuf)
1102             sq(data->sk, 0, L"201", L"No data returned", NULL);
1103     } else {
1104         sq(data->sk, 0, L"200", L"%%ls", p, NULL);
1105     }
1106     free(wbuf);
1107 }
1108
1109 static void cmd_filtercmd(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1110 {
1111     int i;
1112     pid_t pid;
1113     int pipe;
1114     char **cargv, **pp;
1115     char *filtercmd, *argbuf;
1116     size_t cargvsize, cargvdata;
1117     struct passwd *pwent;
1118     
1119     haveargs(2);
1120     havepriv(PERM_TRANS);
1121     if((pwent = getpwuid(data->uid)) == NULL)
1122     {
1123         flog(LOG_WARNING, "no passwd entry for UI user %i", data->uid);
1124         sq(sk, 0, L"505", L"System error - Could not fork session", "Internal error", NULL);
1125         return;
1126     }
1127     if((filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), "dcdl-filtercmd", pwent->pw_dir)) == NULL)
1128     {
1129         flog(LOG_WARNING, "could not find filtercmd executable for user %s", pwent->pw_name);
1130         sq(sk, 0, L"505", L"System error - Could not fork session", L"Could not find filtercmd executable", NULL);
1131         return;
1132     }
1133     cargv = NULL;
1134     cargvsize = cargvdata = 0;
1135     addtobuf(cargv, filtercmd);
1136     for(i = 1; i < argc; i++)
1137     {
1138         if((argbuf = icwcstombs(argv[i], NULL)) == NULL)
1139         {
1140             for(i = 0; i < cargvdata; i++)
1141                 free(cargv[i]);
1142             free(cargv);
1143             sq(sk, 0, L"504", L"%Could not convert argument %i into local character set", i, L"%%s", strerror(errno), NULL);
1144             return;
1145         }
1146         addtobuf(cargv, argbuf);
1147     }
1148     addtobuf(cargv, NULL);
1149     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)
1150     {
1151         flog(LOG_WARNING, "could not fork session in filtercmd: %s", strerror(errno));
1152         sq(sk, 0, L"505", L"System error - Could not fork session", L"%%s", strerror(errno), NULL);
1153         return;
1154     }
1155     if(pid == 0)
1156     {
1157         execv(filtercmd, cargv);
1158         flog(LOG_WARNING, "could not exec filtercmd %s: %s", filtercmd, strerror(errno));
1159         exit(127);
1160     }
1161     for(pp = cargv; *pp; pp++)
1162         free(*pp);
1163     free(cargv);
1164     data->fcmdsk = wrapsock(pipe);
1165     data->fcmdpid = pid;
1166     if(data->fcmdbuf != NULL)
1167     {
1168         free(data->fcmdbuf);
1169         data->fcmdbuf = NULL;
1170     }
1171     data->fcmdbufsize = data->fcmdbufdata = 0;
1172     data->fcmdsk->data = data;
1173     data->fcmdsk->readcb = (void (*)(struct socket *, void *))fcmdread;
1174     data->fcmdsk->errcb = (void (*)(struct socket *, int, void *))fcmderr;
1175 }
1176
1177 static void cmd_lstrarg(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1178 {
1179     struct transfer *transfer;
1180     struct wcspair *ta;
1181     
1182     haveargs(2);
1183     havepriv(PERM_TRANS);
1184     if((transfer = findtransfer(wcstol(argv[1], NULL, 0))) == NULL)
1185     {
1186         sq(sk, 0, L"512", L"No such transfer", NULL);
1187         return;
1188     }
1189     if((transfer->dir == TRNSD_DOWN) && (transfer->owner != data->uid))
1190     {
1191         sq(sk, 0, L"502", L"You do not own that transfer", NULL);
1192         return;
1193     }
1194     if(transfer->args == NULL)
1195     {
1196         sq(sk, 0, L"201", L"Transfer has no arguments", NULL);
1197     } else {
1198         for(ta = transfer->args; ta != NULL; ta = ta->next)
1199             sq(sk, ta->next != NULL, L"200", L"%%ls", ta->key, L"%%ls", ta->val, NULL);
1200     }
1201 }
1202
1203 static void cmd_hashstatus(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1204 {
1205     struct sharecache *node;
1206     int total, hashed;
1207     
1208     total = hashed = 0;
1209     for(node = shareroot->child; node != NULL; node = nextscnode(node))
1210     {
1211         if(node->f.b.type == FILE_REG)
1212         {
1213             total++;
1214             if(node->f.b.hastth)
1215                 hashed++;
1216         }
1217     }
1218     sq(sk, 0, L"200", L"%%i", total, L"tth", L"%%i", hashed, NULL);
1219 }
1220
1221 static void cmd_transstatus(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
1222 {
1223     wchar_t *buf1, *buf2;
1224     
1225     havepriv(PERM_TRANS);
1226     buf1 = swprintf2(L"%lli", bytesdownload);
1227     buf2 = swprintf2(L"%lli", bytesupload);
1228     sq(sk, 0, L"200", L"%%ls", buf1, L"%%ls", buf2, NULL);
1229     free(buf1);
1230     free(buf2);
1231 }
1232
1233 #undef haveargs
1234 #undef havepriv
1235
1236 /*
1237  * Reserved command numbers for nameless commands:
1238  *  0: Issued when a client has connected
1239  *  1: Issued when a named command couldn't be found
1240  */
1241
1242 static struct command commands[] =
1243 {
1244     {NULL, cmd_connect},
1245     {NULL, cmd_notfound},
1246     {L"shutdown", cmd_shutdown},
1247     {L"quit", cmd_quit},
1248     {L"lsauth", cmd_lsauth},
1249     {L"login", cmd_login},
1250     {L"pass", cmd_pass},
1251     {L"cnct", cmd_fnetconnect},
1252     {L"lsnodes", cmd_lsnodes},
1253     {L"dcnct", cmd_disconnect},
1254     {L"lspa", cmd_lspa},
1255     {L"lspeers", cmd_lspeers},
1256     {L"download", cmd_download},
1257     {L"lstrans", cmd_lstrans},
1258     {L"cancel", cmd_cancel},
1259     {L"notify", cmd_notify},
1260     {L"sendchat", cmd_sendchat},
1261     {L"search", cmd_search},
1262     {L"lssrch", cmd_lssrch},
1263     {L"lssr", cmd_lssr},
1264     {L"cansrch", cmd_cansrch},
1265     {L"filtercmd", cmd_filtercmd},
1266     {L"lstrarg", cmd_lstrarg},
1267     {L"hashstatus", cmd_hashstatus},
1268     {L"transstatus", cmd_transstatus},
1269     {NULL, NULL}
1270 };
1271
1272 static void freequeuecmd(struct qcommand *qcmd)
1273 {
1274     int i;
1275     
1276     if(qcmd->argv != NULL)
1277     {
1278         for(i = 0; i < qcmd->argc; i++)
1279             free(qcmd->argv[i]);
1280         free(qcmd->argv);
1281     }
1282     free(qcmd);
1283 }
1284
1285 static struct qcommand *unlinkqcmd(struct uidata *data)
1286 {
1287     struct qcommand *qcmd;
1288     
1289     qcmd = data->queue;
1290     if(qcmd != NULL)
1291     {
1292         data->queue = qcmd->next;
1293         if(qcmd == data->queuelast)
1294             data->queuelast = qcmd->next;
1295     }
1296     return(qcmd);
1297 }
1298
1299 static void notifappendv(struct notif *notif, va_list args)
1300 {
1301     int dt, ca;
1302     
1303     while((dt = va_arg(args, int)) != NOTIF_END)
1304     {
1305         ca = notif->argc;
1306         notif->argv = realloc(notif->argv, sizeof(*notif->argv) * ++notif->argc);
1307         notif->argv[ca].dt = dt;
1308         switch(dt)
1309         {
1310         case NOTIF_INT:
1311         case NOTIF_ID:
1312             notif->argv[ca].d.n = va_arg(args, int);
1313             break;
1314         case NOTIF_STR:
1315             notif->argv[ca].d.s = wcsdup(va_arg(args, wchar_t *));
1316             break;
1317         case NOTIF_FLOAT:
1318             notif->argv[ca].d.d = va_arg(args, double);
1319             break;
1320         }
1321     }
1322 }
1323
1324 static void notifappend(struct notif *notif, ...)
1325 {
1326     va_list args;
1327     
1328     va_start(args, notif);
1329     notifappendv(notif, args);
1330     va_end(args);
1331 }
1332
1333 static struct notif *newnotif(struct uidata *data, int code, ...)
1334 {
1335     struct notif *notif;
1336     va_list args;
1337     
1338     notif = smalloc(sizeof(*notif));
1339     memset(notif, 0, sizeof(*notif));
1340     notif->rlimit = 0.0;
1341     notif->ui = data;
1342     notif->code = code;
1343     va_start(args, code);
1344     notifappendv(notif, args);
1345     va_end(args);
1346     notif->next = NULL;
1347     notif->prev = data->lnotif;
1348     if(data->lnotif != NULL)
1349         data->lnotif->next = notif;
1350     else
1351         data->fnotif = notif;
1352     data->lnotif = notif;
1353     return(notif);
1354 }
1355
1356 static void freenotif(struct notif *notif)
1357 {
1358     int i;
1359     
1360     if(notif->next != NULL)
1361         notif->next->prev = notif->prev;
1362     if(notif->prev != NULL)
1363         notif->prev->next = notif->next;
1364     if(notif == notif->ui->fnotif)
1365         notif->ui->fnotif = notif->next;
1366     if(notif == notif->ui->lnotif)
1367         notif->ui->lnotif = notif->prev;
1368     if(notif->exptimer != NULL)
1369         canceltimer(notif->exptimer);
1370     for(i = 0; i < notif->argc; i++)
1371     {
1372         if(notif->argv[i].dt == NOTIF_STR)
1373             free(notif->argv[i].d.s);
1374     }
1375     if(notif->argv != NULL)
1376         free(notif->argv);
1377     free(notif);
1378 }
1379
1380 static void notifexpire(int cancelled, struct notif *notif)
1381 {
1382     notif->exptimer = NULL;
1383     if(!cancelled)
1384         freenotif(notif);
1385 }
1386
1387 static struct notif *findnotif(struct notif *notif, int dir, int state, int code, int id)
1388 {
1389     int i, cont;
1390     
1391     for(; notif != NULL; notif = (dir?notif->next:notif->prev))
1392     {
1393         if((notif->code == code) && ((state < 0) || (state == notif->state)))
1394         {
1395             cont = 0;
1396             if(id >= 0)
1397             {
1398                 for(i = 0; i < notif->argc; i++)
1399                 {
1400                     if((notif->argv[i].dt == NOTIF_ID) && (notif->argv[i].d.n != id))
1401                     {
1402                         cont = 1;
1403                         break;
1404                     }
1405                 }
1406             }
1407             if(cont)
1408                 continue;
1409             break;
1410         }
1411     }
1412     return(notif);
1413 }
1414
1415 static void freeuidata(struct uidata *data)
1416 {
1417     int i;
1418     struct qcommand *qcmd;
1419     
1420     if(data->next != NULL)
1421         data->next->prev = data->prev;
1422     if(data->prev != NULL)
1423         data->prev->next = data->next;
1424     if(data == actives)
1425         actives = data->next;
1426     data->sk->readcb = NULL;
1427     data->sk->errcb = NULL;
1428     putsock(data->sk);
1429     while((qcmd = unlinkqcmd(data)) != NULL)
1430         freequeuecmd(qcmd);
1431     iconv_close(data->ichandle);
1432     if(data->cw != NULL)
1433         free(data->cw);
1434     if(data->cb != NULL)
1435         free(data->cb);
1436     if(data->argv != NULL)
1437     {
1438         for(i = 0; i < data->argc; i++)
1439             free(data->argv[i]);
1440         free(data->argv);
1441     }
1442     if(data->auth != NULL)
1443         authputhandle(data->auth);
1444     if(data->username != NULL)
1445     {
1446         if(data->userinfo != NULL)
1447             flog(LOG_INFO, "%ls logged out", data->username);
1448         free(data->username);
1449     }
1450     free(data->inbuf);
1451     while(data->fnotif != NULL)
1452         freenotif(data->fnotif);
1453     if(data->fcmdbuf != NULL)
1454         free(data->fcmdbuf);
1455     if(data->fcmdpid != 0)
1456         kill(-data->fcmdpid, SIGHUP);
1457     if(data->fcmdsk != NULL)
1458         putsock(data->fcmdsk);
1459     free(data);
1460 }
1461
1462 static void queuecmd(struct uidata *data, struct command *cmd, int argc, wchar_t **argv)
1463 {
1464     struct qcommand *new;
1465     
1466     new = smalloc(sizeof(*new));
1467     new->cmd = cmd;
1468     new->argc = argc;
1469     new->argv = argv;
1470     new->next = NULL;
1471     if(data->queuelast != NULL)
1472         data->queuelast->next = new;
1473     data->queuelast = new;
1474     if(data->queue == NULL)
1475         data->queue = new;
1476 }
1477
1478 static struct uidata *newuidata(struct socket *sk)
1479 {
1480     struct uidata *data;
1481     
1482     data = smalloc(sizeof(*data));
1483     memset(data, 0, sizeof(*data));
1484     data->sk = sk;
1485     getsock(sk);
1486     data->inbuf = smalloc(1024);
1487     data->uid = -1;
1488     if((data->ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
1489     {
1490         flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
1491         return(NULL);
1492     }
1493     data->next = actives;
1494     data->prev = NULL;
1495     if(actives != NULL)
1496         actives->prev = data;
1497     actives = data;
1498     return(data);
1499 }
1500
1501 static void uiread(struct socket *sk, struct uidata *data)
1502 {
1503     int ret, done;
1504     char *newbuf;
1505     char *p1, *p2;
1506     wchar_t *porig;
1507     size_t datalen, len2;
1508     struct command *cur;
1509     
1510     if(data->indata > 1024)
1511         data->indata = 0;
1512     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
1513         return;
1514     sizebuf(&data->inbuf, &data->inbufsize, data->indata + datalen, 1, 1);
1515     memcpy(data->inbuf + data->indata, newbuf, datalen);
1516     free(newbuf);
1517     data->indata += datalen;
1518     if(data->cb == NULL)
1519     {
1520         data->cb = smalloc(sizeof(wchar_t) * (data->cbsize = 64));
1521         data->cbdata = 0;
1522         data->pp = data->cb;
1523     }
1524     done = 0;
1525     while(!done)
1526     {
1527         if(data->cbsize == data->cbdata)
1528         {
1529             len2 = data->pp - data->cb;
1530             data->cb = srealloc(data->cb, sizeof(wchar_t) * (data->cbsize *= 2));
1531             data->pp = data->cb + len2;
1532         }
1533         p1 = data->inbuf;
1534         p2 = (char *)(porig = (data->cb + data->cbdata));
1535         len2 = sizeof(wchar_t) * (data->cbsize - data->cbdata);
1536         ret = iconv(data->ichandle, &p1, &data->indata, &p2, &len2);
1537         memmove(data->inbuf, p1, data->indata);
1538         /* Just a sanity check */
1539         if(((p2 - ((char *)data->cb)) % sizeof(wchar_t)) != 0)
1540         {
1541             flog(LOG_CRIT, "Aiya! iconv does strange things to our wchar_t's!");
1542             abort();
1543         }
1544         data->cbdata += (((wchar_t *)p2) - porig);
1545         if(ret < 0)
1546         {
1547             switch(errno)
1548             {
1549             case EILSEQ:
1550                 /* XXX: Should this really just ignore it? */
1551                 data->indata = 0;
1552                 done = 1;
1553                 break;
1554             case EINVAL:
1555                 done = 1;
1556                 break;
1557             case E2BIG:
1558                 /* Just a sanity check */
1559                 if(data->cbsize != data->cbdata)
1560                 {
1561                     flog(LOG_CRIT, "Aiya! iconv doesn't give us wchar_t's!");
1562                     abort();
1563                 }
1564                 break;
1565             default:
1566                 flog(LOG_WARNING, "bug: strange error from iconv in uiread: %s", strerror(errno));
1567                 break;
1568             }
1569         } else {
1570             done = 1;
1571         }
1572     }
1573     done = 0;
1574     while(!done && (data->pp - data->cb < data->cbdata))
1575     {
1576         switch(data->ps)
1577         {
1578         case 0:
1579             if(iswspace(*data->pp))
1580             {
1581                 if(*data->pp == L'\r')
1582                 {
1583                     if(data->pp == data->cb + data->cbdata - 1)
1584                     {
1585                         done = 1;
1586                         break;
1587                     }
1588                     if(*(++data->pp) == L'\n')
1589                     {
1590                         if((data->argv != NULL) && (data->argv[0] != NULL))
1591                         {
1592                             for(cur = commands; cur->handler != NULL; cur++)
1593                             {
1594                                 if(cur->name == NULL)
1595                                     continue;
1596                                 if(!wcscasecmp(cur->name, data->argv[0]))
1597                                 {
1598                                     queuecmd(data, cur, data->argc, data->argv);
1599                                     break;
1600                                 }
1601                             }
1602                             if(cur->handler == NULL)
1603                                 queuecmd(data, &commands[1], data->argc, data->argv);
1604                         } else {
1605                             queuecmd(data, &commands[1], data->argc, data->argv);
1606                         }
1607                         data->argv = NULL;
1608                         data->args = 0;
1609                         data->argc = 0;
1610                         wmemmove(data->cb, data->pp, data->cbdata -= (data->pp - data->cb));
1611                         data->pp = data->cb;
1612                     } else {
1613                         data->pp++;
1614                     }
1615                 } else {
1616                     data->pp++;
1617                 }
1618             } else {
1619                 data->ps = 1;
1620                 data->cwdata = 0;
1621             }
1622             break;
1623         case 1:
1624             if(iswspace(*data->pp))
1625             {
1626                 addtobuf(data->cw, L'\0');
1627                 sizebuf(&data->argv, &data->args, data->argc + 1, sizeof(*data->argv), 1);
1628                 data->argv[data->argc++] = data->cw;
1629                 data->cw = NULL;
1630                 data->cwsize = 0;
1631                 data->cwdata = 0;
1632                 data->ps = 0;
1633             } else if(*data->pp == L'\"') {
1634                 data->ps = 2;
1635                 data->pp++;
1636             } else if(*data->pp == L'\\') {
1637                 if(data->pp == data->cb + data->cbdata - 1)
1638                 {
1639                     done = 1;
1640                     break;
1641                 }
1642                 addtobuf(data->cw, *(++data->pp));
1643                 data->pp++;
1644             } else {
1645                 addtobuf(data->cw, *(data->pp++));
1646             }
1647             break;
1648         case 2:
1649             if(*data->pp == L'\"') 
1650             {
1651                 data->ps = 1;
1652             } else if(*data->pp == L'\\') {
1653                 if(data->pp == data->cb + data->cbdata - 1)
1654                 {
1655                     done = 1;
1656                     break;
1657                 }
1658                 addtobuf(data->cw, *(++(data->pp)));
1659             } else {
1660                 addtobuf(data->cw, *data->pp);
1661             }
1662             data->pp++;
1663             break;
1664         }
1665     }
1666 }
1667
1668 static void uierror(struct socket *sk, int err, struct uidata *data)
1669 {
1670     if(err)
1671         flog(LOG_WARNING, "error occurred on UI socket: %s", strerror(err));
1672     freeuidata(data);
1673 }
1674
1675 static void uiaccept(struct socket *sk, struct socket *newsk, void *data)
1676 {
1677     struct uidata *uidata;
1678     
1679     newsk->data = uidata = newuidata(newsk);
1680     socksettos(newsk, confgetint("ui", "uitos"));
1681     if(uidata == NULL)
1682         return;
1683     newsk->errcb = (void (*)(struct socket *, int, void *))uierror;
1684     newsk->readcb = (void (*)(struct socket *, void *))uiread;
1685     queuecmd(uidata, &commands[0], 0, NULL);
1686 }
1687
1688 static int srcheta(struct search *srch, void *uudata)
1689 {
1690     struct uidata *data;
1691     
1692     for(data = actives; data != NULL; data = data->next)
1693     {
1694         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1695             newnotif(data, 620, NOTIF_ID, srch->id, NOTIF_INT, srch->eta - time(NULL), NOTIF_END);
1696     }
1697     return(0);
1698 }
1699
1700 static int srchcommit(struct search *srch, void *uudata)
1701 {
1702     struct uidata *data;
1703
1704     for(data = actives; data != NULL; data = data->next)
1705     {
1706         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1707             newnotif(data, 621, NOTIF_ID, srch->id, NOTIF_END);
1708     }
1709     return(0);
1710 }
1711
1712 static int srchres(struct search *srch, struct srchres *sr, void *uudata)
1713 {
1714     struct uidata *data;
1715
1716     for(data = actives; data != NULL; data = data->next)
1717     {
1718         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1719         {
1720             newnotif(data, 622, NOTIF_ID, srch->id, NOTIF_STR, sr->filename, NOTIF_STR, sr->fnet->name, NOTIF_STR, sr->peerid, NOTIF_INT, sr->size,
1721                      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);
1722         }
1723     }
1724     return(0);
1725 }
1726
1727 static int recvchat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *string, void *uudata)
1728 {
1729     struct uidata *data;
1730     
1731     for(data = actives; data != NULL; data = data->next)
1732     {
1733         if(haspriv(data, PERM_CHAT) && data->notify.b.fnchat)
1734             newnotif(data, 600, NOTIF_ID, fn->id, NOTIF_INT, public, NOTIF_STR, name, NOTIF_STR, peer, NOTIF_STR, string, NOTIF_END);
1735     }
1736     return(0);
1737 }
1738
1739 static int fnactive(struct fnetnode *fn, wchar_t *attrib, void *uudata)
1740 {
1741     struct uidata *data;
1742     struct notif *notif;
1743     
1744     if(!wcscmp(attrib, L"state"))
1745     {
1746         for(data = actives; data != NULL; data = data->next)
1747         {
1748             if(data->notify.b.fnact)
1749                 newnotif(data, 601, NOTIF_ID, fn->id, NOTIF_INT, fn->state, NOTIF_END);
1750         }
1751     } else if(!wcscmp(attrib, L"name")) {
1752         for(data = actives; data != NULL; data = data->next)
1753         {
1754             if(data->notify.b.fnact)
1755                 newnotif(data, 602, NOTIF_ID, fn->id, NOTIF_STR, fn->name, NOTIF_END);
1756         }
1757     } else if(!wcscmp(attrib, L"numpeers")) {
1758         for(data = actives; data != NULL; data = data->next)
1759         {
1760             if(data->notify.b.fnact)
1761             {
1762                 if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 605, fn->id)) != NULL)
1763                     notif->argv[1].d.n = fn->numpeers;
1764                 else
1765                     newnotif(data, 605, NOTIF_ID, fn->id, NOTIF_INT, fn->numpeers, NOTIF_END)->rlimit = 0.5;
1766             }
1767         }
1768     }
1769     return(0);
1770 }
1771
1772 static int fnunlink(struct fnetnode *fn, void *uudata)
1773 {
1774     struct uidata *data;
1775     
1776     for(data = actives; data != NULL; data = data->next)
1777     {
1778         if(data->notify.b.fnact)
1779             newnotif(data, 603, NOTIF_ID, fn->id, NOTIF_END);
1780     }
1781     return(0);
1782 }
1783
1784 static int peernew(struct fnetnode *fn, struct fnetpeer *peer, void *uudata)
1785 {
1786     struct uidata *data;
1787     
1788     for(data = actives; data != NULL; data = data->next)
1789     {
1790         if(data->notify.b.fnpeer)
1791             newnotif(data, 630, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_STR, peer->nick, NOTIF_END);
1792     }
1793     return(0);
1794 }
1795
1796 static int peerdel(struct fnetnode *fn, struct fnetpeer *peer, void *uudata)
1797 {
1798     struct uidata *data;
1799     
1800     for(data = actives; data != NULL; data = data->next)
1801     {
1802         if(data->notify.b.fnpeer)
1803             newnotif(data, 631, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_END);
1804     }
1805     return(0);
1806 }
1807
1808 static int peerchange(struct fnetnode *fn, struct fnetpeer *peer, struct fnetpeerdi *di, void *uudata)
1809 {
1810     struct uidata *data;
1811     struct notif *notif;
1812     wchar_t buf[32];
1813     
1814     for(data = actives; data != NULL; data = data->next)
1815     {
1816         if(data->notify.b.fnpeer)
1817         {
1818             for(notif = data->fnotif; notif != NULL; notif = notif->next)
1819             {
1820                 if((notif->code == 632) && (notif->state == NOTIF_PEND) && (notif->argv[0].d.n == fn->id) && !wcscmp(notif->argv[1].d.s, peer->id))
1821                     break;
1822             }
1823             if(notif == NULL)
1824                 notif = newnotif(data, 632, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_STR, peer->nick, NOTIF_END);
1825             notifappend(notif, NOTIF_STR, di->datum->id, NOTIF_INT, di->datum->datatype, NOTIF_END);
1826             switch(di->datum->datatype)
1827             {
1828             case FNPD_INT:
1829                 notifappend(notif, NOTIF_INT, di->data.num, NOTIF_END);
1830                 break;
1831             case FNPD_STR:
1832                 notifappend(notif, NOTIF_STR, di->data.str, NOTIF_END);
1833                 break;
1834             case FNPD_LL:
1835                 swprintf(buf, sizeof(buf) / sizeof(*buf), L"%lli", di->data.lnum);
1836                 notifappend(notif, NOTIF_STR, buf, NOTIF_END);
1837                 break;
1838             }
1839         }
1840     }
1841     return(0);
1842 }
1843
1844 static int newfnetnode(struct fnetnode *fn, void *uudata)
1845 {
1846     struct uidata *data;
1847     
1848     for(data = actives; data != NULL; data = data->next)
1849     {
1850         if(data->notify.b.fnact)
1851             newnotif(data, 604, NOTIF_ID, fn->id, NOTIF_STR, fn->fnet->name, NOTIF_END);
1852     }
1853     CBREG(fn, fnetnode_ac, fnactive, NULL, NULL);
1854     CBREG(fn, fnetnode_chat, recvchat, NULL, NULL);
1855     CBREG(fn, fnetnode_unlink, fnunlink, NULL, NULL);
1856     CBREG(fn, fnetpeer_new, peernew, NULL, NULL);
1857     CBREG(fn, fnetpeer_del, peerdel, NULL, NULL);
1858     CBREG(fn, fnetpeer_chdi, peerchange, NULL, NULL);
1859     return(0);
1860 }
1861
1862 static int transferchattr(struct transfer *transfer, wchar_t *attrib, void *uudata)
1863 {
1864     struct uidata *data;
1865     
1866     if(!wcscmp(attrib, L"state"))
1867     {
1868         for(data = actives; data != NULL; data = data->next)
1869         {
1870             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1871                 newnotif(data, 611, NOTIF_ID, transfer->id, NOTIF_INT, transfer->state, NOTIF_END);
1872         }
1873     } else if(!wcscmp(attrib, L"nick")) {
1874         for(data = actives; data != NULL; data = data->next)
1875         {
1876             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1877                 newnotif(data, 612, NOTIF_ID, transfer->id, NOTIF_STR, transfer->peernick, NOTIF_END);
1878         }
1879     } else if(!wcscmp(attrib, L"size")) {
1880         for(data = actives; data != NULL; data = data->next)
1881         {
1882             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1883                 newnotif(data, 613, NOTIF_ID, transfer->id, NOTIF_INT, transfer->size, NOTIF_END);
1884         }
1885     } else if(!wcscmp(attrib, L"error")) {
1886         for(data = actives; data != NULL; data = data->next)
1887         {
1888             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1889                 newnotif(data, 614, NOTIF_ID, transfer->id, NOTIF_INT, transfer->error, NOTIF_END);
1890         }
1891     } else if(!wcscmp(attrib, L"path")) {
1892         for(data = actives; data != NULL; data = data->next)
1893         {
1894             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1895                 newnotif(data, 616, NOTIF_ID, transfer->id, NOTIF_STR, transfer->path, NOTIF_END);
1896         }
1897     } else if(!wcscmp(attrib, L"hash")) {
1898         for(data = actives; data != NULL; data = data->next)
1899         {
1900             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1901                 newnotif(data, 618, NOTIF_ID, transfer->id, NOTIF_STR, (transfer->hash == NULL)?L"":unparsehash(transfer->hash), NOTIF_END);
1902         }
1903     }
1904     return(0);
1905 }
1906
1907 static int transferprog(struct transfer *transfer, void *uudata)
1908 {
1909     struct uidata *data;
1910     struct notif *notif;
1911     
1912     for(data = actives; data != NULL; data = data->next)
1913     {
1914         if(haspriv(data, PERM_TRANS) && data->notify.b.trprog && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1915         {
1916             if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 615, transfer->id)) != NULL)
1917                 notif->argv[1].d.n = transfer->curpos;
1918             else
1919                 newnotif(data, 615, NOTIF_ID, transfer->id, NOTIF_INT, transfer->curpos, NOTIF_END)->rlimit = 0.5;
1920         }
1921     }
1922     return(0);
1923 }
1924
1925 static int transferdestroyed(struct transfer *transfer, void *uudata)
1926 {
1927     struct uidata *data;
1928     
1929     for(data = actives; data != NULL; data = data->next)
1930     {
1931         if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1932             newnotif(data, 617, NOTIF_ID, transfer->id, NOTIF_STR, (transfer->exitstatus == NULL)?L"":(transfer->exitstatus), NOTIF_END);
1933     }
1934     return(0);
1935 }
1936
1937 static int newtransfernotify(struct transfer *transfer, void *uudata)
1938 {
1939     struct uidata *data;
1940     
1941     for(data = actives; data != NULL; data = data->next)
1942     {
1943         if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
1944             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);
1945     }
1946     CBREG(transfer, trans_ac, transferchattr, NULL, NULL);
1947     CBREG(transfer, trans_p, transferprog, NULL, NULL);
1948     CBREG(transfer, trans_destroy, transferdestroyed, NULL, NULL);
1949     return(0);
1950 }
1951
1952 static struct uiuser *newuser(wchar_t *name, unsigned long perms)
1953 {
1954     struct uiuser *new;
1955     
1956     new = smalloc(sizeof(*new));
1957     new->used = 0;
1958     new->name = swcsdup(name);
1959     new->perms = perms;
1960     new->delete = 0;
1961     new->next = users;
1962     new->prev = NULL;
1963     if(users != NULL)
1964         users->prev = new;
1965     users = new;
1966     return(new);
1967 }
1968
1969 static void freeuser(struct uiuser *user)
1970 {
1971     if(user->next != NULL)
1972         user->next->prev = user->prev;
1973     if(user->prev != NULL)
1974         user->prev->next = user->next;
1975     if(user == users)
1976         users = user->next;
1977     free(user->name);
1978     free(user);
1979 }
1980
1981 static int conf_user(int argc, wchar_t **argv)
1982 {
1983     int i, perms, permmod;
1984     struct uiuser *user;
1985     wchar_t *p;
1986     
1987     if(argc < 3)
1988     {
1989         flog(LOG_WARNING, "not enough arguments given for user command");
1990         return(1);
1991     }
1992     perms = 0;
1993     for(i = 2; i < argc; i++)
1994     {
1995         if(!iswalpha(argv[i][0]))
1996             p = argv[i] + 1;
1997         else
1998             p = argv[i];
1999         if(!wcscmp(p, L"disallow"))
2000             permmod = PERM_DISALLOW;
2001         if(!wcscmp(p, L"admin"))
2002             permmod = PERM_ADMIN;
2003         if(!wcscmp(p, L"fnetctl"))
2004             permmod = PERM_FNETCTL;
2005         if(!wcscmp(p, L"trans"))
2006             permmod = PERM_TRANS;
2007         if(!wcscmp(p, L"transcu"))
2008             permmod = PERM_TRANSCU;
2009         if(!wcscmp(p, L"chat"))
2010             permmod = PERM_CHAT;
2011         if(!wcscmp(p, L"srch"))
2012             permmod = PERM_SRCH;
2013         if(!wcscmp(p, L"all"))
2014             permmod = ~0;
2015         if(argv[i][0] == L'-')
2016             perms &= ~permmod;
2017         else
2018             perms |= permmod;
2019     }
2020     if((user = finduser(argv[1])) == NULL)
2021     {
2022         newuser(argv[1], perms);
2023     } else {
2024         user->delete = 0;
2025         user->perms = perms;
2026     }
2027     return(0);
2028 }
2029
2030 static void preinit(int hup)
2031 {
2032     struct uiuser *user;
2033     
2034     if(!hup)
2035     {
2036         newuser(L"default", 0);
2037     } else {
2038         for(user = users; user != NULL; user = user->next)
2039         {
2040             if(!wcscmp(user->name, L"default"))
2041                 user->delete = 1;
2042         }
2043     }
2044 }
2045
2046 static int portupdate(struct configvar *var, void *uudata)
2047 {
2048     struct socket *newsock;
2049     
2050     if((uisocket = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL)
2051     {
2052         flog(LOG_WARNING, "could not create new UI socket, reverting to old: %s", strerror(errno));
2053         return(0);
2054     }
2055     if(uisocket != NULL)
2056         putsock(uisocket);
2057     uisocket = newsock;
2058     return(0);
2059 }
2060
2061 static int init(int hup)
2062 {
2063     struct uiuser *user, *next;
2064     
2065     if(hup)
2066     {
2067         for(user = users; user != NULL; user = next)
2068         {
2069             next = user->next;
2070             if(user->delete)
2071                 freeuser(user);
2072         }
2073     }
2074     if(!hup)
2075     {
2076         if(uisocket != NULL)
2077             putsock(uisocket);
2078         if((uisocket = netcstcplisten(confgetint("ui", "port"), 1, uiaccept, NULL)) == NULL)
2079         {
2080             flog(LOG_CRIT, "could not create UI socket: %s", strerror(errno));
2081             return(1);
2082         }
2083         CBREG(confgetvar("ui", "port"), conf_update, portupdate, NULL, NULL);
2084         GCBREG(newfncb, newfnetnode, NULL);
2085         GCBREG(newtransfercb, newtransfernotify, NULL);
2086     }
2087     return(0);
2088 }
2089
2090 static int run(void)
2091 {
2092     int i, id;
2093     struct uidata *data, *next;
2094     struct qcommand *qcmd;
2095     struct notif *notif, *nnotif;
2096     wchar_t buf[64];
2097     
2098     for(data = actives; data != NULL; data = next)
2099     {
2100         next = data->next;
2101         if(data->close)
2102             freeuidata(data);
2103     }
2104     for(data = actives; data != NULL; data = data->next)
2105     {
2106         for(notif = data->fnotif; notif != NULL; notif = nnotif)
2107         {
2108             nnotif = notif->next;
2109             if(notif->state == NOTIF_WAIT)
2110                 continue;
2111             id = -1;
2112             for(i = 0; i < notif->argc; i++)
2113             {
2114                 if(notif->argv[i].dt == NOTIF_ID)
2115                 {
2116                     id = notif->argv[i].d.n;
2117                     break;
2118                 }
2119             }
2120             if(findnotif(notif->prev, 0, -1, notif->code, id) != NULL)
2121                 continue;
2122             sq(data->sk, 2, L"%%i", notif->code, NULL);
2123             for(i = 0; i < notif->argc; i++)
2124             {
2125                 switch(notif->argv[i].dt)
2126                 {
2127                 case NOTIF_INT:
2128                 case NOTIF_ID:
2129                     sq(data->sk, 2, L"%%i", notif->argv[i].d.n, NULL);
2130                     break;
2131                 case NOTIF_STR:
2132                     if(notif->argv[i].d.s[0] == L'%')
2133                         sq(data->sk, 2, L"%%s", notif->argv[i].d.s, NULL);
2134                     else
2135                         sq(data->sk, 2, notif->argv[i].d.s, NULL);
2136                     break;
2137                 case NOTIF_FLOAT:
2138                     swprintf(buf, 64, L"%f", notif->argv[i].d.d);
2139                     sq(data->sk, 2, buf, NULL);
2140                     break;
2141                 }
2142             }
2143             sq(data->sk, 0, NULL);
2144             if(notif->rlimit != 0)
2145             {
2146                 notif->state = NOTIF_WAIT;
2147                 notif->exptimer = timercallback(ntime() + notif->rlimit, (void (*)(int, void *))notifexpire, notif);
2148             } else {
2149                 freenotif(notif);
2150             }
2151         }
2152         if((qcmd = unlinkqcmd(data)) != NULL)
2153         {
2154             qcmd->cmd->handler(data->sk, data, qcmd->argc, qcmd->argv);
2155             freequeuecmd(qcmd);
2156             return(1);
2157         }
2158     }
2159     return(0);
2160 }
2161
2162 static void terminate(void)
2163 {
2164     while(users != NULL)
2165         freeuser(users);
2166 }
2167
2168 static struct configvar myvars[] =
2169 {
2170     {CONF_VAR_BOOL, "onlylocal", {.num = 1}},
2171     {CONF_VAR_INT, "port", {.num = 1500}},
2172     {CONF_VAR_INT, "uitos", {.num = SOCK_TOS_MINDELAY}},
2173     {CONF_VAR_STRING, "filtercmd", {.str = L"dc-filtercmd"}},
2174     {CONF_VAR_END}
2175 };
2176
2177 static struct configcmd mycmds[] =
2178 {
2179     {"user", conf_user},
2180     {NULL}
2181 };
2182
2183 static struct module me =
2184 {
2185     .name = "ui",
2186     .conf =
2187     {
2188         .vars = myvars,
2189         .cmds = mycmds
2190     },
2191     .preinit = preinit,
2192     .init = init,
2193     .run = run,
2194     .terminate = terminate
2195 };
2196
2197 MODULE(me)