e0a37ef0caed7fb4b78f28148f811194d727571f
[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             } else {
873                 fn = newfn();
874                 fn->id = ires->argv[0].val.num;
875                 fn->fnet = swcsdup(ires->argv[1].val.str);
876                 fn->name = swcsdup(ires->argv[2].val.str);
877                 fn->numusers = ires->argv[3].val.num;
878                 fn->state = ires->argv[4].val.num;
879                 fn->found = 1;
880             }
881             dc_freeires(ires);
882         }
883         for(fn = dc_fnetnodes; fn != NULL; fn = next)
884         {
885             next = fn->next;
886             if(!fn->found)
887                 freefn(fn);
888         }
889         data->callback(200, data->data);
890         free(resp->data);
891     } else if(resp->code == 201) {
892         while(dc_fnetnodes != NULL)
893             freefn(dc_fnetnodes);
894         data->callback(201, data->data);
895         free(resp->data);
896     } else if(resp->code == 502) {
897         while(dc_fnetnodes != NULL)
898             freefn(dc_fnetnodes);
899         data->callback(502, data->data);
900         free(resp->data);
901     }
902     return(1);
903 }
904
905 static int gettrlistcallback(struct dc_response *resp)
906 {
907     struct dc_intresp *ires;
908     struct gencbdata *data;
909     struct dc_transfer *transfer, *next;
910     
911     data = resp->data;
912     if(resp->code == 200)
913     {
914         for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
915             transfer->found = 0;
916         while((ires = dc_interpret(resp)) != NULL)
917         {
918             if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
919             {
920                 transfer->found = 1;
921                 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
922                 {
923                     if(transfer->path != NULL)
924                         free(transfer->path);
925                     transfer->path = swcsdup(ires->argv[5].val.str);
926                 }
927                 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
928                 {
929                     if(transfer->peerid != NULL)
930                         free(transfer->peerid);
931                     transfer->peerid = swcsdup(ires->argv[3].val.str);
932                 }
933                 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
934                 {
935                     if(transfer->peernick != NULL)
936                         free(transfer->peernick);
937                     transfer->peernick = swcsdup(ires->argv[4].val.str);
938                 }
939                 transfer->dir = ires->argv[1].val.num;
940                 transfer->state = ires->argv[2].val.num;
941                 transfer->size = ires->argv[6].val.num;
942                 transfer->curpos = ires->argv[7].val.num;
943                 if(transfer->hash != NULL)
944                 {
945                     free(transfer->hash);
946                     transfer->hash = NULL;
947                 }
948                 if(wcslen(ires->argv[8].val.str) > 0)
949                     transfer->hash = swcsdup(ires->argv[8].val.str);
950             } else {
951                 transfer = newtransfer();
952                 transfer->id = ires->argv[0].val.num;
953                 transfer->dir = ires->argv[1].val.num;
954                 transfer->state = ires->argv[2].val.num;
955                 transfer->peerid = swcsdup(ires->argv[3].val.str);
956                 transfer->peernick = swcsdup(ires->argv[4].val.str);
957                 transfer->path = swcsdup(ires->argv[5].val.str);
958                 transfer->size = ires->argv[6].val.num;
959                 transfer->curpos = ires->argv[7].val.num;
960                 if(wcslen(ires->argv[8].val.str) > 0)
961                     transfer->hash = swcsdup(ires->argv[8].val.str);
962                 transfer->found = 1;
963             }
964             dc_freeires(ires);
965         }
966         for(transfer = dc_transfers; transfer != NULL; transfer = next)
967         {
968             next = transfer->next;
969             if(!transfer->found)
970                 freetransfer(transfer);
971         }
972         data->callback(200, data->data);
973         free(data);
974     } else if(resp->code == 201) {
975         while(dc_transfers != NULL)
976             freetransfer(dc_transfers);
977         data->callback(201, data->data);
978         free(data);
979     } else if(resp->code == 502) {
980         while(dc_transfers != NULL)
981             freetransfer(dc_transfers);
982         data->callback(502, data->data);
983         free(data);
984     }
985     return(1);
986 }
987
988 static int getpeerlistcallback(struct dc_response *resp)
989 {
990     int i, o;
991     struct dc_fnetnode *fn;
992     struct fnetcbdata *data;
993     struct dc_fnetpeer *peer, *next;
994     struct dc_fnetpeerdatum *datum;
995     
996     data = resp->data;
997     if((fn = dc_findfnetnode(data->fnid)) == NULL)
998     {
999         data->callback(NULL, -1, data->data);
1000         free(data);
1001         return(1);
1002     }
1003     if(resp->code == 200)
1004     {
1005         for(peer = fn->peers; peer != NULL; peer = peer->next)
1006             peer->found = 0;
1007         for(i = 0; i < resp->numlines; i++)
1008         {
1009             if((peer = dc_fnetfindpeer(fn, resp->rlines[i].argv[1])) == NULL)
1010                 peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1011             peer->found = 1;
1012             for(o = 3; o < resp->rlines[i].argc; o += 2)
1013             {
1014                 if((datum = finddatum(fn, resp->rlines[i].argv[o])) != NULL)
1015                 {
1016                     switch(datum->dt)
1017                     {
1018                     case DC_FNPD_INT:
1019                         peersetnum(peer, datum->id, wcstol(resp->rlines[i].argv[o + 1], NULL, 10));
1020                         break;
1021                     case DC_FNPD_LL:
1022                         peersetlnum(peer, datum->id, wcstoll(resp->rlines[i].argv[o + 1], NULL, 10));
1023                         break;
1024                     case DC_FNPD_STR:
1025                         peersetstr(peer, datum->id, resp->rlines[i].argv[o + 1]);
1026                         break;
1027                     }
1028                 }
1029             }
1030         }
1031         for(peer = fn->peers; peer != NULL; peer = next)
1032         {
1033             next = peer->next;
1034             if(!peer->found)
1035                 delpeer(peer);
1036         }
1037     } else if(resp->code == 201) {
1038         while(fn->peers != NULL)
1039             delpeer(fn->peers);
1040     }
1041     data->callback(fn, resp->code, data->data);
1042     free(data);
1043     return(1);
1044 }
1045
1046 static int getpalistcallback(struct dc_response *resp)
1047 {
1048     struct dc_fnetnode *fn;
1049     struct dc_intresp *ires;
1050     struct fnetcbdata *data;
1051     
1052     data = resp->data;
1053     if((fn = dc_findfnetnode(data->fnid)) == NULL)
1054     {
1055         data->callback(NULL, -1, data->data);
1056         free(data);
1057         return(1);
1058     }
1059     if(resp->code == 200)
1060     {
1061         while((ires = dc_interpret(resp)) != NULL)
1062         {
1063             adddatum(fn, ires->argv[0].val.str, ires->argv[1].val.num);
1064             dc_freeires(ires);
1065         }
1066         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1067     } else if(resp->code == 201) {
1068         dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1069     } else {
1070         data->callback(fn, resp->code, data->data);
1071         free(data);
1072     }
1073     return(1);
1074 }
1075
1076 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
1077 {
1078     struct gencbdata *data;
1079     
1080     data = smalloc(sizeof(*data));
1081     data->callback = callback;
1082     data->data = udata;
1083     dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
1084 }
1085
1086 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
1087 {
1088     struct gencbdata *data;
1089     
1090     data = smalloc(sizeof(*data));
1091     data->callback = callback;
1092     data->data = udata;
1093     dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
1094 }
1095
1096 void dc_getpeerlistasync(struct dc_fnetnode *fn, void (*callback)(struct dc_fnetnode *, int, void *), void *udata)
1097 {
1098     struct fnetcbdata *data;
1099     
1100     data = smalloc(sizeof(*data));
1101     data->callback = callback;
1102     data->fnid = fn->id;
1103     data->data = udata;
1104     dc_queuecmd(getpalistcallback, data, L"lspa", L"%%i", fn->id, NULL);
1105 }
1106
1107 void dc_uimisc_disconnected(void)
1108 {
1109     while(dc_fnetnodes != NULL)
1110         freefn(dc_fnetnodes);
1111     while(dc_transfers != NULL)
1112         freetransfer(dc_transfers);
1113 }
1114
1115 void dc_uimisc_handlenotify(struct dc_response *resp)
1116 {
1117     int i;
1118     struct dc_fnetnode *fn;
1119     struct dc_transfer *transfer;
1120     struct dc_fnetpeer *peer;
1121     struct dc_intresp *ires;
1122     
1123     if((ires = dc_interpret(resp)) == NULL)
1124         return;
1125     switch(resp->code)
1126     {
1127     case 601:
1128         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1129             fn->state = ires->argv[1].val.num;
1130         break;
1131     case 602:
1132         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1133         {
1134             if(fn->name != NULL)
1135                 free(fn->name);
1136             fn->name = swcsdup(ires->argv[1].val.str);
1137         }
1138         break;
1139     case 603:
1140         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1141             freefn(fn);
1142         break;
1143     case 604:
1144         fn = newfn();
1145         fn->id = ires->argv[0].val.num;
1146         if(fn->fnet != NULL)
1147             free(fn->fnet);
1148         fn->fnet = swcsdup(ires->argv[1].val.str);
1149         fn->state = DC_FNN_STATE_SYN;
1150         fn->numusers = 0;
1151         break;
1152     case 605:
1153         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1154             fn->numusers = ires->argv[1].val.num;
1155         break;
1156     case 610:
1157         transfer = newtransfer();
1158         transfer->id = ires->argv[0].val.num;
1159         transfer->dir = ires->argv[1].val.num;
1160         if(transfer->dir == DC_TRNSD_UP)
1161             transfer->state = DC_TRNS_HS;
1162         transfer->peerid = swcsdup(ires->argv[2].val.str);
1163         if(ires->argv[3].val.str[0])
1164             transfer->path = swcsdup(ires->argv[3].val.str);
1165         break;
1166     case 611:
1167         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1168             transfer->state = ires->argv[1].val.num;
1169         break;
1170     case 612:
1171         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1172         {
1173             if(transfer->peernick != NULL)
1174                 free(transfer->peernick);
1175             transfer->peernick = swcsdup(ires->argv[1].val.str);
1176         }
1177         break;
1178     case 613:
1179         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1180             transfer->size = ires->argv[1].val.num;
1181         break;
1182     case 614:
1183         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1184         {
1185             transfer->error = ires->argv[1].val.num;
1186             time(&transfer->errortime);
1187         }
1188         break;
1189     case 615:
1190         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1191             transfer->curpos = ires->argv[1].val.num;
1192         break;
1193     case 616:
1194         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1195         {
1196             if(transfer->path != NULL)
1197                 free(transfer->path);
1198             transfer->path = swcsdup(ires->argv[1].val.str);
1199         }
1200         break;
1201     case 617:
1202         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1203             freetransfer(transfer);
1204         break;
1205     case 618:
1206         if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1207         {
1208             if(transfer->hash != NULL)
1209             {
1210                 free(transfer->hash);
1211                 transfer->hash = NULL;
1212             }
1213             if(wcslen(ires->argv[1].val.str) > 0)
1214                 transfer->hash = swcsdup(ires->argv[1].val.str);
1215         }
1216         break;
1217     case 630:
1218         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1219         {
1220             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) == NULL)
1221             {
1222                 peer = addpeer(fn, ires->argv[1].val.str, ires->argv[2].val.str);
1223                 if(fn->newpeercb != NULL)
1224                     fn->newpeercb(peer);
1225             }
1226         }
1227         break;
1228     case 631:
1229         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1230         {
1231             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1232             {
1233                 if(fn->delpeercb != NULL)
1234                     fn->delpeercb(peer);
1235                 delpeer(peer);
1236             }
1237         }
1238         break;
1239     case 632:
1240         if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1241         {
1242             if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1243             {
1244                 if(wcscmp(ires->argv[2].val.str, peer->nick))
1245                 {
1246                     free(peer->nick);
1247                     peer->nick = swcsdup(ires->argv[2].val.str);
1248                 }
1249                 for(i = 4; i < resp->rlines[0].argc; i += 3)
1250                 {
1251                     switch(wcstol(resp->rlines[0].argv[i + 1], NULL, 10))
1252                     {
1253                     case DC_FNPD_INT:
1254                         peersetnum(peer, resp->rlines[0].argv[i], wcstol(resp->rlines[0].argv[i + 2], NULL, 10));
1255                         break;
1256                     case DC_FNPD_LL:
1257                         peersetlnum(peer, resp->rlines[0].argv[i], wcstoll(resp->rlines[0].argv[i + 2], NULL, 10));
1258                         break;
1259                     case DC_FNPD_STR:
1260                         peersetstr(peer, resp->rlines[0].argv[i], resp->rlines[0].argv[i + 2]);
1261                         break;
1262                     }
1263                 }
1264                 if(fn->chpeercb != NULL)
1265                     fn->chpeercb(peer);
1266             }
1267         }
1268         break;
1269     default:
1270         break;
1271     }
1272     dc_freeires(ires);
1273     resp->curline = 0;
1274 }
1275
1276 /* Note the backspace handling - it's not as elegant as possible, but
1277  * it helps avoid the "box-of-toothpicks" syndrome when writing search
1278  * expressions manually. */
1279 wchar_t **dc_lexsexpr(wchar_t *sexpr)
1280 {
1281     wchar_t **ret;
1282     wchar_t *buf;
1283     size_t retsize, retdata, bufsize, bufdata;
1284     int state;
1285     
1286     ret = NULL;
1287     buf = NULL;
1288     retsize = retdata = bufsize = bufdata = 0;
1289     state = 0;
1290     while(*sexpr != L'\0')
1291     {
1292         switch(state)
1293         {
1294         case 0:
1295             if(!iswspace(*sexpr))
1296                 state = 1;
1297             else
1298                 sexpr++;
1299             break;
1300         case 1:
1301             if(iswspace(*sexpr))
1302             {
1303                 if(buf != NULL)
1304                 {
1305                     addtobuf(buf, L'\0');
1306                     addtobuf(ret, buf);
1307                     buf = NULL;
1308                     bufsize = bufdata = 0;
1309                 }
1310                 state = 0;
1311             } else if((*sexpr == L'(') ||
1312                       (*sexpr == L')') ||
1313                       (*sexpr == L'&') ||
1314                       (*sexpr == L'|') ||
1315                       (*sexpr == L'!')) {
1316                 if(buf != NULL)
1317                 {
1318                     addtobuf(buf, L'\0');
1319                     addtobuf(ret, buf);
1320                     buf = NULL;
1321                     bufsize = bufdata = 0;
1322                 }
1323                 addtobuf(buf, *sexpr);
1324                 addtobuf(buf, L'\0');
1325                 addtobuf(ret, buf);
1326                 buf = NULL;
1327                 bufsize = bufdata = 0;
1328                 sexpr++;
1329             } else if(*sexpr == L'\"') {
1330                 sexpr++;
1331                 state = 2;
1332             } else if(*sexpr == L'\\') {
1333                 sexpr++;
1334                 if(*sexpr == L'\0')
1335                 {
1336                     addtobuf(buf, *sexpr);
1337                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1338                     addtobuf(buf, *sexpr);
1339                     sexpr++;
1340                 } else {
1341                     addtobuf(buf, L'\\');
1342                     addtobuf(buf, *sexpr);
1343                     sexpr++;
1344                 }
1345             } else {
1346                 addtobuf(buf, *(sexpr++));
1347             }
1348             break;
1349         case 2:
1350             if(*sexpr == L'\\')
1351             {
1352                 sexpr++;
1353                 if(*sexpr == L'\0')
1354                 {
1355                     addtobuf(buf, *sexpr);
1356                 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1357                     addtobuf(buf, *sexpr);
1358                     sexpr++;
1359                 } else {
1360                     addtobuf(buf, L'\\');
1361                     addtobuf(buf, *sexpr);
1362                     sexpr++;
1363                 }
1364             } else if(*sexpr == L'\"') {
1365                 state = 1;
1366                 sexpr++;
1367             } else {
1368                 addtobuf(buf, *(sexpr++));
1369             }
1370             break;
1371         }
1372     }
1373     if(buf != NULL)
1374     {
1375         addtobuf(buf, L'\0');
1376         addtobuf(ret, buf);
1377     }
1378     addtobuf(ret, NULL);
1379     return(ret);
1380 }
1381
1382 void dc_freewcsarr(wchar_t **arr)
1383 {
1384     wchar_t **buf;
1385     
1386     if(arr == NULL)
1387         return;
1388     for(buf = arr; *buf != NULL; buf++)
1389         free(*buf);
1390     free(arr);
1391 }