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