2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
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.
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.
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
31 #include <doldaconnect/uilib.h>
32 #include <doldaconnect/uimisc.h>
33 #include <doldaconnect/utils.h>
45 void (*process)(struct dc_response *resp, struct logindata *data);
46 int (*init)(struct logindata *data);
47 void (*release)(struct logindata *data);
52 int (*conv)(int type, wchar_t *text, char **resp, void *data);
53 void (*callback)(int err, wchar_t *reason, void *data);
59 struct authmech *mech;
64 void (*callback)(int resp, void *data);
68 struct dc_fnetnode *dc_fnetnodes = NULL;
69 struct dc_transfer *dc_transfers = NULL;
71 static void freelogindata(struct logindata *data)
73 if((data->mech != NULL) && (data->mech->release != NULL))
74 data->mech->release(data);
75 if(data->freeusername)
80 static int logincallback(struct dc_response *resp);
82 static void process_authless(struct dc_response *resp, struct logindata *data)
87 data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
93 if((ires = dc_interpret(resp)) != NULL)
96 if(data->conv((resp->code == 303)?DC_LOGIN_CONV_INFO:DC_LOGIN_CONV_ERROR, ires->argv[0].val.str, &buf, data->data))
98 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
101 dc_queuecmd(logincallback, data, L"pass", L"", NULL);
105 memset(buf, 0, strlen(buf));
113 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
117 data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
121 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
127 static void process_pam(struct dc_response *resp, struct logindata *data)
129 struct dc_intresp *ires;
136 data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
143 if(resp->code == 301)
144 convtype = DC_LOGIN_CONV_NOECHO;
145 else if(resp->code == 302)
146 convtype = DC_LOGIN_CONV_ECHO;
147 else if(resp->code == 303)
148 convtype = DC_LOGIN_CONV_INFO;
149 else if(resp->code == 304)
150 convtype = DC_LOGIN_CONV_ERROR;
151 if((ires = dc_interpret(resp)) != NULL)
154 if(data->conv(convtype, ires->argv[0].val.str, &buf, data->data))
156 data->callback(DC_LOGIN_ERR_CONV, NULL, data->data);
159 dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
163 memset(buf, 0, strlen(buf));
170 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
174 data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
178 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
188 krb5_context context;
189 krb5_principal sprinc, myprinc;
191 krb5_auth_context authcon;
193 krb5_creds *servcreds;
194 int valid, fwd, fwded;
197 static char *hexencode(char *data, size_t datalen)
200 size_t bufsize, bufdata;
204 bufsize = bufdata = 0;
205 for(; datalen > 0; datalen--, data++)
207 dig = (*data & 0xF0) >> 4;
209 this = 'A' + dig - 10;
215 this = 'A' + dig - 10;
224 static char *hexdecode(char *data, size_t *len)
227 size_t bufsize, bufdata;
230 bufsize = bufdata = 0;
233 if((*data >= 'A') && (*data <= 'F'))
235 this = (this & 0x0F) | ((*data - 'A' + 10) << 4);
236 } else if((*data >= '0') && (*data <= '9')) {
237 this = (this & 0x0F) | ((*data - '0') << 4);
250 if((*data >= 'A') && (*data <= 'F'))
252 this = (this & 0xF0) | (*data - 'A' + 10);
253 } else if((*data >= '0') && (*data <= '9')) {
254 this = (this & 0xF0) | (*data - '0');
268 static void process_krb5(struct dc_response *resp, struct logindata *data)
271 struct dc_intresp *ires;
272 struct krb5data *krb;
274 krb5_ap_rep_enc_part *repl;
277 krb = data->mechdata;
281 data->callback(DC_LOGIN_ERR_SUCCESS, NULL, data->data);
288 buf = hexencode(krb->reqbuf.data, krb->reqbuf.length);
289 dc_queuecmd(logincallback, data, L"pass", L"%%s", buf, NULL);
294 if((ires = dc_interpret(resp)) != NULL)
296 k5d.data = hexdecode(icswcstombs(ires->argv[0].val.str, NULL, NULL), &k5d.length);
299 if((ret = krb5_rd_rep(krb->context, krb->authcon, &k5d, &repl)) != 0)
301 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
305 /* XXX: Do I need to do something with this? */
307 krb5_free_ap_rep_enc_part(krb->context, repl);
309 if(krb->fwd && !krb->fwded)
311 if(krb->reqbuf.data != NULL)
312 free(krb->reqbuf.data);
313 krb->reqbuf.data = NULL;
314 if((ret = krb5_fwd_tgt_creds(krb->context, krb->authcon, NULL, krb->servcreds->client, krb->servcreds->server, 0, 1, &krb->reqbuf)) != 0)
316 fprintf(stderr, "krb5_fwd_tgt_creds reported an error: %s\n", error_message(ret));
317 dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
321 dc_queuecmd(logincallback, data, L"pass", L"32", NULL);
326 dc_queuecmd(logincallback, data, L"pass", L"31", NULL);
334 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
340 data->callback(DC_LOGIN_ERR_SERVER, NULL, data->data);
344 data->callback(DC_LOGIN_ERR_AUTHFAIL, NULL, data->data);
348 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
354 static int init_krb5(struct logindata *data)
357 struct krb5data *krb;
361 krb = smalloc(sizeof(*krb));
362 memset(krb, 0, sizeof(*krb));
365 data->mechdata = krb;
366 if((ret = krb5_init_context(&krb->context)) != 0)
368 fprintf(stderr, "krb5_init_context reported an error: %s\n", error_message(ret));
371 if((ret = krb5_auth_con_init(krb->context, &krb->authcon)) != 0)
373 fprintf(stderr, "krb5_auth_con_init reported an error: %s\n", error_message(ret));
376 krb5_auth_con_setflags(krb->context, krb->authcon, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
377 if((ret = krb5_sname_to_principal(krb->context, dc_gethostname(), "doldacond", KRB5_NT_SRV_HST, &krb->sprinc)) != 0)
379 fprintf(stderr, "krb5_sname_to_principal reported an error: %s\n", error_message(ret));
382 if((ret = krb5_cc_default(krb->context, &krb->ccache)) != 0)
384 fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
387 if((ret = krb5_cc_get_principal(krb->context, krb->ccache, &krb->myprinc)) != 0)
389 fprintf(stderr, "krb5_cc_default reported an error: %s\n", error_message(ret));
392 memset(&creds, 0, sizeof(creds));
393 creds.client = krb->myprinc;
394 creds.server = krb->sprinc;
395 if((ret = krb5_get_credentials(krb->context, 0, krb->ccache, &creds, &krb->servcreds)) != 0)
397 fprintf(stderr, "krb5_get_credentials reported an error: %s\n", error_message(ret));
400 /* WTF is this checksum stuff?! The Krb docs don't say a word about it! */
401 cksum.data = sstrdup(dc_gethostname());
402 cksum.length = strlen(cksum.data);
403 if((ret = krb5_mk_req_extended(krb->context, &krb->authcon, AP_OPTS_MUTUAL_REQUIRED, &cksum, krb->servcreds, &krb->reqbuf)) != 0)
405 fprintf(stderr, "krb5_mk_req_extended reported an error: %s\n", error_message(ret));
413 static void release_krb5(struct logindata *data)
415 struct krb5data *krb;
417 if((krb = data->mechdata) == NULL)
419 if(krb->servcreds != NULL)
420 krb5_free_creds(krb->context, krb->servcreds);
421 if(krb->reqbuf.data != NULL)
422 free(krb->reqbuf.data);
423 if(krb->sprinc != NULL)
424 krb5_free_principal(krb->context, krb->sprinc);
425 if(krb->myprinc != NULL)
426 krb5_free_principal(krb->context, krb->myprinc);
427 if(krb->ccache != NULL)
428 krb5_cc_close(krb->context, krb->ccache);
429 if(krb->authcon != NULL)
430 krb5_auth_con_free(krb->context, krb->authcon);
431 if(krb->context != NULL)
432 krb5_free_context(krb->context);
437 /* Arranged in order of priority */
438 static struct authmech authmechs[] =
443 .process = process_krb5,
445 .release = release_krb5
450 .process = process_authless,
456 .process = process_pam,
465 static int builtinconv(int type, wchar_t *text, char **resp, void *data)
471 if((buf = icwcstombs(text, NULL)) == NULL)
475 *resp = sstrdup(pass);
476 memset(pass, 0, strlen(pass));
482 static int logincallback(struct dc_response *resp)
485 struct dc_intresp *ires;
486 struct logindata *data;
489 struct passwd *pwent;
493 if(!wcscmp(resp->cmdname, L"lsauth"))
495 if(resp->code == 201)
497 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
501 while((ires = dc_interpret(resp)) != NULL)
503 if(!data->useauthless && !wcscmp(ires->argv[0].val.str, L"authless"))
508 for(i = 0; authmechs[i].name != NULL; i++)
510 if(!wcscmp(authmechs[i].name, ires->argv[0].val.str) && ((i < mech) || (mech == -1)))
512 odata = data->mechdata;
513 data->mechdata = NULL;
514 if((authmechs[i].init != NULL) && authmechs[i].init(data))
516 if(authmechs[i].release != NULL)
517 authmechs[i].release(data);
518 data->mechdata = odata;
519 fprintf(stderr, "authentication mechanism %ls failed, trying further...\n", authmechs[i].name);
521 if((data->mech != NULL) && data->mech->release != NULL)
523 ndata = data->mechdata;
524 data->mechdata = odata;
525 data->mech->release(data);
526 data->mechdata = ndata;
529 data->mech = authmechs + i;
538 data->callback(DC_LOGIN_ERR_NOLOGIN, NULL, data->data);
541 if((username = data->username) == NULL)
543 if((pwent = getpwuid(getuid())) == NULL)
545 data->callback(DC_LOGIN_ERR_USER, NULL, data->data);
549 username = pwent->pw_name;
551 dc_queuecmd(logincallback, data, L"login", data->mech->name, L"%%s", username, NULL);
554 } else if(!wcscmp(resp->cmdname, L"login") || !wcscmp(resp->cmdname, L"pass")) {
555 data->mech->process(resp, data);
560 void dc_loginasync(char *username, int useauthless, int (*conv)(int, wchar_t *, char **, void *), void (*callback)(int, wchar_t *, void *), void *udata)
562 struct logindata *data;
564 data = smalloc(sizeof(*data));
570 data->mechdata = NULL;
571 data->callback = callback;
572 data->useauthless = useauthless;
573 data->freeusername = 0;
576 data->username = NULL;
578 data->username = sstrdup(username);
579 data->freeusername = 1;
581 dc_queuecmd(logincallback, data, L"lsauth", NULL);
584 static struct dc_fnetnode *newfn(void)
586 struct dc_fnetnode *fn;
588 fn = smalloc(sizeof(*fn));
589 memset(fn, 0, sizeof(*fn));
593 fn->state = fn->numusers = fn->found = 0;
594 fn->destroycb = NULL;
596 fn->next = dc_fnetnodes;
598 if(dc_fnetnodes != NULL)
599 dc_fnetnodes->prev = fn;
604 static void freefn(struct dc_fnetnode *fn)
607 fn->next->prev = fn->prev;
609 fn->prev->next = fn->next;
610 if(fn == dc_fnetnodes)
611 dc_fnetnodes = fn->next;
612 if(fn->destroycb != NULL)
621 struct dc_fnetnode *dc_findfnetnode(int id)
623 struct dc_fnetnode *fn;
625 for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
633 static struct dc_transfer *newtransfer(void)
635 struct dc_transfer *transfer;
637 transfer = smalloc(sizeof(*transfer));
638 memset(transfer, 0, sizeof(*transfer));
640 transfer->peerid = transfer->peernick = transfer->path = NULL;
641 transfer->state = DC_TRNS_WAITING;
642 transfer->dir = DC_TRNSD_UNKNOWN;
644 transfer->curpos = -1;
645 transfer->destroycb = NULL;
646 transfer->udata = NULL;
647 transfer->next = dc_transfers;
648 transfer->prev = NULL;
649 if(dc_transfers != NULL)
650 dc_transfers->prev = transfer;
651 dc_transfers = transfer;
655 static void freetransfer(struct dc_transfer *transfer)
657 if(transfer->next != NULL)
658 transfer->next->prev = transfer->prev;
659 if(transfer->prev != NULL)
660 transfer->prev->next = transfer->next;
661 if(transfer == dc_transfers)
662 dc_transfers = transfer->next;
663 if(transfer->destroycb != NULL)
664 transfer->destroycb(transfer);
665 if(transfer->peerid != NULL)
666 free(transfer->peerid);
667 if(transfer->peernick != NULL)
668 free(transfer->peernick);
669 if(transfer->path != NULL)
670 free(transfer->path);
674 struct dc_transfer *dc_findtransfer(int id)
676 struct dc_transfer *transfer;
678 for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
680 if(transfer->id == id)
686 static int getfnlistcallback(struct dc_response *resp)
688 struct dc_intresp *ires;
689 struct gencbdata *data;
690 struct dc_fnetnode *fn, *next;
693 if(resp->code == 200)
695 for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
697 while((ires = dc_interpret(resp)) != NULL)
699 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
704 fn->fnet = swcsdup(ires->argv[1].val.str);
707 fn->name = swcsdup(ires->argv[2].val.str);
708 fn->numusers = ires->argv[3].val.num;
709 fn->state = ires->argv[4].val.num;
712 fn->id = ires->argv[0].val.num;
713 fn->fnet = swcsdup(ires->argv[1].val.str);
714 fn->name = swcsdup(ires->argv[2].val.str);
715 fn->numusers = ires->argv[3].val.num;
716 fn->state = ires->argv[4].val.num;
721 for(fn = dc_fnetnodes; fn != NULL; fn = next)
727 data->callback(200, data->data);
729 } else if(resp->code == 201) {
730 while(dc_fnetnodes != NULL)
731 freefn(dc_fnetnodes);
732 data->callback(201, data->data);
734 } else if(resp->code == 502) {
735 while(dc_fnetnodes != NULL)
736 freefn(dc_fnetnodes);
737 data->callback(502, data->data);
743 static int gettrlistcallback(struct dc_response *resp)
745 struct dc_intresp *ires;
746 struct gencbdata *data;
747 struct dc_transfer *transfer, *next;
750 if(resp->code == 200)
752 for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
754 while((ires = dc_interpret(resp)) != NULL)
756 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
759 if((transfer->path == NULL) || wcscmp(transfer->path, ires->argv[5].val.str))
761 if(transfer->path != NULL)
762 free(transfer->path);
763 transfer->path = swcsdup(ires->argv[5].val.str);
765 if((transfer->peerid == NULL) || wcscmp(transfer->peerid, ires->argv[3].val.str))
767 if(transfer->peerid != NULL)
768 free(transfer->peerid);
769 transfer->peerid = swcsdup(ires->argv[3].val.str);
771 if((transfer->peernick == NULL) || wcscmp(transfer->peernick, ires->argv[4].val.str))
773 if(transfer->peernick != NULL)
774 free(transfer->peernick);
775 transfer->peernick = swcsdup(ires->argv[4].val.str);
777 transfer->dir = ires->argv[1].val.num;
778 transfer->state = ires->argv[2].val.num;
779 transfer->size = ires->argv[6].val.num;
780 transfer->curpos = ires->argv[7].val.num;
782 transfer = newtransfer();
783 transfer->id = ires->argv[0].val.num;
784 transfer->dir = ires->argv[1].val.num;
785 transfer->state = ires->argv[2].val.num;
786 transfer->peerid = swcsdup(ires->argv[3].val.str);
787 transfer->peernick = swcsdup(ires->argv[4].val.str);
788 transfer->path = swcsdup(ires->argv[5].val.str);
789 transfer->size = ires->argv[6].val.num;
790 transfer->curpos = ires->argv[7].val.num;
795 for(transfer = dc_transfers; transfer != NULL; transfer = next)
797 next = transfer->next;
799 freetransfer(transfer);
801 data->callback(200, data->data);
803 } else if(resp->code == 201) {
804 while(dc_transfers != NULL)
805 freetransfer(dc_transfers);
806 data->callback(201, data->data);
808 } else if(resp->code == 502) {
809 while(dc_transfers != NULL)
810 freetransfer(dc_transfers);
811 data->callback(502, data->data);
817 void dc_getfnlistasync(void (*callback)(int, void *), void *udata)
819 struct gencbdata *data;
821 data = smalloc(sizeof(*data));
822 data->callback = callback;
824 dc_queuecmd(getfnlistcallback, data, L"lsnodes", NULL);
827 void dc_gettrlistasync(void (*callback)(int, void *), void *udata)
829 struct gencbdata *data;
831 data = smalloc(sizeof(*data));
832 data->callback = callback;
834 dc_queuecmd(gettrlistcallback, data, L"lstrans", NULL);
837 void dc_uimisc_disconnected(void)
839 while(dc_fnetnodes != NULL)
840 freefn(dc_fnetnodes);
841 while(dc_transfers != NULL)
842 freetransfer(dc_transfers);
845 void dc_uimisc_handlenotify(struct dc_response *resp)
847 struct dc_fnetnode *fn;
848 struct dc_transfer *transfer;
849 struct dc_intresp *ires;
851 if((ires = dc_interpret(resp)) == NULL)
856 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
857 fn->state = ires->argv[1].val.num;
860 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
864 fn->name = swcsdup(ires->argv[1].val.str);
868 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
873 fn->id = ires->argv[0].val.num;
876 fn->fnet = swcsdup(ires->argv[1].val.str);
877 fn->state = DC_FNN_STATE_SYN;
881 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
882 fn->numusers = ires->argv[1].val.num;
885 transfer = newtransfer();
886 transfer->id = ires->argv[0].val.num;
887 transfer->dir = ires->argv[1].val.num;
888 if(transfer->dir == DC_TRNSD_UP)
889 transfer->state = DC_TRNS_HS;
890 transfer->peerid = swcsdup(ires->argv[2].val.str);
891 if(ires->argv[3].val.str[0])
892 transfer->path = swcsdup(ires->argv[3].val.str);
895 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
896 transfer->state = ires->argv[1].val.num;
899 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
901 if(transfer->peernick != NULL)
902 free(transfer->peernick);
903 transfer->peernick = swcsdup(ires->argv[1].val.str);
907 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
908 transfer->size = ires->argv[1].val.num;
911 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
913 transfer->error = ires->argv[1].val.num;
914 time(&transfer->errortime);
918 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
919 transfer->curpos = ires->argv[1].val.num;
922 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
924 if(transfer->path != NULL)
925 free(transfer->path);
926 transfer->path = swcsdup(ires->argv[1].val.str);
930 if((transfer = dc_findtransfer(ires->argv[0].val.num)) != NULL)
931 freetransfer(transfer);
940 /* Note the backspace handling - it's not as elegant as possible, but
941 * it helps avoid the "box-of-toothpicks" syndrome when writing search
942 * expressions manually. */
943 wchar_t **dc_lexsexpr(wchar_t *sexpr)
947 size_t retsize, retdata, bufsize, bufdata;
952 retsize = retdata = bufsize = bufdata = 0;
954 while(*sexpr != L'\0')
959 if(!iswspace(*sexpr))
969 addtobuf(buf, L'\0');
972 bufsize = bufdata = 0;
975 } else if((*sexpr == L'(') ||
982 addtobuf(buf, L'\0');
985 bufsize = bufdata = 0;
987 addtobuf(buf, *sexpr);
988 addtobuf(buf, L'\0');
991 bufsize = bufdata = 0;
993 } else if(*sexpr == L'\"') {
996 } else if(*sexpr == L'\\') {
1000 addtobuf(buf, *sexpr);
1001 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1002 addtobuf(buf, *sexpr);
1005 addtobuf(buf, L'\\');
1006 addtobuf(buf, *sexpr);
1010 addtobuf(buf, *(sexpr++));
1019 addtobuf(buf, *sexpr);
1020 } else if((*sexpr == L'\\') || (*sexpr == L'\"')) {
1021 addtobuf(buf, *sexpr);
1024 addtobuf(buf, L'\\');
1025 addtobuf(buf, *sexpr);
1028 } else if(*sexpr == L'\"') {
1032 addtobuf(buf, *(sexpr++));
1039 addtobuf(buf, L'\0');
1042 addtobuf(ret, NULL);
1046 void dc_freewcsarr(wchar_t **arr)
1052 for(buf = arr; *buf != NULL; buf++)