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