Close UI sockets immediately from freeuidata.
[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     closesock(data->sk);
1552     putsock(data->sk);
1553     while((qcmd = unlinkqcmd(data)) != NULL)
1554         freequeuecmd(qcmd);
1555     iconv_close(data->ichandle);
1556     if(data->cw != NULL)
1557         free(data->cw);
1558     if(data->cb != NULL)
1559         free(data->cb);
1560     if(data->argv != NULL)
1561     {
1562         for(i = 0; i < data->argc; i++)
1563             free(data->argv[i]);
1564         free(data->argv);
1565     }
1566     if(data->auth != NULL)
1567         authputhandle(data->auth);
1568     if(data->regname != NULL)
1569         free(data->regname);
1570     if(data->username != NULL)
1571     {
1572         if(data->userinfo != NULL)
1573             flog(LOG_INFO, "%ls logged out", data->username);
1574         free(data->username);
1575     }
1576     free(data->inbuf);
1577     while(data->fnotif != NULL)
1578         freenotif(data->fnotif);
1579     if(data->fcmdbuf != NULL)
1580         free(data->fcmdbuf);
1581     if(data->fcmdpid != 0)
1582         kill(-data->fcmdpid, SIGHUP);
1583     if(data->fcmdsk != NULL)
1584         putsock(data->fcmdsk);
1585     free(data);
1586 }
1587
1588 static void queuecmd(struct uidata *data, struct command *cmd, int argc, wchar_t **argv)
1589 {
1590     struct qcommand *new;
1591     
1592     new = smalloc(sizeof(*new));
1593     new->cmd = cmd;
1594     new->argc = argc;
1595     new->argv = argv;
1596     new->next = NULL;
1597     if(data->queuelast != NULL)
1598         data->queuelast->next = new;
1599     data->queuelast = new;
1600     if(data->queue == NULL)
1601         data->queue = new;
1602 }
1603
1604 static struct uidata *newuidata(struct socket *sk)
1605 {
1606     struct uidata *data;
1607     static int curid = 0;
1608     
1609     data = smalloc(sizeof(*data));
1610     memset(data, 0, sizeof(*data));
1611     data->id = curid++;
1612     data->sk = sk;
1613     getsock(sk);
1614     data->inbuf = smalloc(1024);
1615     data->uid = -1;
1616     if((data->ichandle = iconv_open("wchar_t", "utf-8")) == (iconv_t)-1)
1617     {
1618         flog(LOG_CRIT, "iconv cannot handle UTF-8: %s", strerror(errno));
1619         return(NULL);
1620     }
1621     data->next = actives;
1622     data->prev = NULL;
1623     if(actives != NULL)
1624         actives->prev = data;
1625     actives = data;
1626     return(data);
1627 }
1628
1629 static void uiread(struct socket *sk, struct uidata *data)
1630 {
1631     int ret, done;
1632     char *newbuf;
1633     char *p1, *p2;
1634     wchar_t *porig;
1635     size_t datalen, len2;
1636     struct command *cur;
1637     
1638     if(data->indata > 1024)
1639         data->indata = 0;
1640     if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
1641         return;
1642     sizebuf(&data->inbuf, &data->inbufsize, data->indata + datalen, 1, 1);
1643     memcpy(data->inbuf + data->indata, newbuf, datalen);
1644     free(newbuf);
1645     data->indata += datalen;
1646     if(data->cb == NULL)
1647     {
1648         data->cb = smalloc(sizeof(wchar_t) * (data->cbsize = 64));
1649         data->cbdata = 0;
1650         data->pp = data->cb;
1651     }
1652     done = 0;
1653     while(!done)
1654     {
1655         if(data->cbsize == data->cbdata)
1656         {
1657             len2 = data->pp - data->cb;
1658             data->cb = srealloc(data->cb, sizeof(wchar_t) * (data->cbsize *= 2));
1659             data->pp = data->cb + len2;
1660         }
1661         p1 = data->inbuf;
1662         p2 = (char *)(porig = (data->cb + data->cbdata));
1663         len2 = sizeof(wchar_t) * (data->cbsize - data->cbdata);
1664         ret = iconv(data->ichandle, &p1, &data->indata, &p2, &len2);
1665         memmove(data->inbuf, p1, data->indata);
1666         /* Just a sanity check */
1667         if(((p2 - ((char *)data->cb)) % sizeof(wchar_t)) != 0)
1668         {
1669             flog(LOG_CRIT, "Aiya! iconv does strange things to our wchar_t's!");
1670             abort();
1671         }
1672         data->cbdata += (((wchar_t *)p2) - porig);
1673         if(ret < 0)
1674         {
1675             switch(errno)
1676             {
1677             case EILSEQ:
1678                 /* XXX: Should this really just ignore it? */
1679                 data->indata = 0;
1680                 done = 1;
1681                 break;
1682             case EINVAL:
1683                 done = 1;
1684                 break;
1685             case E2BIG:
1686                 /* Just a sanity check */
1687                 if(data->cbsize != data->cbdata)
1688                 {
1689                     flog(LOG_CRIT, "Aiya! iconv doesn't give us wchar_t's!");
1690                     abort();
1691                 }
1692                 break;
1693             default:
1694                 flog(LOG_WARNING, "bug: strange error from iconv in uiread: %s", strerror(errno));
1695                 break;
1696             }
1697         } else {
1698             done = 1;
1699         }
1700     }
1701     done = 0;
1702     while(!done && (data->pp - data->cb < data->cbdata))
1703     {
1704         switch(data->ps)
1705         {
1706         case 0:
1707             if(iswspace(*data->pp))
1708             {
1709                 if(*data->pp == L'\r')
1710                 {
1711                     if(data->pp == data->cb + data->cbdata - 1)
1712                     {
1713                         done = 1;
1714                         break;
1715                     }
1716                     if(*(++data->pp) == L'\n')
1717                     {
1718                         if((data->argv != NULL) && (data->argv[0] != NULL))
1719                         {
1720                             for(cur = commands; cur->handler != NULL; cur++)
1721                             {
1722                                 if(cur->name == NULL)
1723                                     continue;
1724                                 if(!wcscasecmp(cur->name, data->argv[0]))
1725                                 {
1726                                     queuecmd(data, cur, data->argc, data->argv);
1727                                     break;
1728                                 }
1729                             }
1730                             if(cur->handler == NULL)
1731                                 queuecmd(data, &commands[1], data->argc, data->argv);
1732                         } else {
1733                             queuecmd(data, &commands[1], data->argc, data->argv);
1734                         }
1735                         data->argv = NULL;
1736                         data->args = 0;
1737                         data->argc = 0;
1738                         wmemmove(data->cb, data->pp, data->cbdata -= (data->pp - data->cb));
1739                         data->pp = data->cb;
1740                     } else {
1741                         data->pp++;
1742                     }
1743                 } else {
1744                     data->pp++;
1745                 }
1746             } else {
1747                 data->ps = 1;
1748                 data->cwdata = 0;
1749             }
1750             break;
1751         case 1:
1752             if(iswspace(*data->pp))
1753             {
1754                 addtobuf(data->cw, L'\0');
1755                 sizebuf(&data->argv, &data->args, data->argc + 1, sizeof(*data->argv), 1);
1756                 data->argv[data->argc++] = data->cw;
1757                 data->cw = NULL;
1758                 data->cwsize = 0;
1759                 data->cwdata = 0;
1760                 data->ps = 0;
1761             } else if(*data->pp == L'\"') {
1762                 data->ps = 2;
1763                 data->pp++;
1764             } else if(*data->pp == L'\\') {
1765                 if(data->pp == data->cb + data->cbdata - 1)
1766                 {
1767                     done = 1;
1768                     break;
1769                 }
1770                 addtobuf(data->cw, *(++data->pp));
1771                 data->pp++;
1772             } else {
1773                 addtobuf(data->cw, *(data->pp++));
1774             }
1775             break;
1776         case 2:
1777             if(*data->pp == L'\"') 
1778             {
1779                 data->ps = 1;
1780             } else if(*data->pp == L'\\') {
1781                 if(data->pp == data->cb + data->cbdata - 1)
1782                 {
1783                     done = 1;
1784                     break;
1785                 }
1786                 addtobuf(data->cw, *(++(data->pp)));
1787             } else {
1788                 addtobuf(data->cw, *data->pp);
1789             }
1790             data->pp++;
1791             break;
1792         }
1793     }
1794 }
1795
1796 static void uierror(struct socket *sk, int err, struct uidata *data)
1797 {
1798     if(err)
1799         flog(LOG_WARNING, "error occurred on UI socket: %s", strerror(err));
1800     freeuidata(data);
1801 }
1802
1803 static void uiaccept(struct socket *sk, struct socket *newsk, void *data)
1804 {
1805     struct uidata *uidata;
1806     
1807     newsk->data = uidata = newuidata(newsk);
1808     socksettos(newsk, confgetint("ui", "uitos"));
1809     if(uidata == NULL)
1810         return;
1811     newsk->errcb = (void (*)(struct socket *, int, void *))uierror;
1812     newsk->readcb = (void (*)(struct socket *, void *))uiread;
1813     queuecmd(uidata, &commands[0], 0, NULL);
1814 }
1815
1816 static int srcheta(struct search *srch, void *uudata)
1817 {
1818     struct uidata *data;
1819     
1820     for(data = actives; data != NULL; data = data->next)
1821     {
1822         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1823             newnotif(data, 620, NOTIF_ID, srch->id, NOTIF_INT, srch->eta - time(NULL), NOTIF_END);
1824     }
1825     return(0);
1826 }
1827
1828 static int srchcommit(struct search *srch, void *uudata)
1829 {
1830     struct uidata *data;
1831
1832     for(data = actives; data != NULL; data = data->next)
1833     {
1834         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1835             newnotif(data, 621, NOTIF_ID, srch->id, NOTIF_END);
1836     }
1837     return(0);
1838 }
1839
1840 static int srchres(struct search *srch, struct srchres *sr, void *uudata)
1841 {
1842     struct uidata *data;
1843
1844     for(data = actives; data != NULL; data = data->next)
1845     {
1846         if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
1847         {
1848             newnotif(data, 622, NOTIF_ID, srch->id, NOTIF_STR, sr->filename, NOTIF_STR, sr->fnet->name, NOTIF_STR, sr->peerid, NOTIF_INT, sr->size,
1849                      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);
1850         }
1851     }
1852     return(0);
1853 }
1854
1855 static int recvchat(struct fnetnode *fn, int public, wchar_t *name, wchar_t *peer, wchar_t *string, void *uudata)
1856 {
1857     struct uidata *data;
1858     
1859     for(data = actives; data != NULL; data = data->next)
1860     {
1861         if(haspriv(data, PERM_CHAT) && data->notify.b.fnchat)
1862             newnotif(data, 600, NOTIF_ID, fn->id, NOTIF_INT, public, NOTIF_STR, name, NOTIF_STR, peer, NOTIF_STR, string, NOTIF_END);
1863     }
1864     return(0);
1865 }
1866
1867 static int fnactive(struct fnetnode *fn, wchar_t *attrib, void *uudata)
1868 {
1869     struct uidata *data;
1870     struct notif *notif;
1871     
1872     if(!wcscmp(attrib, L"state"))
1873     {
1874         for(data = actives; data != NULL; data = data->next)
1875         {
1876             if(data->notify.b.fnact)
1877                 newnotif(data, 601, NOTIF_ID, fn->id, NOTIF_INT, fn->state, NOTIF_END);
1878         }
1879     } else if(!wcscmp(attrib, L"name")) {
1880         for(data = actives; data != NULL; data = data->next)
1881         {
1882             if(data->notify.b.fnact)
1883                 newnotif(data, 602, NOTIF_ID, fn->id, NOTIF_STR, fn->name, NOTIF_END);
1884         }
1885     } else if(!wcscmp(attrib, L"numpeers")) {
1886         for(data = actives; data != NULL; data = data->next)
1887         {
1888             if(data->notify.b.fnact)
1889             {
1890                 if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 605, fn->id)) != NULL)
1891                     notif->argv[1].d.n = fn->numpeers;
1892                 else
1893                     newnotif(data, 605, NOTIF_ID, fn->id, NOTIF_INT, fn->numpeers, NOTIF_END)->rlimit = 0.5;
1894             }
1895         }
1896     }
1897     return(0);
1898 }
1899
1900 static int fnunlink(struct fnetnode *fn, void *uudata)
1901 {
1902     struct uidata *data;
1903     
1904     for(data = actives; data != NULL; data = data->next)
1905     {
1906         if(data->notify.b.fnact)
1907             newnotif(data, 603, NOTIF_ID, fn->id, NOTIF_END);
1908     }
1909     return(0);
1910 }
1911
1912 static int peernew(struct fnetnode *fn, struct fnetpeer *peer, void *uudata)
1913 {
1914     struct uidata *data;
1915     
1916     for(data = actives; data != NULL; data = data->next)
1917     {
1918         if(data->notify.b.fnpeer)
1919             newnotif(data, 630, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_STR, peer->nick, NOTIF_END);
1920     }
1921     return(0);
1922 }
1923
1924 static int peerdel(struct fnetnode *fn, struct fnetpeer *peer, void *uudata)
1925 {
1926     struct uidata *data;
1927     
1928     for(data = actives; data != NULL; data = data->next)
1929     {
1930         if(data->notify.b.fnpeer)
1931             newnotif(data, 631, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_END);
1932     }
1933     return(0);
1934 }
1935
1936 static int peerchange(struct fnetnode *fn, struct fnetpeer *peer, struct fnetpeerdi *di, void *uudata)
1937 {
1938     struct uidata *data;
1939     struct notif *notif;
1940     wchar_t buf[32];
1941     
1942     for(data = actives; data != NULL; data = data->next)
1943     {
1944         if(data->notify.b.fnpeer)
1945         {
1946             for(notif = data->fnotif; notif != NULL; notif = notif->next)
1947             {
1948                 if((notif->code == 632) && (notif->state == NOTIF_PEND) && (notif->argv[0].d.n == fn->id) && !wcscmp(notif->argv[1].d.s, peer->id))
1949                     break;
1950             }
1951             if(notif == NULL)
1952                 notif = newnotif(data, 632, NOTIF_INT, fn->id, NOTIF_STR, peer->id, NOTIF_STR, peer->nick, NOTIF_END);
1953             notifappend(notif, NOTIF_STR, di->datum->id, NOTIF_INT, di->datum->datatype, NOTIF_END);
1954             switch(di->datum->datatype)
1955             {
1956             case FNPD_INT:
1957                 notifappend(notif, NOTIF_INT, di->data.num, NOTIF_END);
1958                 break;
1959             case FNPD_STR:
1960                 notifappend(notif, NOTIF_STR, di->data.str, NOTIF_END);
1961                 break;
1962             case FNPD_LL:
1963                 swprintf(buf, sizeof(buf) / sizeof(*buf), L"%lli", di->data.lnum);
1964                 notifappend(notif, NOTIF_STR, buf, NOTIF_END);
1965                 break;
1966             }
1967         }
1968     }
1969     return(0);
1970 }
1971
1972 static int newfnetnode(struct fnetnode *fn, void *uudata)
1973 {
1974     struct uidata *data;
1975     
1976     for(data = actives; data != NULL; data = data->next)
1977     {
1978         if(data->notify.b.fnact)
1979             newnotif(data, 604, NOTIF_ID, fn->id, NOTIF_STR, fn->fnet->name, NOTIF_END);
1980     }
1981     CBREG(fn, fnetnode_ac, fnactive, NULL, NULL);
1982     CBREG(fn, fnetnode_chat, recvchat, NULL, NULL);
1983     CBREG(fn, fnetnode_unlink, fnunlink, NULL, NULL);
1984     CBREG(fn, fnetpeer_new, peernew, NULL, NULL);
1985     CBREG(fn, fnetpeer_del, peerdel, NULL, NULL);
1986     CBREG(fn, fnetpeer_chdi, peerchange, NULL, NULL);
1987     return(0);
1988 }
1989
1990 static int transferchattr(struct transfer *transfer, wchar_t *attrib, void *uudata)
1991 {
1992     struct uidata *data;
1993     
1994     if(!wcscmp(attrib, L"state"))
1995     {
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, 611, NOTIF_ID, transfer->id, NOTIF_INT, transfer->state, NOTIF_END);
2000         }
2001     } else if(!wcscmp(attrib, L"nick")) {
2002         for(data = actives; data != NULL; data = data->next)
2003         {
2004             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2005                 newnotif(data, 612, NOTIF_ID, transfer->id, NOTIF_STR, transfer->peernick, NOTIF_END);
2006         }
2007     } else if(!wcscmp(attrib, L"size")) {
2008         for(data = actives; data != NULL; data = data->next)
2009         {
2010             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2011                 newnotif(data, 613, NOTIF_ID, transfer->id, NOTIF_INT, transfer->size, NOTIF_END);
2012         }
2013     } else if(!wcscmp(attrib, L"error")) {
2014         for(data = actives; data != NULL; data = data->next)
2015         {
2016             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2017                 newnotif(data, 614, NOTIF_ID, transfer->id, NOTIF_INT, transfer->error, NOTIF_END);
2018         }
2019     } else if(!wcscmp(attrib, L"path")) {
2020         for(data = actives; data != NULL; data = data->next)
2021         {
2022             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2023                 newnotif(data, 616, NOTIF_ID, transfer->id, NOTIF_STR, transfer->path, NOTIF_END);
2024         }
2025     } else if(!wcscmp(attrib, L"hash")) {
2026         for(data = actives; data != NULL; data = data->next)
2027         {
2028             if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2029                 newnotif(data, 618, NOTIF_ID, transfer->id, NOTIF_STR, (transfer->hash == NULL)?L"":unparsehash(transfer->hash), NOTIF_END);
2030         }
2031     }
2032     return(0);
2033 }
2034
2035 static int transferprog(struct transfer *transfer, void *uudata)
2036 {
2037     struct uidata *data;
2038     struct notif *notif;
2039     
2040     for(data = actives; data != NULL; data = data->next)
2041     {
2042         if(haspriv(data, PERM_TRANS) && data->notify.b.trprog && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2043         {
2044             if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 615, transfer->id)) != NULL)
2045                 notif->argv[1].d.n = transfer->curpos;
2046             else
2047                 newnotif(data, 615, NOTIF_ID, transfer->id, NOTIF_INT, transfer->curpos, NOTIF_END)->rlimit = 0.5;
2048         }
2049     }
2050     return(0);
2051 }
2052
2053 static int transferdestroyed(struct transfer *transfer, void *uudata)
2054 {
2055     struct uidata *data;
2056     
2057     for(data = actives; data != NULL; data = data->next)
2058     {
2059         if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2060             newnotif(data, 617, NOTIF_ID, transfer->id, NOTIF_STR, (transfer->exitstatus == NULL)?L"":(transfer->exitstatus), NOTIF_END);
2061     }
2062     return(0);
2063 }
2064
2065 static int newtransfernotify(struct transfer *transfer, void *uudata)
2066 {
2067     struct uidata *data;
2068     
2069     for(data = actives; data != NULL; data = data->next)
2070     {
2071         if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
2072             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);
2073     }
2074     CBREG(transfer, trans_ac, transferchattr, NULL, NULL);
2075     CBREG(transfer, trans_p, transferprog, NULL, NULL);
2076     CBREG(transfer, trans_destroy, transferdestroyed, NULL, NULL);
2077     return(0);
2078 }
2079
2080 static struct uiuser *newuser(wchar_t *name, unsigned long perms)
2081 {
2082     struct uiuser *new;
2083     
2084     new = smalloc(sizeof(*new));
2085     new->used = 0;
2086     new->name = swcsdup(name);
2087     new->perms = perms;
2088     new->delete = 0;
2089     new->next = users;
2090     new->prev = NULL;
2091     if(users != NULL)
2092         users->prev = new;
2093     users = new;
2094     return(new);
2095 }
2096
2097 static void freeuser(struct uiuser *user)
2098 {
2099     if(user->next != NULL)
2100         user->next->prev = user->prev;
2101     if(user->prev != NULL)
2102         user->prev->next = user->next;
2103     if(user == users)
2104         users = user->next;
2105     free(user->name);
2106     free(user);
2107 }
2108
2109 static int conf_user(int argc, wchar_t **argv)
2110 {
2111     int i, perms, permmod;
2112     struct uiuser *user;
2113     wchar_t *p;
2114     
2115     if(argc < 3)
2116     {
2117         flog(LOG_WARNING, "not enough arguments given for user command");
2118         return(1);
2119     }
2120     perms = 0;
2121     for(i = 2; i < argc; i++)
2122     {
2123         if(!iswalpha(argv[i][0]))
2124             p = argv[i] + 1;
2125         else
2126             p = argv[i];
2127         if(!wcscmp(p, L"disallow"))
2128             permmod = PERM_DISALLOW;
2129         if(!wcscmp(p, L"admin"))
2130             permmod = PERM_ADMIN;
2131         if(!wcscmp(p, L"fnetctl"))
2132             permmod = PERM_FNETCTL;
2133         if(!wcscmp(p, L"trans"))
2134             permmod = PERM_TRANS;
2135         if(!wcscmp(p, L"transcu"))
2136             permmod = PERM_TRANSCU;
2137         if(!wcscmp(p, L"chat"))
2138             permmod = PERM_CHAT;
2139         if(!wcscmp(p, L"srch"))
2140             permmod = PERM_SRCH;
2141         if(!wcscmp(p, L"all"))
2142             permmod = ~0;
2143         if(argv[i][0] == L'-')
2144             perms &= ~permmod;
2145         else
2146             perms |= permmod;
2147     }
2148     if((user = finduser(argv[1])) == NULL)
2149     {
2150         newuser(argv[1], perms);
2151     } else {
2152         user->delete = 0;
2153         user->perms = perms;
2154     }
2155     return(0);
2156 }
2157
2158 static void preinit(int hup)
2159 {
2160     struct uiuser *user;
2161     
2162     if(!hup)
2163     {
2164         newuser(L"default", PERM_DISALLOW);
2165     } else {
2166         for(user = users; user != NULL; user = user->next)
2167         {
2168             if(!wcscmp(user->name, L"default"))
2169                 user->delete = 1;
2170         }
2171     }
2172 }
2173
2174 static struct sockaddr_un *makeunixname(void)
2175 {
2176     static struct sockaddr_un buf;
2177     char *val;
2178     struct passwd *pwd;
2179     uid_t uid;
2180     
2181     memset(&buf, 0, sizeof(buf));
2182     buf.sun_family = PF_UNIX;
2183     if((val = icswcstombs(confgetstr("ui", "unixsock"), NULL, NULL)) == NULL) {
2184         flog(LOG_WARNING, "could not map Unix socket name into local charset: %s", strerror(errno));
2185         return(NULL);
2186     }
2187     if(!strcmp(val, "none"))
2188         return(NULL);
2189     if(!strcmp(val, "default"))
2190     {
2191         if((uid = getuid()) == 0)
2192         {
2193             strcpy(buf.sun_path, "/var/run/doldacond.sock");
2194             return(&buf);
2195         } else {
2196             if((pwd = getpwuid(uid)) == NULL)
2197             {
2198                 flog(LOG_ERR, "could not get passwd entry for current user: %s", strerror(errno));
2199                 return(NULL);
2200             }
2201             strcpy(buf.sun_path, "/tmp/doldacond-");
2202             strcat(buf.sun_path, pwd->pw_name);
2203             return(&buf);
2204         }
2205     }
2206     if(strchr(val, '/'))
2207     {
2208         strcpy(buf.sun_path, val);
2209         return(&buf);
2210     }
2211     flog(LOG_WARNING, "invalid Unix socket name: %s", val);
2212     return(NULL);
2213 }
2214
2215 static int tcpportupdate(struct configvar *var, void *uudata)
2216 {
2217     struct socket *newsock;
2218     
2219     newsock = NULL;
2220     if((var->val.num != -1) && ((newsock = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL))
2221     {
2222         flog(LOG_WARNING, "could not create new TCP UI socket, reverting to old: %s", strerror(errno));
2223         return(0);
2224     }
2225     if(tcpsocket != NULL)
2226     {
2227         putsock(tcpsocket);
2228         tcpsocket = NULL;
2229     }
2230     tcpsocket = newsock;
2231     return(0);
2232 }
2233
2234 static int unixsockupdate(struct configvar *var, void *uudata)
2235 {
2236     struct socket *newsock;
2237     struct sockaddr_un *un;
2238     mode_t ou;
2239     
2240     newsock = NULL;
2241     ou = umask(0111);
2242     if(((un = makeunixname()) != NULL) && ((newsock = netcslistenlocal(SOCK_STREAM, (struct sockaddr *)un, sizeof(*un), uiaccept, NULL)) == NULL))
2243     {
2244         umask(ou);
2245         flog(LOG_WARNING, "could not create new Unix UI socket, reverting to old: %s", strerror(errno));
2246         return(0);
2247     }
2248     umask(ou);
2249     if(unixsocket != NULL)
2250     {
2251         putsock(unixsocket);
2252         unixsocket = NULL;
2253     }
2254     unixsocket = newsock;
2255     return(0);
2256 }
2257
2258 static int init(int hup)
2259 {
2260     struct uiuser *user, *next;
2261     struct sockaddr_un *un;
2262     struct passwd *pwd;
2263     wchar_t *wcsname;
2264     mode_t ou;
2265     
2266     if(hup)
2267     {
2268         for(user = users; user != NULL; user = next)
2269         {
2270             next = user->next;
2271             if(user->delete)
2272                 freeuser(user);
2273         }
2274     }
2275     if(!hup)
2276     {
2277         starttime = time(NULL);
2278         if((confgetint("ui", "port") != -1) && ((tcpsocket = netcstcplisten(confgetint("ui", "port"), 1, uiaccept, NULL)) == NULL))
2279         {
2280             flog(LOG_CRIT, "could not create TCP UI socket: %s", strerror(errno));
2281             return(1);
2282         }
2283         CBREG(confgetvar("ui", "port"), conf_update, tcpportupdate, NULL, NULL);
2284         ou = umask(0111);
2285         if(((un = makeunixname()) != NULL) && ((unixsocket = netcslistenlocal(SOCK_STREAM, (struct sockaddr *)un, sizeof(*un), uiaccept, NULL)) == NULL))
2286         {
2287             umask(ou);
2288             flog(LOG_CRIT, "could not create Unix UI socket: %s", strerror(errno));
2289             return(1);
2290         }
2291         umask(ou);
2292         CBREG(confgetvar("ui", "unixsock"), conf_update, unixsockupdate, NULL, NULL);
2293         GCBREG(newfncb, newfnetnode, NULL);
2294         GCBREG(newtransfercb, newtransfernotify, NULL);
2295     }
2296     if(getuid() != 0)
2297     {
2298         for(user = users; user != NULL; user = user->next)
2299         {
2300             if(wcscmp(user->name, L"default"))
2301                 break;
2302         }
2303         if(!user)
2304         {
2305             if((pwd = getpwuid(getuid())) == NULL)
2306             {
2307                 flog(LOG_CRIT, "could not get login info: %s", strerror(errno));
2308                 return(1);
2309             }
2310             if((wcsname = icmbstowcs(pwd->pw_name, NULL)) == NULL)
2311             {
2312                 flog(LOG_CRIT, "could not convert user name into wcs: %s", strerror(errno));
2313                 return(1);
2314             }
2315             newuser(wcsname, ~PERM_DISALLOW);
2316             free(wcsname);
2317         }
2318     }
2319     return(0);
2320 }
2321
2322 static int run(void)
2323 {
2324     int i, id;
2325     struct uidata *data, *next;
2326     struct qcommand *qcmd;
2327     struct notif *notif, *nnotif;
2328     
2329     for(data = actives; data != NULL; data = next)
2330     {
2331         next = data->next;
2332         if(data->close)
2333             freeuidata(data);
2334     }
2335     for(data = actives; data != NULL; data = data->next)
2336     {
2337         for(notif = data->fnotif; notif != NULL; notif = nnotif)
2338         {
2339             nnotif = notif->next;
2340             if(notif->state == NOTIF_WAIT)
2341                 continue;
2342             id = -1;
2343             for(i = 0; i < notif->argc; i++)
2344             {
2345                 if(notif->argv[i].dt == NOTIF_ID)
2346                 {
2347                     id = notif->argv[i].d.n;
2348                     break;
2349                 }
2350             }
2351             if(findnotif(notif->prev, 0, -1, notif->code, id) != NULL)
2352                 continue;
2353             sq(data->sk, 2, L"%i", notif->code, NULL);
2354             for(i = 0; i < notif->argc; i++)
2355             {
2356                 switch(notif->argv[i].dt)
2357                 {
2358                 case NOTIF_INT:
2359                 case NOTIF_ID:
2360                     sq(data->sk, 2, L"%i", notif->argv[i].d.n, NULL);
2361                     break;
2362                 case NOTIF_STR:
2363                     if(notif->argv[i].d.s[0] == L'%')
2364                         sq(data->sk, 2, L"%ls", notif->argv[i].d.s, NULL);
2365                     else
2366                         sq(data->sk, 2, notif->argv[i].d.s, NULL);
2367                     break;
2368                 case NOTIF_FLOAT:
2369                     sq(data->sk, 2, L"%f", notif->argv[i].d.d, NULL);
2370                     break;
2371                 }
2372             }
2373             sq(data->sk, 0, NULL);
2374             if(notif->rlimit != 0)
2375             {
2376                 notif->state = NOTIF_WAIT;
2377                 notif->exptimer = timercallback(ntime() + notif->rlimit, (void (*)(int, void *))notifexpire, notif);
2378             } else {
2379                 freenotif(notif);
2380             }
2381         }
2382         if((qcmd = unlinkqcmd(data)) != NULL)
2383         {
2384             qcmd->cmd->handler(data->sk, data, qcmd->argc, qcmd->argv);
2385             freequeuecmd(qcmd);
2386             return(1);
2387         }
2388     }
2389     return(0);
2390 }
2391
2392 static void terminate(void)
2393 {
2394     while(users != NULL)
2395         freeuser(users);
2396     if(tcpsocket != NULL)
2397         putsock(tcpsocket);
2398     if(unixsocket != NULL)
2399         putsock(unixsocket);
2400 }
2401
2402 static struct configvar myvars[] =
2403 {
2404     /** If true, UI connections will only be accepted from localhost
2405      * addresses (127.0.0.1, ::1 or ::ffff:127.0.0.1). Unless you are
2406      * completely sure that you know what you are doing, never turn
2407      * this off when auth.authless is on. */
2408     {CONF_VAR_BOOL, "onlylocal", {.num = 1}},
2409     /** The TCP port number on which to accept UI client connections,
2410      * or -1 to not listen on TCP. */
2411     {CONF_VAR_INT, "port", {.num = 1500}},
2412     /**
2413      * Controls the the name to use for the Unix socket on which to
2414      * accept UI client connections. If the name contains a slash, it
2415      * is treated as a file name to bind on. If the name is "default",
2416      * the file name will be "/var/run/doldacond.sock" if doldacond
2417      * runs with UID == 0, or "/tmp/doldacond-NAME" otherwise, where
2418      * NAME is the user name of the UID which doldacond runs as. If
2419      * the name is "none", no Unix socket will be used. Otherwise, an
2420      * error is signaled.
2421      */
2422     {CONF_VAR_STRING, "unixsock", {.str = L"default"}},
2423     /** The TOS value to use for UI connections (see the TOS VALUES
2424      * section). */
2425     {CONF_VAR_INT, "uitos", {.num = SOCK_TOS_MINDELAY}},
2426     /** The name of the filtercmd script (see the FILES section for
2427      * lookup information). */
2428     {CONF_VAR_STRING, "filtercmd", {.str = L"dc-filtercmd"}},
2429     {CONF_VAR_END}
2430 };
2431
2432 static struct configcmd mycmds[] =
2433 {
2434     {"user", conf_user},
2435     {NULL}
2436 };
2437
2438 static struct module me =
2439 {
2440     .name = "ui",
2441     .conf =
2442     {
2443         .vars = myvars,
2444         .cmds = mycmds
2445     },
2446     .preinit = preinit,
2447     .init = init,
2448     .run = run,
2449     .terminate = terminate
2450 };
2451
2452 MODULE(me)