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