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