Add util.scm.
[doldaconnect.git] / lib / uimisc.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
20 #include <unistd.h>
21 /* I'm very unsure about this, but for now it defines wcstoll (which
22  * should be defined anyway) and doesn't break anything... let's keep
23  * two eyes wide open, though. */
24 #define __USE_ISOC99
25 #include <wchar.h>
26 #include <wctype.h>
27 #include <pwd.h>
28 #include <string.h>
29 #include <malloc.h>
30 #include <stdio.h>
31
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 #include <doldaconnect/uilib.h>
36 #include <doldaconnect/uimisc.h>
37 #include <doldaconnect/utils.h>
38
39 #ifdef HAVE_KRB5
40 #include <krb5.h>
41 #include <com_err.h>
42 #endif
43
44 struct logindata;
45
46 struct authmech
47 {
48     wchar_t *name;
49     void (*process)(struct dc_response *resp, struct logindata *data);
50     int (*init)(struct logindata *data);
51     void (*release)(struct logindata *data);
52 };
53
54 struct logindata
55 {
56     int (*conv)(int type, wchar_t *text, char **resp, void *data);
57     void (*callback)(int err, wchar_t *reason, void *data);
58     char *username;
59     int freeusername;
60     int useauthless;
61     void *data;
62     void *mechdata;
63     struct authmech *mech;
64 };
65
66 struct gencbdata
67 {
68     void (*callback)(int resp, void *data);
69     void *data;
70 };
71
72 struct fnetcbdata
73 {
74     void (*callback)(struct dc_fnetnode *fn, int resp, void *data);
75     int fnid;
76     void *data;
77 };
78
79 struct dc_fnetnode *dc_fnetnodes = NULL;
80 struct dc_transfer *dc_transfers = NULL;
81
82 static void freelogindata(struct logindata *data)
83 {
84     if((data->mech != NULL) && (data->mech->release != NULL))
85         data->mech->release(data);
86     if(data->freeusername)
87         free(data->username);
88     free(data);
89 }
90
91 static int logincallback(struct dc_response *resp);
92
93 static void process_authless(struct dc_response *resp, struct logindata *data)
94 {
95     switch(resp->code)
96     {
97     case 200:
98         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
99         freelogindata(data);
100         break;
101 /*
102     case 303:
103     case 304:
104         if((ires = dc_interpret(resp)) != NULL)
105         {
106             buf = NULL;
107             if(data->conv((resp->code == 303)?DC_LOGIN_CONV_INFO:DC_LOGIN_CONV_ERROR, ires->argv[0].val.str, &buf, data->data))
108             {
109                 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
110                 freelogindata(data);
111             } else {
112                 dc_queuecmd(logincallback, data, L"pass", L"", NULL);
113             }
114             if(buf != NULL)
115             {
116                 memset(buf, 0, strlen(buf));
117                 free(buf);
118             }
119             dc_freeires(ires);
120         }
121         break;
122 */
123     case 505:
124         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
125         freelogindata(data);
126         break;
127     case 506:
128         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
129         freelogindata(data);
130         break;
131     default:
132         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
133         freelogindata(data);
134         break;
135     }
136 }
137
138 static void process_pam(struct dc_response *resp, struct logindata *data)
139 {
140     struct dc_intresp *ires;
141     int convtype;
142     char *buf;
143     
144     switch(resp->code)
145     {
146     case 200:
147         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
148         freelogindata(data);
149         break;
150     case 301:
151     case 302:
152     case 303:
153     case 304:
154         if(resp->code == 301)
155             convtype = DC_LOGIN_CONV_NOECHO;
156         else if(resp->code == 302)
157             convtype = DC_LOGIN_CONV_ECHO;
158         else if(resp->code == 303)
159             convtype = DC_LOGIN_CONV_INFO;
160         else if(resp->code == 304)
161             convtype = DC_LOGIN_CONV_ERROR;
162         if((ires = dc_interpret(resp)) != NULL)
163         {
164             buf = NULL;
165             if(data->conv(convtype, ires->argv[0].val.str, &buf, data->data))
166             {
167                 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
168                 freelogindata(data);
169             } else {
170                 dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
171             }
172             if(buf != NULL)
173             {
174                 memset(buf, 0, strlen(buf));
175                 free(buf);
176             }
177             dc_freeires(ires);
178         }
179         break;
180     case 505:
181         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
182         freelogindata(data);
183         break;
184     case 506:
185         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
186         freelogindata(data);
187         break;
188     default:
189         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
190         freelogindata(data);
191         break;
192     }
193 }
194
195 #ifdef HAVE_KRB5
196 struct krb5data
197 {
198     int state;
199     krb5_context context;
200     krb5_principal sprinc, myprinc;
201     krb5_ccache ccache;
202     krb5_auth_context authcon;
203     krb5_data reqbuf;
204     krb5_creds *servcreds;
205     int valid, fwd, fwded;
206 };
207
208 static char *hexencode(char *data, size_t datalen)
209 {
210     char *buf, this;
211     size_t bufsize, bufdata;
212     int dig;
213     
214     buf = NULL;
215     bufsize = bufdata = 0;
216     for(; datalen > 0; datalen--, data++)
217     {
218         dig = (*data & 0xF0) >> 4;
219         if(dig > 9)
220             this = 'A' + dig - 10;
221         else
222             this = dig + '0';
223         addtobuf(buf, this);
224         dig = *data & 0x0F;
225         if(dig > 9)
226             this = 'A' + dig - 10;
227         else
228             this = dig + '0';
229         addtobuf(buf, this);
230     }
231     addtobuf(buf, 0);
232     return(buf);
233 }
234
235 static char *hexdecode(char *data, size_t *len)
236 {
237     char *buf, this;
238     size_t bufsize, bufdata;
239     
240     buf = NULL;
241     bufsize = bufdata = 0;
242     for(; *data; data++)
243     {
244         if((*data >= 'A') && (*data <= 'F'))
245         {
246             this = (this & 0x0F) | ((*data - 'A' + 10) << 4);
247         } else if((*data >= '0') && (*data <= '9')) {
248             this = (this & 0x0F) | ((*data - '0') << 4);
249         } else {
250             if(buf != NULL)
251                 free(buf);
252             return(NULL);
253         }
254         data++;
255         if(!*data)
256         {
257             if(buf != NULL)
258                 free(buf);
259             return(NULL);
260         }
261         if((*data >= 'A') && (*data <= 'F'))
262         {
263             this = (this & 0xF0) | (*data - 'A' + 10);
264         } else if((*data >= '0') && (*data <= '9')) {
265             this = (this & 0xF0) | (*data - '0');
266         } else {
267             if(buf != NULL)
268                 free(buf);
269             return(NULL);
270         }
271         addtobuf(buf, this);
272     }
273     addtobuf(buf, 0);
274     if(len != NULL)
275         *len = bufdata - 1;
276     return(buf);
277 }
278
279 static void process_krb5(struct dc_response *resp, struct logindata *data)
280 {
281     int ret;
282     struct dc_intresp *ires;
283     struct krb5data *krb;
284     krb5_data k5d;
285     krb5_ap_rep_enc_part *repl;
286     char *buf;
287     
288     krb = data->mechdata;
289     switch(resp->code)
290     {
291     case 200:
292         data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
293         freelogindata(data);
294         break;
295     case 300:
296         switch(krb->state)
297         {
298         case 0:
299             buf = hexencode(krb->reqbuf.data, krb->reqbuf.length);
300             dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
301             free(buf);
302             krb->state = 1;
303             break;
304         case 1:
305             if((ires = dc_interpret(resp)) != NULL)
306             {
307                 k5d.data = hexdecode(icswcstombs(ires->argv[0].val.str, NULL, NULL), &k5d.length);
308                 if(!krb->valid)
309                 {
310                     if((ret = krb5_rd_rep(krb->context, krb->authcon, &k5d, &repl)) != 0)
311                     {
312                         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
313                         freelogindata(data);
314                         break;
315                     }
316                     /* XXX: Do I need to do something with this? */
317                     krb->valid = 1;
318                     krb5_free_ap_rep_enc_part(krb->context, repl);
319                 }
320                 if(krb->fwd && !krb->fwded)
321                 {
322                     if(krb->reqbuf.data != NULL)
323                         free(krb->reqbuf.data);
324                     krb->reqbuf.data = NULL;
325                     if((ret = krb5_fwd_tgt_creds(krb->context, krb->authcon, NULL, krb->servcreds->client, krb->servcreds->server, 0, 1, &krb->reqbuf)) != 0)
326                     {
327                         fprintf(stderr, "krb5_fwd_tgt_creds reported an error: %s\n", error_message(ret));
328                         dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
329                         krb->fwd = 0;
330                         krb->state = 2;
331                     } else {
332                         dc_queuecmd(logincallback, data, L"pass", L"32", NULL);
333                         krb->state = 0;
334                         krb->fwded = 1;
335                     }
336                 } else {
337                     dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
338                     krb->state = 2;
339                 }
340                 free(k5d.data);
341                 dc_freeires(ires);
342             }
343             break;
344         default:
345             data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
346             freelogindata(data);
347             break;
348         }
349         break;
350     case 505:
351         data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
352         freelogindata(data);
353         break;
354     case 506:
355         data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
356         freelogindata(data);
357         break;
358     default:
359         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
360         freelogindata(data);
361         break;
362     }
363 }
364
365 static int init_krb5(struct logindata *data)
366 {
367     int ret;
368     struct krb5data *krb;
369     krb5_data cksum;
370     krb5_creds creds;
371     
372     krb = smalloc(sizeof(*krb));
373     memset(krb, 0, sizeof(*krb));
374     krb->fwd = 1;
375     krb->fwded = 0;
376     data->mechdata = krb;
377     if((ret = krb5_init_context(&krb->context)) != 0)
378     {
379         fprintf(stderr, "krb5_init_context reported an error: %s\n", error_message(ret));
380         return(1);
381     }
382     if((ret = krb5_auth_con_init(krb->context, &krb->authcon)) != 0)
383     {
384         fprintf(stderr, "krb5_auth_con_init reported an error: %s\n", error_message(ret));
385         return(1);
386     }
387     krb5_auth_con_setflags(krb->context, krb->authcon, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
388     if((ret = krb5_sname_to_principal(krb->context, dc_gethostname(), "doldacond", KRB5_NT_SRV_HST, &krb->sprinc)) != 0)
389     {
390         fprintf(stderr, "krb5_sname_to_principal reported an error: %s\n", error_message(ret));
391         return(1);
392     }
393     if((ret = krb5_cc_default(krb->context, &krb->ccache)) != 0)
394     {
395         fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
396         return(1);
397     }
398     if((ret = krb5_cc_get_principal(krb->context, krb->ccache, &krb->myprinc)) != 0)
399     {
400         fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
401         return(1);
402     }
403     memset(&creds, 0, sizeof(creds));
404     creds.client = krb->myprinc;
405     creds.server = krb->sprinc;
406     if((ret = krb5_get_credentials(krb->context, 0, krb->ccache, &creds, &krb->servcreds)) != 0)
407     {
408         fprintf(stderr, "krb5_get_credentials reported an error: %s\n", error_message(ret));
409         return(1);
410     }
411     /* WTF is this checksum stuff?! The Krb docs don't say a word about it! */
412     cksum.data = sstrdup(dc_gethostname());
413     cksum.length = strlen(cksum.data);
414     if((ret = krb5_mk_req_extended(krb->context, &krb->authcon, AP_OPTS_MUTUAL_REQUIRED, &cksum, krb->servcreds, &krb->reqbuf)) != 0)
415     {
416         fprintf(stderr, "krb5_mk_req_extended reported an error: %s\n", error_message(ret));
417         return(1);
418     }
419     free(cksum.data);
420     krb->state = 0;
421     return(0);
422 }
423
424 static void release_krb5(struct logindata *data)
425 {
426     struct krb5data *krb;
427     
428     if((krb = data->mechdata) == NULL)
429         return;
430     if(krb->servcreds != NULL)
431         krb5_free_creds(krb->context, krb->servcreds);
432     if(krb->reqbuf.data != NULL)
433         free(krb->reqbuf.data);
434     if(krb->sprinc != NULL)
435         krb5_free_principal(krb->context, krb->sprinc);
436     if(krb->myprinc != NULL)
437         krb5_free_principal(krb->context, krb->myprinc);
438     if(krb->ccache != NULL)
439         krb5_cc_close(krb->context, krb->ccache);
440     if(krb->authcon != NULL)
441         krb5_auth_con_free(krb->context, krb->authcon);
442     if(krb->context != NULL)
443         krb5_free_context(krb->context);
444     free(krb);
445 }
446 #endif
447
448 /* Arranged in order of priority */
449 static struct authmech authmechs[] =
450 {
451 #ifdef HAVE_KRB5
452     {
453         .name = L"krb5",
454         .process = process_krb5,
455         .init = init_krb5,
456         .release = release_krb5
457     },
458 #endif
459     {
460         .name = L"authless",
461         .process = process_authless,
462         .init = NULL,
463         .release = NULL
464     },
465     {
466         .name = L"pam",
467         .process = process_pam,
468         .init = NULL,
469         .release = NULL
470     },
471     {
472         .name = NULL
473     }
474 };
475
476 static int builtinconv(int type, wchar_t *text, char **resp, void *data)
477 {
478     char *buf, *pass;
479     
480     if(isatty(0))
481     {
482         if((buf = icwcstombs(text, NULL)) == NULL)
483             return(1);
484         pass = getpass(buf);
485         free(buf);
486         *resp = sstrdup(pass);
487         memset(pass, 0, strlen(pass));
488         return(0);
489     }
490     return(1);
491 }
492
493 static int logincallback(struct dc_response *resp)
494 {
495     int i;
496     struct dc_intresp *ires;
497     struct logindata *data;
498     int mech;
499     char *username;
500     struct passwd *pwent;
501     void *odata, *ndata;
502     
503     data = resp->data;
504     if(!wcscmp(resp->cmdname, L"lsauth"))
505     {
506         if(resp->code == 201)
507         {
508             data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
509             freelogindata(data);
510         } else {
511             mech = -1;
512             while((ires = dc_interpret(resp)) != NULL)
513             {
514                 if(!data->useauthless && !wcscmp(ires->argv[0].val.str, L"authless"))
515                 {
516                     dc_freeires(ires);
517                     continue;
518                 }
519                 for(i = 0; authmechs[i].name != NULL; i++)
520                 {
521                     if(!wcscmp(authmechs[i].name, ires->argv[0].val.str) && ((i < mech) || (mech == -1)))
522                     {
523                         odata = data->mechdata;
524                         data->mechdata = NULL;
525                         if((authmechs[i].init != NULL) && authmechs[i].init(data))
526                         {
527                             if(authmechs[i].release != NULL)
528                                 authmechs[i].release(data);
529                             data->mechdata = odata;
530                             fprintf(stderr, "authentication mechanism %ls failed, trying further...\n", authmechs[i].name);
531                         } else {
532                             if((data->mech != NULL) && data->mech->release != NULL)
533                             {
534                                 ndata = data->mechdata;
535                                 data->mechdata = odata;
536                                 data->mech->release(data);
537                                 data->mechdata = ndata;
538                             }
539                             mech = i;
540                             data->mech = authmechs + i;
541                         }
542                         break;
543                     }
544                 }
545                 dc_freeires(ires);
546             }
547             if(mech == -1)
548             {
549                 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
550                 freelogindata(data);
551             } else {
552                 if((username = data->username) == NULL)
553                 {
554                     if((pwent = getpwuid(getuid())) == NULL)
555                     {
556                         data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
557                         freelogindata(data);
558                         return(1);
559                     }
560                     username = pwent->pw_name;
561                 }
562                 dc_queuecmd(logincallback, data, L"login", data->mech->name, L"%%s", username, NULL);
563             }
564         }
565     } else if(!wcscmp(resp->cmdname, L"login") || !wcscmp(resp->cmdname, L"pass")) {
566         data->mech->process(resp, data);
567     }
568     return(1);
569 }
570
571 void dc_loginasync(char *username, int useauthless, int (*conv)(int, wchar_t *, char **, void *), void (*callback)(int, wchar_t *, void *), void *udata)
572 {
573     struct logindata *data;
574     
575     data = smalloc(sizeof(*data));
576     if(conv == NULL)
577         conv = builtinconv;
578     data->conv = conv;
579     data->mech = NULL;
580     data->data = udata;
581     data->mechdata = NULL;
582     data->callback = callback;
583     data->useauthless = useauthless;
584     data->freeusername = 0;
585     if(username == NULL)
586     {
587         data->username = NULL;
588     } else {
589         data->username = sstrdup(username);
590         data->freeusername = 1;
591     }
592     dc_queuecmd(logincallback, data, L"lsauth", NULL);
593 }
594
595 static struct dc_fnetpeerdatum *finddatum(struct dc_fnetnode *fn, wchar_t *id)
596 {
597     struct dc_fnetpeerdatum *datum;
598     
599     for(datum = fn->peerdata; datum != NULL; datum = datum->next)
600     {
601         if(!wcscmp(datum->id, id))
602             break;
603     }
604     return(datum);
605 }
606
607 static struct dc_fnetpeerdatum *adddatum(struct dc_fnetnode *fn, wchar_t *id, int dt)
608 {
609     struct dc_fnetpeerdatum *datum;
610     
611     datum = smalloc(sizeof(*datum));
612     memset(datum, 0, sizeof(*datum));
613     datum->refcount = 0;
614     datum->dt = dt;
615     datum->id = swcsdup(id);
616     datum->prev = NULL;
617     datum->next = fn->peerdata;
618     if(fn->peerdata != NULL)
619         fn->peerdata->prev = datum;
620     fn->peerdata = datum;
621     return(datum);
622 }
623
624 static struct dc_fnetpeerdi *difindoradd(struct dc_fnetpeer *peer, struct dc_fnetpeerdatum *datum)
625 {
626     int i;
627     
628     for(i = 0; i < peer->dinum; i++)
629     {
630         if(peer->di[i].datum == datum)
631             return(&peer->di[i]);
632     }
633     peer->di = srealloc(peer->di, sizeof(struct dc_fnetpeerdi) * ++(peer->dinum));
634     memset(&peer->di[i], 0, sizeof(struct dc_fnetpeerdi));
635     peer->di[i].datum = datum;
636     datum->refcount++;
637     return(&peer->di[i]);
638 }
639
640 static void putdatum(struct dc_fnetnode *fn, struct dc_fnetpeerdatum *datum)
641 {
642     if(--datum->refcount > 0)
643         return;
644     if(datum->next != NULL)
645         datum->next->prev = datum->prev;
646     if(datum->prev != NULL)
647         datum->prev->next = datum->next;
648     if(fn->peerdata == datum)
649         fn->peerdata = datum->next;
650     free(datum->id);
651     free(datum);
652 }
653
654 static void peersetnum(struct dc_fnetpeer *peer, wchar_t *id, int value)
655 {
656     struct dc_fnetpeerdatum *datum;
657     struct dc_fnetpeerdi *di;
658     
659     if((datum = finddatum(peer->fn, id)) == NULL)
660         datum = adddatum(peer->fn, id, DC_FNPD_INT);
661     di = difindoradd(peer, datum);
662     di->d.num = value;
663 }
664
665 static void peersetlnum(struct dc_fnetpeer *peer, wchar_t *id, long long value)
666 {
667     struct dc_fnetpeerdatum *datum;
668     struct dc_fnetpeerdi *di;
669     
670     if((datum = finddatum(peer->fn, id)) == NULL)
671         datum = adddatum(peer->fn, id, DC_FNPD_INT);
672     di = difindoradd(peer, datum);
673     di->d.lnum = value;
674 }
675
676 static void peersetstr(struct dc_fnetpeer *peer, wchar_t *id, wchar_t *value)
677 {
678     struct dc_fnetpeerdatum *datum;
679     struct dc_fnetpeerdi *di;
680     
681     if((datum = finddatum(peer->fn, id)) == NULL)
682         datum = adddatum(peer->fn, id, DC_FNPD_INT);
683     di = difindoradd(peer, datum);
684     if(di->d.str != NULL)
685         free(di->d.str);
686     di->d.str = swcsdup(value);
687 }
688
689 struct dc_fnetpeer *dc_fnetfindpeer(struct dc_fnetnode *fn, wchar_t *id)
690 {
691     struct dc_fnetpeer *peer;
692     
693     for(peer = fn->peers; peer != NULL; peer = peer->next)
694     {
695         if(!wcscmp(peer->id, id))
696             break;
697     }
698     return(peer);
699 }
700
701 static struct dc_fnetpeer *addpeer(struct dc_fnetnode *fn, wchar_t *id, wchar_t *nick)
702 {
703     struct dc_fnetpeer *peer;
704     
705     peer = smalloc(sizeof(*peer));
706     memset(peer, 0, sizeof(*peer));
707     peer->fn = fn;
708     peer->id = swcsdup(id);
709     peer->nick = swcsdup(nick);
710     peer->next = fn->peers;
711     peer->prev = NULL;
712     if(fn->peers != NULL)
713         fn->peers->prev = peer;
714     fn->peers = peer;
715     return(peer);
716 }
717
718 static void delpeer(struct dc_fnetpeer *peer)
719 {
720     int i;
721     
722     if(peer->next != NULL)
723         peer->next->prev = peer->prev;
724     if(peer->prev != NULL)
725         peer->prev->next = peer->next;
726     if(peer->fn->peers == peer)
727         peer->fn->peers = peer->next;
728     free(peer->id);
729     free(peer->nick);
730     for(i = 0; i < peer->dinum; i++)
731     {
732         if((peer->di[i].datum->dt == DC_FNPD_STR) && (peer->di[i].d.str != NULL))
733             free(peer->di[i].d.str);
734         putdatum(peer->fn, peer->di[i].datum);
735     }
736     free(peer);
737 }
738
739 static struct dc_fnetnode *newfn(void)
740 {
741     struct dc_fnetnode *fn;
742     
743     fn = smalloc(sizeof(*fn));
744     memset(fn, 0, sizeof(*fn));
745     fn->id = -1;
746     fn->name = NULL;
747     fn->fnet = NULL;
748     fn->state = fn->numusers = fn->found = 0;
749     fn->destroycb = NULL;
750     fn->udata = NULL;
751     fn->next = dc_fnetnodes;
752     fn->prev = NULL;
753     if(dc_fnetnodes != NULL)
754         dc_fnetnodes->prev = fn;
755     dc_fnetnodes = fn;
756     return(fn);
757 }
758
759 static void freefn(struct dc_fnetnode *fn)
760 {
761     if(fn->next != NULL)
762         fn->next->prev = fn->prev;
763     if(fn->prev != NULL)
764         fn->prev->next = fn->next;
765     if(fn == dc_fnetnodes)
766         dc_fnetnodes = fn->next;
767     if(fn->destroycb != NULL)
768         fn->destroycb(fn);
769     while(fn->peers != NULL)
770         delpeer(fn->peers);
771     while(fn->peerdata != NULL)
772     {
773         fn->peerdata->refcount = 0;
774         putdatum(fn, fn->peerdata);
775     }
776     if(fn->name != NULL)
777         free(fn->name);
778     if(fn->fnet != NULL)
779         free(fn->fnet);
780     free(fn);
781 }
782
783 struct dc_fnetnode *dc_findfnetnode(int id)
784 {
785     struct dc_fnetnode *fn;
786     
787     for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
788     {
789         if(fn->id == id)
790             break;
791     }
792     return(fn);
793 }
794
795 static struct dc_transfer *newtransfer(void)
796 {
797     struct dc_transfer *transfer;
798     
799     transfer = smalloc(sizeof(*transfer));
800     memset(transfer, 0, sizeof(*transfer));
801     transfer->id = -1;
802     transfer->peerid = transfer->peernick = transfer->path = NULL;
803     transfer->state = DC_TRNS_WAITING;
804     transfer->dir = DC_TRNSD_UNKNOWN;
805     transfer->size = -1;
806     transfer->curpos = -1;
807     transfer->destroycb = NULL;
808     transfer->udata = NULL;
809     transfer->next = dc_transfers;
810     transfer->prev = NULL;
811     if(dc_transfers != NULL)
812         dc_transfers->prev = transfer;
813     dc_transfers = transfer;
814     return(transfer);
815 }
816
817 static void freetransfer(struct dc_transfer *transfer)
818 {
819     if(transfer->next != NULL)
820         transfer->next->prev = transfer->prev;
821     if(transfer->prev != NULL)
822         transfer->prev->next = transfer->next;
823     if(transfer == dc_transfers)
824         dc_transfers = transfer->next;
825     if(transfer->destroycb != NULL)
826         transfer->destroycb(transfer);
827     if(transfer->peerid != NULL)
828         free(transfer->peerid);
829     if(transfer->peernick != NULL)
830         free(transfer->peernick);
831     if(transfer->path != NULL)
832         free(transfer->path);
833     free(transfer);
834 }
835
836 struct dc_transfer *dc_findtransfer(int id)
837 {
838     struct dc_transfer *transfer;
839     
840     for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
841     {
842         if(transfer->id == id)
843             break;
844     }
845     return(transfer);
846 }
847
848 static int getfnlistcallback(struct dc_response *resp)
849 {
850     struct dc_intresp *ires;
851     struct gencbdata *data;
852     struct dc_fnetnode *fn, *next;
853     
854     data = resp->data;
855     if(resp->code == 200)
856     {
857         for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
858             fn->found = 0;
859         while((ires = dc_interpret(resp)) != NULL)
860         {
861             if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
862             {
863                 fn->found = 1;
864                 if(fn->fnet != NULL)
865                     free(fn->fnet);
866                 fn->fnet = swcsdup(ires->argv[1].val.str);
867                 if(fn->name != NULL)
868                     free(fn->name);
869                 fn->name = swcsdup(ires->argv[2].val.str);
870                 fn->numusers = ires->argv[3].val.num;
871                 fn->state = ires->argv[4].val.num;
872                 if(fn->pubid != NULL)
873                     free(fn->pubid);
874                 fn->pubid = swcsdup(ires->argv[5].val.str);
875             } else {
876                 fn = newfn();
877                 fn->id = ires->argv[0].val.num;
878                 fn->fnet = swcsdup(ires->argv[1].val.str);
879                 fn->name = swcsdup(ires->argv[2].val.str);
880                 fn->numusers = ires->argv[3].val.num;
881                 fn->state = ires->argv[4].val.num;
882                 fn->pubid = swcsdup(ires->argv[5].val.str);
883                 fn->found = 1;
884             }
885             dc_freeires(ires);
886         }
887         for(fn = dc_fnetnodes; fn != NULL; fn = next)
888         {
889             next = fn->next;
890             if(!fn->found)
891                 freefn(fn);
892         }
893         data->callback(200, data->data);
894         free(resp->data);
895     } else if(resp->code == 201) {
896         while(dc_fnetnodes != NULL)
897             freefn(dc_fnetnodes);
898         data->callback(201, data->data);
899         free(resp->data);
900     } else if(resp->code == 502) {
901         while(dc_fnetnodes != NULL)
902             freefn(dc_fnetnodes);
903         data->callback(502, data->data);
904         free(resp->data);
905     }
906     return(1);
907 }
908
909 static int gettrlistcallback(struct dc_response *resp)
910 {
911     struct dc_intresp *ires;
912     struct gencbdata *data;
913     struct dc_transfer *transfer, *next;
914     
915     data = resp->data;
916     if(resp->code == 200)
917     {
918         for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
919             transfer->found = 0;
920         while((ires = dc_interpret(resp)) != NULL)
921         {
922             if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
923             {
924                 transfer->found = 1;
925                 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
926                 {
927                     if(transfer->path != NULL)
928                         free(transfer->path);
929                     transfer->path = swcsdup(ires->argv[5].val.str);
930                 }
931                 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
932                 {
933                     if(transfer->peerid != NULL)
934                         free(transfer->peerid);
935                     transfer->peerid = swcsdup(ires->argv[3].val.str);
936                 }
937                 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
938                 {
939                     if(transfer->peernick != NULL)
940                         free(transfer->peernick);
941                     transfer->peernick = swcsdup(ires->argv[4].val.str);
942                 }
943                 transfer->dir = ires->argv[1].val.num;
944                 transfer->state = ires->argv[2].val.num;
945                 transfer->size = ires->argv[6].val.num;
946                 transfer->curpos = ires->argv[7].val.num;
947                 if(transfer->hash != NULL)
948                 {
949                     free(transfer->hash);
950                     transfer->hash = NULL;
951                 }
952                 if(wcslen(ires->argv[8].val.str) > 0)
953                     transfer->hash = swcsdup(ires->argv[8].val.str);
954             } else {
955                 transfer = newtransfer();
956                 transfer->id = ires->argv[0].val.num;
957                 transfer->dir = ires->argv[1].val.num;
958                 transfer->state = ires->argv[2].val.num;
959                 transfer->peerid = swcsdup(ires->argv[3].val.str);
960                 transfer->peernick = swcsdup(ires->argv[4].val.str);
961                 transfer->path = swcsdup(ires->argv[5].val.str);
962                 transfer->size = ires->argv[6].val.num;
963                 transfer->curpos = ires->argv[7].val.num;
964                 if(wcslen(ires->argv[8].val.str) > 0)
965                     transfer->hash = swcsdup(ires->argv[8].val.str);
966                 transfer->found = 1;
967             }
968             dc_freeires(ires);
969         }
970         for(transfer = dc_transfers; transfer != NULL; transfer = next)
971         {
972             next = transfer->next;
973             if(!transfer->found)
974                 freetransfer(transfer);
975         }
976         data->callback(200, data->data);
977         free(data);
978     } else if(resp->code == 201) {
979         while(dc_transfers != NULL)
980             freetransfer(dc_transfers);
981         data->callback(201, data->data);
982         free(data);
983     } else if(resp->code == 502) {
984         while(dc_transfers != NULL)
985             freetransfer(dc_transfers);
986         data->callback(502, data->data);
987         free(data);
988     }
989     return(1);
990 }
991
992 static int getpeerlistcallback(struct dc_response *resp)
993 {
994     int i, o;
995     struct dc_fnetnode *fn;
996     struct fnetcbdata *data;
997     struct dc_fnetpeer *peer, *next;
998     struct dc_fnetpeerdatum *datum;
999     
1000     data = resp->data;
1001     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1002     {
1003         data->callback(NULL, -1, data->data);
1004         free(data);
1005         return(1);
1006     }
1007     if(resp->code == 200)
1008     {
1009         for(peer = fn->peers; peer != NULL; peer = peer->next)
1010             peer->found = 0;
1011         for(i = 0; i < resp->numlines; i++)
1012         {
1013             if((peer = dc_fnetfindpeer(fn, resp->rlines[i].argv[1])) == NULL)
1014                 peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1015             peer->found = 1;
1016             for(o = 3; o < resp->rlines[i].argc; o += 2)
1017             {
1018                 if((datum = finddatum(fn, resp->rlines[i].argv[o])) != NULL)
1019                 {
1020                     switch(datum->dt)
1021                     {
1022                     case DC_FNPD_INT:
1023                         peersetnum(peer, datum->id, wcstol(resp->rlines[i].argv[o + 1], NULL, 10));
1024                         break;
1025                     case DC_FNPD_LL:
1026                         peersetlnum(peer, datum->id, wcstoll(resp->rlines[i].argv[o + 1], NULL, 10));
1027                         break;
1028                     case DC_FNPD_STR:
1029                         peersetstr(peer, datum->id, resp->rlines[i].argv[o + 1]);
1030                         break;
1031                     }
1032                 }
1033             }
1034         }
1035         for(peer = fn->peers; peer != NULL; peer = next)
1036         {
1037             next = peer->next;
1038             if(!peer->found)
1039                 delpeer(peer);
1040         }
1041     } else if(resp->code == 201) {
1042         while(fn->peers != NULL)
1043             delpeer(fn->peers);
1044     }
1045     data->callback(fn, resp->code, data->data);
1046     free(data);
1047     return(1);
1048 }
1049
1050 static int getpalistcallback(struct dc_response *resp)
1051 {
1052     struct dc_fnetnode *fn;
1053     struct dc_intresp *ires;
1054     struct fnetcbdata *data;
1055     
1056     data = resp->data;
1057     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1058     {
1059         data->callback(NULL, -1, data->data);
1060         free(data);
1061         return(1);
1062     }
1063     if(resp->code == 200)
1064     {
1065         while((ires = dc_interpret(resp)) != NULL)
1066         {
1067             adddatum(fn, ires->argv[0].val.str, ires->argv[1].val.num);
1068             dc_freeires(ires);
1069         }
1070         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1071     } else if(resp->code == 201) {
1072         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1073     } else {
1074         data->callback(fn, resp->code, data->data);
1075         free(data);
1076     }
1077     return(1);
1078 }
1079
1080 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
1081 {
1082     struct gencbdata *data;
1083     
1084     data = smalloc(sizeof(*data));
1085     data->callback = callback;
1086     data->data = udata;
1087     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
1088 }
1089
1090 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
1091 {
1092     struct gencbdata *data;
1093     
1094     data = smalloc(sizeof(*data));
1095     data->callback = callback;
1096     data->data = udata;
1097     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
1098 }
1099
1100 void dc_getpeerlistasync(struct dc_fnetnode *fn, void (*callback)(struct dc_fnetnode *, int, void *), void *udata)
1101 {
1102     struct fnetcbdata *data;
1103     
1104     data = smalloc(sizeof(*data));
1105     data->callback = callback;
1106     data->fnid = fn->id;
1107     data->data = udata;
1108     dc_queuecmd(getpalistcallback, data, L"lspa", L"%%i", fn->id, NULL);
1109 }
1110
1111 void dc_uimisc_disconnected(void)
1112 {
1113     while(dc_fnetnodes != NULL)
1114         freefn(dc_fnetnodes);
1115     while(dc_transfers != NULL)
1116         freetransfer(dc_transfers);
1117 }
1118
1119 void dc_uimisc_handlenotify(struct dc_response *resp)
1120 {
1121     int i;
1122     struct dc_fnetnode *fn;
1123     struct dc_transfer *transfer;
1124     struct dc_fnetpeer *peer;
1125     struct dc_intresp *ires;
1126     
1127     if((ires = dc_interpret(resp)) == NULL)
1128         return;
1129     switch(resp->code)
1130     {
1131     case 601:
1132         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1133             fn->state = ires->argv[1].val.num;
1134         break;
1135     case 602:
1136         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1137         {
1138             if(fn->name != NULL)
1139                 free(fn->name);
1140             fn->name = swcsdup(ires->argv[1].val.str);
1141         }
1142         break;
1143     case 603:
1144         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1145             freefn(fn);
1146         break;
1147     case 604:
1148         fn = newfn();
1149         fn->id = ires->argv[0].val.num;
1150         if(fn->fnet != NULL)
1151             free(fn->fnet);
1152         fn->fnet = swcsdup(ires->argv[1].val.str);
1153         fn->state = DC_FNN_STATE_SYN;
1154         fn->numusers = 0;
1155         break;
1156     case 605:
1157         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1158             fn->numusers = ires->argv[1].val.num;
1159         break;
1160     case 610:
1161         transfer = newtransfer();
1162         transfer->id = ires->argv[0].val.num;
1163         transfer->dir = ires->argv[1].val.num;
1164         if(transfer->dir == DC_TRNSD_UP)
1165             transfer->state = DC_TRNS_HS;
1166         transfer->peerid = swcsdup(ires->argv[2].val.str);
1167         if(ires->argv[3].val.str[0])
1168             transfer->path = swcsdup(ires->argv[3].val.str);
1169         break;
1170     case 611:
1171         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1172             transfer->state = ires->argv[1].val.num;
1173         break;
1174     case 612:
1175         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1176         {
1177             if(transfer->peernick != NULL)
1178                 free(transfer->peernick);
1179             transfer->peernick = swcsdup(ires->argv[1].val.str);
1180         }
1181         break;
1182     case 613:
1183         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1184             transfer->size = ires->argv[1].val.num;
1185         break;
1186     case 614:
1187         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1188         {
1189             transfer->error = ires->argv[1].val.num;
1190             time(&transfer->errortime);
1191         }
1192         break;
1193     case 615:
1194         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1195             transfer->curpos = ires->argv[1].val.num;
1196         break;
1197     case 616:
1198         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1199         {
1200             if(transfer->path != NULL)
1201                 free(transfer->path);
1202             transfer->path = swcsdup(ires->argv[1].val.str);
1203         }
1204         break;
1205     case 617:
1206         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1207             freetransfer(transfer);
1208         break;
1209     case 618:
1210         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1211         {
1212             if(transfer->hash != NULL)
1213             {
1214                 free(transfer->hash);
1215                 transfer->hash = NULL;
1216             }
1217             if(wcslen(ires->argv[1].val.str) > 0)
1218                 transfer->hash = swcsdup(ires->argv[1].val.str);
1219         }
1220         break;
1221     case 630:
1222         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1223         {
1224             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) == NULL)
1225             {
1226                 peer = addpeer(fn, ires->argv[1].val.str, ires->argv[2].val.str);
1227                 if(fn->newpeercb != NULL)
1228                     fn->newpeercb(peer);
1229             }
1230         }
1231         break;
1232     case 631:
1233         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1234         {
1235             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1236             {
1237                 if(fn->delpeercb != NULL)
1238                     fn->delpeercb(peer);
1239                 delpeer(peer);
1240             }
1241         }
1242         break;
1243     case 632:
1244         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1245         {
1246             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1247             {
1248                 if(wcscmp(ires->argv[2].val.str, peer->nick))
1249                 {
1250                     free(peer->nick);
1251                     peer->nick = swcsdup(ires->argv[2].val.str);
1252                 }
1253                 for(i = 4; i < resp->rlines[0].argc; i += 3)
1254                 {
1255                     switch(wcstol(resp->rlines[0].argv[i + 1], NULL, 10))
1256                     {
1257                     case DC_FNPD_INT:
1258                         peersetnum(peer, resp->rlines[0].argv[i], wcstol(resp->rlines[0].argv[i + 2], NULL, 10));
1259                         break;
1260                     case DC_FNPD_LL:
1261                         peersetlnum(peer, resp->rlines[0].argv[i], wcstoll(resp->rlines[0].argv[i + 2], NULL, 10));
1262                         break;
1263                     case DC_FNPD_STR:
1264                         peersetstr(peer, resp->rlines[0].argv[i], resp->rlines[0].argv[i + 2]);
1265                         break;
1266                     }
1267                 }
1268                 if(fn->chpeercb != NULL)
1269                     fn->chpeercb(peer);
1270             }
1271         }
1272         break;
1273     default:
1274         break;
1275     }
1276     dc_freeires(ires);
1277     resp->curline = 0;
1278 }
1279
1280 /* Note the backspace handling - it's not as elegant as possible, but
1281  * it helps avoid the "box-of-toothpicks" syndrome when writing search
1282  * expressions manually. */
1283 wchar_t **dc_lexsexpr(wchar_t *sexpr)
1284 {
1285     wchar_t **ret;
1286     wchar_t *buf;
1287     size_t retsize, retdata, bufsize, bufdata;
1288     int state;
1289     
1290     ret = NULL;
1291     buf = NULL;
1292     retsize = retdata = bufsize = bufdata = 0;
1293     state = 0;
1294     while(*sexpr != L'\0')
1295     {
1296         switch(state)
1297         {
1298         case 0:
1299             if(!iswspace(*sexpr))
1300                 state = 1;
1301             else
1302                 sexpr++;
1303             break;
1304         case 1:
1305             if(iswspace(*sexpr))
1306             {
1307                 if(buf != NULL)
1308                 {
1309                     addtobuf(buf, L'\0');
1310                     addtobuf(ret, buf);
1311                     buf = NULL;
1312                     bufsize = bufdata = 0;
1313                 }
1314                 state = 0;
1315             } else if((*sexpr == L'(') ||
1316                       (*sexpr == L')') ||
1317                       (*sexpr == L'&') ||
1318                       (*sexpr == L'|') ||
1319                       (*sexpr == L'!')) {
1320                 if(buf != NULL)
1321                 {
1322                     addtobuf(buf, L'\0');
1323                     addtobuf(ret, buf);
1324                     buf = NULL;
1325                     bufsize = bufdata = 0;
1326                 }
1327                 addtobuf(buf, *sexpr);
1328                 addtobuf(buf, L'\0');
1329                 addtobuf(ret, buf);
1330                 buf = NULL;
1331                 bufsize = bufdata = 0;
1332                 sexpr++;
1333             } else if(*sexpr == L'\"') {
1334                 sexpr++;
1335                 state = 2;
1336             } else if(*sexpr == L'\\') {
1337                 sexpr++;
1338                 if(*sexpr == L'\0')
1339                 {
1340                     addtobuf(buf, *sexpr);
1341                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1342                     addtobuf(buf, *sexpr);
1343                     sexpr++;
1344                 } else {
1345                     addtobuf(buf, L'\\');
1346                     addtobuf(buf, *sexpr);
1347                     sexpr++;
1348                 }
1349             } else {
1350                 addtobuf(buf, *(sexpr++));
1351             }
1352             break;
1353         case 2:
1354             if(*sexpr == L'\\')
1355             {
1356                 sexpr++;
1357                 if(*sexpr == L'\0')
1358                 {
1359                     addtobuf(buf, *sexpr);
1360                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1361                     addtobuf(buf, *sexpr);
1362                     sexpr++;
1363                 } else {
1364                     addtobuf(buf, L'\\');
1365                     addtobuf(buf, *sexpr);
1366                     sexpr++;
1367                 }
1368             } else if(*sexpr == L'\"') {
1369                 state = 1;
1370                 sexpr++;
1371             } else {
1372                 addtobuf(buf, *(sexpr++));
1373             }
1374             break;
1375         }
1376     }
1377     if(buf != NULL)
1378     {
1379         addtobuf(buf, L'\0');
1380         addtobuf(ret, buf);
1381     }
1382     addtobuf(ret, NULL);
1383     return(ret);
1384 }
1385
1386 void dc_freewcsarr(wchar_t **arr)
1387 {
1388     wchar_t **buf;
1389     
1390     if(arr == NULL)
1391         return;
1392     for(buf = arr; *buf != NULL; buf++)
1393         free(*buf);
1394     free(arr);
1395 }