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