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