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