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