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