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