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