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