4232453118ab7e1a81b636d28871b0b7f0e5efcd
[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 #include <stdlib.h>
22 /* I'm very unsure about this, but for now it defines wcstoll (which
23  * should be defined anyway) and doesn't break anything... let's keep
24  * two eyes wide open, though. */
25 #define __USE_ISOC99
26 #include <wchar.h>
27 #include <wctype.h>
28 #include <pwd.h>
29 #include <string.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 sortlist1(const struct dc_respline *l1, const struct dc_respline *l2)
993 {
994     return(wcscmp(l1->argv[1], l2->argv[1]));
995 }
996
997 static int sortlist2(const struct dc_fnetpeer **p1, const struct dc_fnetpeer **p2)
998 {
999     return(wcscmp((*p1)->id, (*p2)->id));
1000 }
1001
1002 static void fillpeer(struct dc_fnetpeer *peer, struct dc_respline *r)
1003 {
1004     int i;
1005     struct dc_fnetpeerdatum *datum;
1006     
1007     for(i = 3; i < r->argc; i += 2)
1008     {
1009         if((datum = finddatum(peer->fn, r->argv[i])) != NULL)
1010         {
1011             switch(datum->dt)
1012             {
1013             case DC_FNPD_INT:
1014                 peersetnum(peer, datum->id, wcstol(r->argv[i + 1], NULL, 10));
1015                 break;
1016             case DC_FNPD_LL:
1017                 peersetlnum(peer, datum->id, wcstoll(r->argv[i + 1], NULL, 10));
1018                 break;
1019             case DC_FNPD_STR:
1020                 peersetstr(peer, datum->id, r->argv[i + 1]);
1021                 break;
1022             }
1023         }
1024     }
1025 }
1026
1027 static int getpeerlistcallback(struct dc_response *resp)
1028 {
1029     int i, o, c;
1030     struct dc_fnetnode *fn;
1031     struct fnetcbdata *data;
1032     struct dc_fnetpeer *peer;
1033     struct dc_fnetpeer **plist;
1034     size_t plistsize, plistdata;
1035     
1036     data = resp->data;
1037     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1038     {
1039         data->callback(NULL, -1, data->data);
1040         free(data);
1041         return(1);
1042     }
1043     if(resp->code == 200)
1044     {
1045         qsort(resp->rlines, resp->numlines, sizeof(*resp->rlines), (int (*)(const void *, const void *))sortlist1);
1046         plist = NULL;
1047         plistsize = plistdata = 0;
1048         for(i = 0, peer = fn->peers; peer != NULL; peer = peer->next)
1049             addtobuf(plist, peer);
1050         qsort(plist, plistdata, sizeof(*plist), (int (*)(const void *, const void *))sortlist2);
1051         i = o = 0;
1052         while(1)
1053         {
1054             if((i < resp->numlines) && (o < plistdata))
1055             {
1056                 c = wcscmp(resp->rlines[i].argv[1], plist[o]->id);
1057                 if(c < 0)
1058                 {
1059                     peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1060                     fillpeer(peer, resp->rlines + i);
1061                     i++;
1062                 } else if(c > 0) {
1063                     delpeer(plist[o]);
1064                     o++;
1065                 } else {
1066                     fillpeer(plist[o], resp->rlines + i);
1067                     i++;
1068                     o++;
1069                 }
1070             } else if(i < resp->numlines) {
1071                 peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1072                 fillpeer(peer, resp->rlines + i);
1073                 i++;
1074             } else if(o < plistdata) {
1075                 delpeer(plist[o]);
1076                 o++;
1077             } else {
1078                 break;
1079             }
1080         }
1081         free(plist);
1082     } else if(resp->code == 201) {
1083         while(fn->peers != NULL)
1084             delpeer(fn->peers);
1085     }
1086     data->callback(fn, resp->code, data->data);
1087     free(data);
1088     return(1);
1089 }
1090
1091 static int getpalistcallback(struct dc_response *resp)
1092 {
1093     struct dc_fnetnode *fn;
1094     struct dc_intresp *ires;
1095     struct fnetcbdata *data;
1096     
1097     data = resp->data;
1098     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1099     {
1100         data->callback(NULL, -1, data->data);
1101         free(data);
1102         return(1);
1103     }
1104     if(resp->code == 200)
1105     {
1106         while((ires = dc_interpret(resp)) != NULL)
1107         {
1108             adddatum(fn, ires->argv[0].val.str, ires->argv[1].val.num);
1109             dc_freeires(ires);
1110         }
1111         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1112     } else if(resp->code == 201) {
1113         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1114     } else {
1115         data->callback(fn, resp->code, data->data);
1116         free(data);
1117     }
1118     return(1);
1119 }
1120
1121 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
1122 {
1123     struct gencbdata *data;
1124     
1125     data = smalloc(sizeof(*data));
1126     data->callback = callback;
1127     data->data = udata;
1128     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
1129 }
1130
1131 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
1132 {
1133     struct gencbdata *data;
1134     
1135     data = smalloc(sizeof(*data));
1136     data->callback = callback;
1137     data->data = udata;
1138     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
1139 }
1140
1141 void dc_getpeerlistasync(struct dc_fnetnode *fn, void (*callback)(struct dc_fnetnode *, int, void *), void *udata)
1142 {
1143     struct fnetcbdata *data;
1144     
1145     data = smalloc(sizeof(*data));
1146     data->callback = callback;
1147     data->fnid = fn->id;
1148     data->data = udata;
1149     dc_queuecmd(getpalistcallback, data, L"lspa", L"%%i", fn->id, NULL);
1150 }
1151
1152 void dc_uimisc_disconnected(void)
1153 {
1154     while(dc_fnetnodes != NULL)
1155         freefn(dc_fnetnodes);
1156     while(dc_transfers != NULL)
1157         freetransfer(dc_transfers);
1158 }
1159
1160 void dc_uimisc_handlenotify(struct dc_response *resp)
1161 {
1162     int i;
1163     struct dc_fnetnode *fn;
1164     struct dc_transfer *transfer;
1165     struct dc_fnetpeer *peer;
1166     struct dc_intresp *ires;
1167     
1168     if((ires = dc_interpret(resp)) == NULL)
1169         return;
1170     switch(resp->code)
1171     {
1172     case 601:
1173         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1174             fn->state = ires->argv[1].val.num;
1175         break;
1176     case 602:
1177         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1178         {
1179             if(fn->name != NULL)
1180                 free(fn->name);
1181             fn->name = swcsdup(ires->argv[1].val.str);
1182         }
1183         break;
1184     case 603:
1185         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1186             freefn(fn);
1187         break;
1188     case 604:
1189         fn = newfn();
1190         fn->id = ires->argv[0].val.num;
1191         if(fn->fnet != NULL)
1192             free(fn->fnet);
1193         fn->fnet = swcsdup(ires->argv[1].val.str);
1194         fn->state = DC_FNN_STATE_SYN;
1195         fn->numusers = 0;
1196         break;
1197     case 605:
1198         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1199             fn->numusers = ires->argv[1].val.num;
1200         break;
1201     case 610:
1202         transfer = newtransfer();
1203         transfer->id = ires->argv[0].val.num;
1204         transfer->dir = ires->argv[1].val.num;
1205         if(transfer->dir == DC_TRNSD_UP)
1206             transfer->state = DC_TRNS_HS;
1207         transfer->peerid = swcsdup(ires->argv[2].val.str);
1208         if(ires->argv[3].val.str[0])
1209             transfer->path = swcsdup(ires->argv[3].val.str);
1210         break;
1211     case 611:
1212         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1213             transfer->state = ires->argv[1].val.num;
1214         break;
1215     case 612:
1216         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1217         {
1218             if(transfer->peernick != NULL)
1219                 free(transfer->peernick);
1220             transfer->peernick = swcsdup(ires->argv[1].val.str);
1221         }
1222         break;
1223     case 613:
1224         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1225             transfer->size = ires->argv[1].val.num;
1226         break;
1227     case 614:
1228         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1229         {
1230             transfer->error = ires->argv[1].val.num;
1231             time(&transfer->errortime);
1232         }
1233         break;
1234     case 615:
1235         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1236             transfer->curpos = ires->argv[1].val.num;
1237         break;
1238     case 616:
1239         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1240         {
1241             if(transfer->path != NULL)
1242                 free(transfer->path);
1243             transfer->path = swcsdup(ires->argv[1].val.str);
1244         }
1245         break;
1246     case 617:
1247         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1248             freetransfer(transfer);
1249         break;
1250     case 618:
1251         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1252         {
1253             if(transfer->hash != NULL)
1254             {
1255                 free(transfer->hash);
1256                 transfer->hash = NULL;
1257             }
1258             if(wcslen(ires->argv[1].val.str) > 0)
1259                 transfer->hash = swcsdup(ires->argv[1].val.str);
1260         }
1261         break;
1262     case 630:
1263         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1264         {
1265             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) == NULL)
1266             {
1267                 peer = addpeer(fn, ires->argv[1].val.str, ires->argv[2].val.str);
1268                 if(fn->newpeercb != NULL)
1269                     fn->newpeercb(peer);
1270             }
1271         }
1272         break;
1273     case 631:
1274         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1275         {
1276             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1277             {
1278                 if(fn->delpeercb != NULL)
1279                     fn->delpeercb(peer);
1280                 delpeer(peer);
1281             }
1282         }
1283         break;
1284     case 632:
1285         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1286         {
1287             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1288             {
1289                 if(wcscmp(ires->argv[2].val.str, peer->nick))
1290                 {
1291                     free(peer->nick);
1292                     peer->nick = swcsdup(ires->argv[2].val.str);
1293                 }
1294                 for(i = 4; i < resp->rlines[0].argc; i += 3)
1295                 {
1296                     switch(wcstol(resp->rlines[0].argv[i + 1], NULL, 10))
1297                     {
1298                     case DC_FNPD_INT:
1299                         peersetnum(peer, resp->rlines[0].argv[i], wcstol(resp->rlines[0].argv[i + 2], NULL, 10));
1300                         break;
1301                     case DC_FNPD_LL:
1302                         peersetlnum(peer, resp->rlines[0].argv[i], wcstoll(resp->rlines[0].argv[i + 2], NULL, 10));
1303                         break;
1304                     case DC_FNPD_STR:
1305                         peersetstr(peer, resp->rlines[0].argv[i], resp->rlines[0].argv[i + 2]);
1306                         break;
1307                     }
1308                 }
1309                 if(fn->chpeercb != NULL)
1310                     fn->chpeercb(peer);
1311             }
1312         }
1313         break;
1314     default:
1315         break;
1316     }
1317     dc_freeires(ires);
1318     resp->curline = 0;
1319 }
1320
1321 /* Note the backspace handling - it's not as elegant as possible, but
1322  * it helps avoid the "box-of-toothpicks" syndrome when writing search
1323  * expressions manually. */
1324 wchar_t **dc_lexsexpr(wchar_t *sexpr)
1325 {
1326     wchar_t **ret;
1327     wchar_t *buf;
1328     size_t retsize, retdata, bufsize, bufdata;
1329     int state;
1330     
1331     ret = NULL;
1332     buf = NULL;
1333     retsize = retdata = bufsize = bufdata = 0;
1334     state = 0;
1335     while(*sexpr != L'\0')
1336     {
1337         switch(state)
1338         {
1339         case 0:
1340             if(!iswspace(*sexpr))
1341                 state = 1;
1342             else
1343                 sexpr++;
1344             break;
1345         case 1:
1346             if(iswspace(*sexpr))
1347             {
1348                 if(buf != NULL)
1349                 {
1350                     addtobuf(buf, L'\0');
1351                     addtobuf(ret, buf);
1352                     buf = NULL;
1353                     bufsize = bufdata = 0;
1354                 }
1355                 state = 0;
1356             } else if((*sexpr == L'(') ||
1357                       (*sexpr == L')') ||
1358                       (*sexpr == L'&') ||
1359                       (*sexpr == L'|') ||
1360                       (*sexpr == L'!')) {
1361                 if(buf != NULL)
1362                 {
1363                     addtobuf(buf, L'\0');
1364                     addtobuf(ret, buf);
1365                     buf = NULL;
1366                     bufsize = bufdata = 0;
1367                 }
1368                 addtobuf(buf, *sexpr);
1369                 addtobuf(buf, L'\0');
1370                 addtobuf(ret, buf);
1371                 buf = NULL;
1372                 bufsize = bufdata = 0;
1373                 sexpr++;
1374             } else if(*sexpr == L'\"') {
1375                 sexpr++;
1376                 state = 2;
1377             } else if(*sexpr == L'\\') {
1378                 sexpr++;
1379                 if(*sexpr == L'\0')
1380                 {
1381                     addtobuf(buf, *sexpr);
1382                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1383                     addtobuf(buf, *sexpr);
1384                     sexpr++;
1385                 } else {
1386                     addtobuf(buf, L'\\');
1387                     addtobuf(buf, *sexpr);
1388                     sexpr++;
1389                 }
1390             } else {
1391                 addtobuf(buf, *(sexpr++));
1392             }
1393             break;
1394         case 2:
1395             if(*sexpr == L'\\')
1396             {
1397                 sexpr++;
1398                 if(*sexpr == L'\0')
1399                 {
1400                     addtobuf(buf, *sexpr);
1401                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1402                     addtobuf(buf, *sexpr);
1403                     sexpr++;
1404                 } else {
1405                     addtobuf(buf, L'\\');
1406                     addtobuf(buf, *sexpr);
1407                     sexpr++;
1408                 }
1409             } else if(*sexpr == L'\"') {
1410                 state = 1;
1411                 sexpr++;
1412             } else {
1413                 addtobuf(buf, *(sexpr++));
1414             }
1415             break;
1416         }
1417     }
1418     if(buf != NULL)
1419     {
1420         addtobuf(buf, L'\0');
1421         addtobuf(ret, buf);
1422     }
1423     addtobuf(ret, NULL);
1424     return(ret);
1425 }
1426
1427 void dc_freewcsarr(wchar_t **arr)
1428 {
1429     wchar_t **buf;
1430     
1431     if(arr == NULL)
1432         return;
1433     for(buf = arr; *buf != NULL; buf++)
1434         free(*buf);
1435     free(arr);
1436 }