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