Removed every instance of malloc.h.
[doldaconnect.git] / lib / uimisc.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
20#include <unistd.h>
ccce0b4b 21#include <stdlib.h>
8be1b1e3 22/* I'm very unsure about this, but for now it defines wcstoll (which
23 * should be defined anyway) and doesn't break anything... let's keep
24 * two eyes wide open, though. */
25#define __USE_ISOC99
d3372da9 26#include <wchar.h>
27#include <wctype.h>
28#include <pwd.h>
29#include <string.h>
d3372da9 30#include <stdio.h>
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
ccce0b4b 992static int sortlist1(const struct dc_respline *l1, const struct dc_respline *l2)
993{
994 return(wcscmp(l1->argv[1], l2->argv[1]));
995}
996
452f980f 997static int sortlist2(const struct dc_fnetpeer **p1, const struct dc_fnetpeer **p2)
ccce0b4b 998{
452f980f 999 return(wcscmp((*p1)->id, (*p2)->id));
ccce0b4b 1000}
1001
1002static void fillpeer(struct dc_fnetpeer *peer, struct dc_respline *r)
1003{
1004 int i;
1005 struct dc_fnetpeerdatum *datum;
1006
1007 for(i = 3; i < r->argc; i += 2)
1008 {
1009 if((datum = finddatum(peer->fn, r->argv[i])) != NULL)
1010 {
1011 switch(datum->dt)
1012 {
1013 case DC_FNPD_INT:
1014 peersetnum(peer, datum->id, wcstol(r->argv[i + 1], NULL, 10));
1015 break;
1016 case DC_FNPD_LL:
1017 peersetlnum(peer, datum->id, wcstoll(r->argv[i + 1], NULL, 10));
1018 break;
1019 case DC_FNPD_STR:
1020 peersetstr(peer, datum->id, r->argv[i + 1]);
1021 break;
1022 }
1023 }
1024 }
1025}
1026
8be1b1e3 1027static int getpeerlistcallback(struct dc_response *resp)
1028{
ccce0b4b 1029 int i, o, c;
8be1b1e3 1030 struct dc_fnetnode *fn;
1031 struct fnetcbdata *data;
ccce0b4b 1032 struct dc_fnetpeer *peer;
1033 struct dc_fnetpeer **plist;
1034 size_t plistsize, plistdata;
8be1b1e3 1035
1036 data = resp->data;
1037 if((fn = dc_findfnetnode(data->fnid)) == NULL)
1038 {
1039 data->callback(NULL, -1, data->data);
1040 free(data);
1041 return(1);
1042 }
1043 if(resp->code == 200)
1044 {
ccce0b4b 1045 qsort(resp->rlines, resp->numlines, sizeof(*resp->rlines), (int (*)(const void *, const void *))sortlist1);
1046 plist = NULL;
1047 plistsize = plistdata = 0;
1048 for(i = 0, peer = fn->peers; peer != NULL; peer = peer->next)
1049 addtobuf(plist, peer);
1050 qsort(plist, plistdata, sizeof(*plist), (int (*)(const void *, const void *))sortlist2);
1051 i = o = 0;
1052 while(1)
8be1b1e3 1053 {
ccce0b4b 1054 if((i < resp->numlines) && (o < plistdata))
8be1b1e3 1055 {
ccce0b4b 1056 c = wcscmp(resp->rlines[i].argv[1], plist[o]->id);
1057 if(c < 0)
8be1b1e3 1058 {
ccce0b4b 1059 peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1060 fillpeer(peer, resp->rlines + i);
1061 i++;
1062 } else if(c > 0) {
1063 delpeer(plist[o]);
1064 o++;
1065 } else {
452f980f 1066 fillpeer(plist[o], resp->rlines + i);
ccce0b4b 1067 i++;
1068 o++;
8be1b1e3 1069 }
ccce0b4b 1070 } else if(i < resp->numlines) {
1071 peer = addpeer(fn, resp->rlines[i].argv[1], resp->rlines[i].argv[2]);
1072 fillpeer(peer, resp->rlines + i);
1073 i++;
1074 } else if(o < plistdata) {
1075 delpeer(plist[o]);
1076 o++;
1077 } else {
1078 break;
8be1b1e3 1079 }
1080 }
ccce0b4b 1081 free(plist);
8be1b1e3 1082 } else if(resp->code == 201) {
1083 while(fn->peers != NULL)
1084 delpeer(fn->peers);
8be1b1e3 1085 }
1086 data->callback(fn, resp->code, data->data);
1087 free(data);
1088 return(1);
1089}
1090
1091static int getpalistcallback(struct dc_response *resp)
1092{
1093 struct dc_fnetnode *fn;
1094 struct dc_intresp *ires;
1095 struct fnetcbdata *data;
1096
1097 data = resp->data;
1098 if((fn = dc_findfnetnode(data->fnid)) == NULL)
1099 {
1100 data->callback(NULL, -1, data->data);
1101 free(data);
1102 return(1);
1103 }
1104 if(resp->code == 200)
1105 {
1106 while((ires = dc_interpret(resp)) != NULL)
1107 {
1108 adddatum(fn, ires->argv[0].val.str, ires->argv[1].val.num);
1109 dc_freeires(ires);
1110 }
1111 dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1112 } else if(resp->code == 201) {
1113 dc_queuecmd(getpeerlistcallback, data, L"lspeers", L"%%i", fn->id, NULL);
1114 } else {
1115 data->callback(fn, resp->code, data->data);
1116 free(data);
1117 }
1118 return(1);
1119}
1120
d3372da9 1121void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
1122{
1123 struct gencbdata *data;
1124
1125 data = smalloc(sizeof(*data));
1126 data->callback = callback;
1127 data->data = udata;
1128 dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
1129}
1130
1131void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
1132{
1133 struct gencbdata *data;
1134
1135 data = smalloc(sizeof(*data));
1136 data->callback = callback;
1137 data->data = udata;
1138 dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
1139}
1140
8be1b1e3 1141void dc_getpeerlistasync(struct dc_fnetnode *fn, void (*callback)(struct dc_fnetnode *, int, void *), void *udata)
1142{
1143 struct fnetcbdata *data;
1144
1145 data = smalloc(sizeof(*data));
1146 data->callback = callback;
1147 data->fnid = fn->id;
1148 data->data = udata;
1149 dc_queuecmd(getpalistcallback, data, L"lspa", L"%%i", fn->id, NULL);
1150}
1151
d3372da9 1152void dc_uimisc_disconnected(void)
1153{
1154 while(dc_fnetnodes != NULL)
1155 freefn(dc_fnetnodes);
1156 while(dc_transfers != NULL)
1157 freetransfer(dc_transfers);
1158}
1159
1160void dc_uimisc_handlenotify(struct dc_response *resp)
1161{
8be1b1e3 1162 int i;
d3372da9 1163 struct dc_fnetnode *fn;
1164 struct dc_transfer *transfer;
8be1b1e3 1165 struct dc_fnetpeer *peer;
d3372da9 1166 struct dc_intresp *ires;
1167
1168 if((ires = dc_interpret(resp)) == NULL)
1169 return;
1170 switch(resp->code)
1171 {
1172 case 601:
1173 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1174 fn->state = ires->argv[1].val.num;
1175 break;
1176 case 602:
1177 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1178 {
1179 if(fn->name != NULL)
1180 free(fn->name);
1181 fn->name = swcsdup(ires->argv[1].val.str);
1182 }
1183 break;
1184 case 603:
1185 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1186 freefn(fn);
1187 break;
1188 case 604:
1189 fn = newfn();
1190 fn->id = ires->argv[0].val.num;
1191 if(fn->fnet != NULL)
1192 free(fn->fnet);
1193 fn->fnet = swcsdup(ires->argv[1].val.str);
1194 fn->state = DC_FNN_STATE_SYN;
1195 fn->numusers = 0;
1196 break;
1197 case 605:
1198 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1199 fn->numusers = ires->argv[1].val.num;
1200 break;
1201 case 610:
1202 transfer = newtransfer();
1203 transfer->id = ires->argv[0].val.num;
1204 transfer->dir = ires->argv[1].val.num;
1205 if(transfer->dir == DC_TRNSD_UP)
1206 transfer->state = DC_TRNS_HS;
1207 transfer->peerid = swcsdup(ires->argv[2].val.str);
1208 if(ires->argv[3].val.str[0])
1209 transfer->path = swcsdup(ires->argv[3].val.str);
1210 break;
1211 case 611:
1212 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1213 transfer->state = ires->argv[1].val.num;
1214 break;
1215 case 612:
1216 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1217 {
1218 if(transfer->peernick != NULL)
1219 free(transfer->peernick);
1220 transfer->peernick = swcsdup(ires->argv[1].val.str);
1221 }
1222 break;
1223 case 613:
1224 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1225 transfer->size = ires->argv[1].val.num;
1226 break;
1227 case 614:
1228 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1229 {
1230 transfer->error = ires->argv[1].val.num;
1231 time(&transfer->errortime);
1232 }
1233 break;
1234 case 615:
1235 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1236 transfer->curpos = ires->argv[1].val.num;
1237 break;
1238 case 616:
1239 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1240 {
1241 if(transfer->path != NULL)
1242 free(transfer->path);
1243 transfer->path = swcsdup(ires->argv[1].val.str);
1244 }
1245 break;
1246 case 617:
1247 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1248 freetransfer(transfer);
1249 break;
794c3e02 1250 case 618:
1251 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
1252 {
1253 if(transfer->hash != NULL)
1254 {
1255 free(transfer->hash);
1256 transfer->hash = NULL;
1257 }
1258 if(wcslen(ires->argv[1].val.str) > 0)
1259 transfer->hash = swcsdup(ires->argv[1].val.str);
1260 }
1261 break;
8be1b1e3 1262 case 630:
cece2a51 1263 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
8be1b1e3 1264 {
1265 if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) == NULL)
d4c5deab 1266 {
1267 peer = addpeer(fn, ires->argv[1].val.str, ires->argv[2].val.str);
1268 if(fn->newpeercb != NULL)
1269 fn->newpeercb(peer);
1270 }
8be1b1e3 1271 }
1272 break;
1273 case 631:
cece2a51 1274 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
8be1b1e3 1275 {
1276 if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
d4c5deab 1277 {
1278 if(fn->delpeercb != NULL)
1279 fn->delpeercb(peer);
8be1b1e3 1280 delpeer(peer);
d4c5deab 1281 }
8be1b1e3 1282 }
1283 break;
1284 case 632:
cece2a51 1285 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
8be1b1e3 1286 {
1287 if((peer = dc_fnetfindpeer(fn, ires->argv[1].val.str)) != NULL)
1288 {
1289 if(wcscmp(ires->argv[2].val.str, peer->nick))
1290 {
1291 free(peer->nick);
1292 peer->nick = swcsdup(ires->argv[2].val.str);
1293 }
aedeb734 1294 for(i = 4; i < resp->rlines[0].argc; i += 3)
8be1b1e3 1295 {
1296 switch(wcstol(resp->rlines[0].argv[i + 1], NULL, 10))
1297 {
1298 case DC_FNPD_INT:
1299 peersetnum(peer, resp->rlines[0].argv[i], wcstol(resp->rlines[0].argv[i + 2], NULL, 10));
1300 break;
1301 case DC_FNPD_LL:
1302 peersetlnum(peer, resp->rlines[0].argv[i], wcstoll(resp->rlines[0].argv[i + 2], NULL, 10));
1303 break;
1304 case DC_FNPD_STR:
1305 peersetstr(peer, resp->rlines[0].argv[i], resp->rlines[0].argv[i + 2]);
1306 break;
1307 }
1308 }
d4c5deab 1309 if(fn->chpeercb != NULL)
1310 fn->chpeercb(peer);
8be1b1e3 1311 }
1312 }
1313 break;
d3372da9 1314 default:
1315 break;
1316 }
1317 dc_freeires(ires);
1318 resp->curline = 0;
1319}
1320
1321/* Note the backspace handling - it's not as elegant as possible, but
1322 * it helps avoid the "box-of-toothpicks" syndrome when writing search
1323 * expressions manually. */
1324wchar_t **dc_lexsexpr(wchar_t *sexpr)
1325{
1326 wchar_t **ret;
1327 wchar_t *buf;
1328 size_t retsize, retdata, bufsize, bufdata;
1329 int state;
1330
1331 ret = NULL;
1332 buf = NULL;
1333 retsize = retdata = bufsize = bufdata = 0;
1334 state = 0;
1335 while(*sexpr != L'\0')
1336 {
1337 switch(state)
1338 {
1339 case 0:
1340 if(!iswspace(*sexpr))
1341 state = 1;
1342 else
1343 sexpr++;
1344 break;
1345 case 1:
1346 if(iswspace(*sexpr))
1347 {
1348 if(buf != NULL)
1349 {
1350 addtobuf(buf, L'\0');
1351 addtobuf(ret, buf);
1352 buf = NULL;
1353 bufsize = bufdata = 0;
1354 }
1355 state = 0;
1356 } else if((*sexpr == L'(') ||
1357 (*sexpr == L')') ||
1358 (*sexpr == L'&') ||
1359 (*sexpr == L'|') ||
1360 (*sexpr == L'!')) {
1361 if(buf != NULL)
1362 {
1363 addtobuf(buf, L'\0');
1364 addtobuf(ret, buf);
1365 buf = NULL;
1366 bufsize = bufdata = 0;
1367 }
1368 addtobuf(buf, *sexpr);
1369 addtobuf(buf, L'\0');
1370 addtobuf(ret, buf);
1371 buf = NULL;
1372 bufsize = bufdata = 0;
1373 sexpr++;
1374 } else if(*sexpr == L'\"') {
1375 sexpr++;
1376 state = 2;
1377 } else if(*sexpr == L'\\') {
1378 sexpr++;
1379 if(*sexpr == L'\0')
1380 {
1381 addtobuf(buf, *sexpr);
1382 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1383 addtobuf(buf, *sexpr);
1384 sexpr++;
1385 } else {
1386 addtobuf(buf, L'\\');
1387 addtobuf(buf, *sexpr);
1388 sexpr++;
1389 }
1390 } else {
1391 addtobuf(buf, *(sexpr++));
1392 }
1393 break;
1394 case 2:
1395 if(*sexpr == L'\\')
1396 {
1397 sexpr++;
1398 if(*sexpr == L'\0')
1399 {
1400 addtobuf(buf, *sexpr);
1401 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1402 addtobuf(buf, *sexpr);
1403 sexpr++;
1404 } else {
1405 addtobuf(buf, L'\\');
1406 addtobuf(buf, *sexpr);
1407 sexpr++;
1408 }
1409 } else if(*sexpr == L'\"') {
1410 state = 1;
1411 sexpr++;
1412 } else {
1413 addtobuf(buf, *(sexpr++));
1414 }
1415 break;
1416 }
1417 }
1418 if(buf != NULL)
1419 {
1420 addtobuf(buf, L'\0');
1421 addtobuf(ret, buf);
1422 }
1423 addtobuf(ret, NULL);
1424 return(ret);
1425}
1426
1427void dc_freewcsarr(wchar_t **arr)
1428{
1429 wchar_t **buf;
1430
1431 if(arr == NULL)
1432 return;
1433 for(buf = arr; *buf != NULL; buf++)
1434 free(*buf);
1435 free(arr);
1436}