X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=daemon%2Fui.c;h=0f6e701bbe45d5d9d3aab1e46c912d1362754443;hb=b984902c37729827db65bc4271a6cd07ff7c0546;hp=87818bba7ab3b1155d4e108f61dbe9fe2df0d30f;hpb=796283c6df4713edcac9c199bf4fd26dff42c2fd;p=doldaconnect.git diff --git a/daemon/ui.c b/daemon/ui.c index 87818bb..0f6e701 100644 --- a/daemon/ui.c +++ b/daemon/ui.c @@ -1,6 +1,6 @@ /* * Dolda Connect - Modular multiuser Direct Connect-style client - * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com) + * Copyright (C) 2004 Fredrik Tolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,10 +19,11 @@ #define _GNU_SOURCE #include #include -#include #include #include #include +#include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include @@ -61,6 +63,7 @@ #define NOTIF_STR 2 #define NOTIF_FLOAT 3 #define NOTIF_ID 4 +#define NOTIF_OFF 5 #define NOTIF_PEND 0 #define NOTIF_WAIT 1 @@ -104,6 +107,7 @@ struct notif union { int n; + off_t o; wchar_t *s; double d; } d; @@ -115,6 +119,7 @@ struct uidata struct uidata *next, *prev; struct socket *sk; struct qcommand *queue, *queuelast; + size_t queuesize; struct authhandle *auth; int close; union @@ -127,11 +132,14 @@ struct uidata int tract:1; int trprog:1; int srch:1; + int msg:1; } b; int w; } notify; wchar_t *username; struct uiuser *userinfo; + int id; + wchar_t *regname; uid_t uid; struct notif *fnotif, *lnotif; char *fcmdbuf; @@ -143,7 +151,7 @@ struct uidata size_t inbufsize, indata; /* Wordset storage */ wchar_t **argv; - int argc, args; + size_t argc, args; /* WCS conversation stuff */ wchar_t *cb; /* Conversation buffer */ size_t cbsize, cbdata; @@ -158,10 +166,14 @@ struct uidata static int srcheta(struct search *srch, void *uudata); static int srchcommit(struct search *srch, void *uudata); static int srchres(struct search *srch, struct srchres *sr, void *uudata); +static struct notif *newnotif(struct uidata *data, int code, ...); +static void notifappend(struct notif *notif, ...); struct uiuser *users = NULL; struct uidata *actives = NULL; -struct socket *uisocket = NULL; +struct lport *tcpsocket = NULL; +struct lport *unixsocket = NULL; +static time_t starttime; static wchar_t *quoteword(wchar_t *word) { @@ -205,7 +217,7 @@ static void sq(struct socket *sk, int cont, ...) { int num, freepart; va_list al; - char *final; + char *final, *sarg; wchar_t *buf; wchar_t *part, *tpart; size_t bufsize, bufdata; @@ -216,32 +228,44 @@ static void sq(struct socket *sk, int cont, ...) va_start(al, cont); while((part = va_arg(al, wchar_t *)) != NULL) { + freepart = 0; if(*part == L'%') { - /* - * This kludge demands that all arguments that you call it - * with are the size of an int. That happens to be the - * case for most datatypes on most platforms and - * compilers, but I don't know exactly which ones, and - * also a long long is a notable candidate of an arg that - * is not the size of an int on 32-bit archs. If it breaks - * some existing call on your architecture, please tell - * me. - */ - part = vswprintf2(tpart = (part + 1), al); - for(; *tpart != L'\0'; tpart++) + tpart = part + 1; + if(!wcscmp(tpart, L"i")) { - if(*tpart == L'%') + freepart = 1; + part = swprintf2(L"%i", va_arg(al, int)); + } else if(!wcscmp(tpart, L"zi")) { + freepart = 1; + part = swprintf2(L"%zi", va_arg(al, size_t)); + } else if(!wcscmp(tpart, L"oi")) { + freepart = 1; + part = swprintf2(L"%ji", (intmax_t)va_arg(al, off_t)); + } else if(!wcscmp(tpart, L"s")) { + freepart = 1; + part = icmbstowcs(sarg = va_arg(al, char *), NULL); + if(part == NULL) { - if(tpart[1] == L'%') - tpart++; - else - va_arg(al, int); + freepart = 0; + part = L"ERROR"; + flog(LOG_ERR, "could not convert local string to wcs: %s", sarg); } + } else if(!wcscmp(tpart, L"ls")) { + part = va_arg(al, wchar_t *); + } else if(!wcscmp(tpart, L"ll")) { + freepart = 1; + part = swprintf2(L"%lli", va_arg(al, long long)); + } else if(!wcscmp(tpart, L"f")) { + freepart = 1; + part = swprintf2(L"%f", va_arg(al, double)); + } else if(!wcscmp(tpart, L"x")) { + freepart = 1; + part = swprintf2(L"%x", va_arg(al, int)); + } else { + flog(LOG_CRIT, "BUG: unknown type code in sq: %ls", tpart); + abort(); } - freepart = 1; - } else { - freepart = 0; } if((tpart = quoteword(part)) != NULL) { @@ -310,41 +334,52 @@ static int haspriv(struct uidata *data, int perm) /* Useful macros for the command functions: */ #define haveargs(n) do { if(argc < n) { sq(sk, 0, L"501", L"Wrong number of arguments", NULL); return; } } while(0) -#define havepriv(p) do { if((data->userinfo == NULL) || ((data->userinfo->perms & (p)) != (p))) { sq(sk, 0, L"502", L"%Unauthorized request - %x needed", (p), NULL); return; } } while(0) +#define havepriv(p) do { if((data->userinfo == NULL) || ((data->userinfo->perms & (p)) != (p))) { sq(sk, 0, L"502", L"Unauthorized request", L"needed", L"%x", (p), NULL); return; } } while(0) static void cmd_connect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) { int valid; + struct in6_addr mv4lo; + struct sockaddr *remote; if(confgetint("ui", "onlylocal")) { - switch(sk->remote->sa_family) - { - case AF_INET: - valid = ((struct sockaddr_in *)sk->remote)->sin_addr.s_addr == INADDR_LOOPBACK; - break; - case AF_INET6: - valid = !memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)); - break; - default: - valid = 0; - break; + valid = 0; + if(!sockpeeraddr(sk, &remote, NULL)) { + switch(remote->sa_family) + { + case AF_INET: + valid = ((struct sockaddr_in *)remote)->sin_addr.s_addr == INADDR_LOOPBACK; + break; + case AF_INET6: + inet_pton(AF_INET6, "::ffff:127.0.0.1", &mv4lo); + valid = 0; + if(!memcmp(&((struct sockaddr_in6 *)remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback))) + valid = 1; + if(!memcmp(&((struct sockaddr_in6 *)remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback))) + valid = 1; + break; + case AF_UNIX: + valid = 1; + break; + } + free(remote); } if(!valid) { sq(sk, 0, L"502", L"Only localhost connections allowed to this host", NULL); - sk->close = 1; + closesock(sk); data->close = 1; return; } } - sq(sk, 0, L"200", L"%Dolda Connect daemon v%s", VERSION, NULL); + sq(sk, 0, L"201", L"1", L"3", L"Dolda Connect daemon v" VERSION, NULL); } static void cmd_notfound(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) { if((argv != NULL) && (argv[0] != NULL)) - sq(sk, 0, L"500", L"%Command not found: %ls", argv[0], NULL); + sq(sk, 0, L"500", L"Command not found", NULL); else sq(sk, 0, L"500", L"No command", NULL); } @@ -372,7 +407,7 @@ static void cmd_lsauth(struct socket *sk, struct uidata *data, int argc, wchar_t prev = NULL; for(mech = mechs; mech != NULL; mech = mech->next) { - if(mech->enabled) + if(mech->enabled && authavailable(mech, sk)) { if(prev != NULL) sq(sk, 1, L"200", prev->name, NULL); @@ -415,13 +450,13 @@ static void cmd_login(struct socket *sk, struct uidata *data, int argc, wchar_t if(errno == ENOENT) sq(sk, 0, L"508", L"No such authentication mechanism", NULL); else - sq(sk, 0, L"505", L"Could not initialize authentication system", L"%%s", strerror(errno), NULL); + sq(sk, 0, L"505", L"Could not initialize authentication system", L"%s", strerror(errno), NULL); free(buf); logout(data); return; } free(buf); - switch(authenticate(data->auth, NULL)) + switch(authenticate(data->auth, sk, NULL)) { case AUTH_SUCCESS: data->userinfo = finduser(data->username); @@ -430,19 +465,20 @@ static void cmd_login(struct socket *sk, struct uidata *data, int argc, wchar_t if(data->uid == -1) { sq(sk, 0, L"506", L"Authentication error", NULL); - flog(LOG_INFO, "user %ls authenticated successfully, but no account existed", data->username); + flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formatsockpeer(sk)); logout(data); } else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) { sq(sk, 0, L"506", L"Authentication error", NULL); - flog(LOG_INFO, "user %ls authenticated successfully, but was not authorized", data->username); + flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formatsockpeer(sk)); logout(data); } else { sq(sk, 0, L"200", L"Welcome", NULL); - flog(LOG_INFO, "%ls (UID %i) logged in", data->username, data->uid); + flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formatsockpeer(sk)); } break; case AUTH_DENIED: - sq(sk, 0, L"506", L"Authentication error", L"%%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL); + sq(sk, 0, L"506", L"Authentication error", L"%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL); + flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk)); logout(data); break; case AUTH_PASS: @@ -464,15 +500,15 @@ static void cmd_login(struct socket *sk, struct uidata *data, int argc, wchar_t code = 304; break; } - sq(sk, 0, L"%%i", code, data->auth->text, NULL); + sq(sk, 0, L"%i", code, data->auth->text, NULL); break; case AUTH_ERR: - sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL); + sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL); logout(data); break; default: flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_login"); - sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL); + sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL); logout(data); break; } @@ -494,7 +530,7 @@ static void cmd_pass(struct socket *sk, struct uidata *data, int argc, wchar_t * sq(sk, 0, L"507", L"Data not expected", NULL); return; } - switch(authenticate(data->auth, buf)) + switch(authenticate(data->auth, sk, buf)) { case AUTH_SUCCESS: data->userinfo = finduser(data->username); @@ -503,19 +539,20 @@ static void cmd_pass(struct socket *sk, struct uidata *data, int argc, wchar_t * if(data->uid == -1) { sq(sk, 0, L"506", L"Authentication error", NULL); - flog(LOG_INFO, "user %ls authenticated successfully, but no account existed", data->username); + flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formatsockpeer(sk)); logout(data); } else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) { sq(sk, 0, L"506", L"Authentication error", NULL); - flog(LOG_INFO, "user %ls authenticated successfully, but was not authorized", data->username); + flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formatsockpeer(sk)); logout(data); } else { sq(sk, 0, L"200", L"Welcome", NULL); - flog(LOG_INFO, "%ls (UID %i) logged in", data->username, data->uid); + flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formatsockpeer(sk)); } break; case AUTH_DENIED: - sq(sk, 0, L"506", L"Authentication error", L"%%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL); + sq(sk, 0, L"506", L"Authentication error", L"%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL); + flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk)); logout(data); break; case AUTH_PASS: @@ -537,15 +574,15 @@ static void cmd_pass(struct socket *sk, struct uidata *data, int argc, wchar_t * code = 304; break; } - sq(sk, 0, L"%%i", code, data->auth->text, NULL); + sq(sk, 0, L"%i", code, data->auth->text, NULL); break; case AUTH_ERR: - sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL); + sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL); logout(data); break; default: flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_pass"); - sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL); + sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL); logout(data); break; } @@ -562,6 +599,11 @@ static void cmd_fnetconnect(struct socket *sk, struct uidata *data, int argc, wc haveargs(3); havepriv(PERM_FNETCTL); + for(i = 0, fn = fnetnodes; fn != NULL; i++, fn = fn->next); + if((confgetint("fnet", "maxnodes") > 0) && (i >= confgetint("fnet", "maxnodes"))) { + sq(sk, 0, L"515", L"Too many fnetnodes connected already", NULL); + return; + } if((buf = icwcstombs(argv[2], NULL)) == NULL) { sq(sk, 0, L"504", L"Could not convert data to locale charset", NULL); @@ -570,7 +612,7 @@ static void cmd_fnetconnect(struct socket *sk, struct uidata *data, int argc, wc args = NULL; for(i = 3; i < argc - 1; i += 2) newwcspair(argv[i], argv[i + 1], &args); - fn = fnetinitconnect(argv[1], buf, args); + fn = fnetinitconnect(argv[1], data->userinfo->name, buf, args); err = errno; free(buf); if(fn == NULL) @@ -578,13 +620,13 @@ static void cmd_fnetconnect(struct socket *sk, struct uidata *data, int argc, wc if(errno == EPROTONOSUPPORT) sq(sk, 0, L"511", L"No such network name", NULL); else - sq(sk, 0, L"509", L"Could not parse the address", L"%%s", strerror(err), NULL); + sq(sk, 0, L"509", L"Could not parse the address", L"%s", strerror(err), NULL); return; } linkfnetnode(fn); fnetsetname(fn, argv[2]); + sq(sk, 0, L"200", L"%i", fn->id, L"Connection under way", NULL); putfnetnode(fn); - sq(sk, 0, L"200", L"Connection under way", NULL); } static void cmd_lsnodes(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) @@ -598,7 +640,7 @@ static void cmd_lsnodes(struct socket *sk, struct uidata *data, int argc, wchar_ } for(fn = fnetnodes; fn != NULL; fn = fn->next) { - sq(sk, (fn->next != NULL)?1:0, L"200", L"%%i", fn->id, fn->fnet->name, (fn->name == NULL)?L"":fn->name, L"%%i", fn->numpeers, L"%%i", fn->state, NULL); + sq(sk, (fn->next != NULL)?1:0, L"200", L"%i", fn->id, fn->fnet->name, (fn->name == NULL)?L"":fn->name, L"%i", fn->numpeers, L"%i", fn->state, L"%ls", fn->pubid, NULL); } } @@ -621,6 +663,11 @@ static void cmd_disconnect(struct socket *sk, struct uidata *data, int argc, wch sq(sk, 0, L"510", L"No such node", NULL); return; } + if(wpfind(fn->args, L"locked") && !((data->userinfo->perms & PERM_ADMIN) || !wcscmp(data->userinfo->name, fn->owner))) + { + sq(sk, 0, L"502", L"This node is locked and you are neither administrator nor its owner", NULL); + return; + } killfnetnode(fn); unlinkfnetnode(fn); } @@ -643,7 +690,7 @@ static void cmd_lspa(struct socket *sk, struct uidata *data, int argc, wchar_t * sq(sk, 0, L"201", L"No data available", NULL); } else { for(datum = fn->peerdata; datum != NULL; datum = datum->next) - sq(sk, (datum->next != NULL)?1:0, L"200", datum->id, L"%%i", datum->datatype, NULL); + sq(sk, (datum->next != NULL)?1:0, L"200", datum->id, L"%i", datum->datatype, NULL); } } @@ -651,8 +698,7 @@ static void cmd_lspeers(struct socket *sk, struct uidata *data, int argc, wchar_ { int i; struct fnetnode *fn; - struct fnetpeer *peer; - wchar_t buf[40]; + struct fnetpeer *peer, *npeer; haveargs(2); if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL) @@ -662,24 +708,20 @@ static void cmd_lspeers(struct socket *sk, struct uidata *data, int argc, wchar_ } if(fn->peers == NULL) { - sq(sk, 0, L"201", L"No peers avaiable", NULL); + sq(sk, 0, L"201", L"No peers available", NULL); } else { - for(peer = fn->peers; peer != NULL; peer = peer->next) + for(peer = btreeiter(fn->peers); peer != NULL; peer = npeer) { - sq(sk, 2 | ((peer->next != NULL)?1:0), L"200", peer->id, peer->nick, NULL); + npeer = btreeiter(NULL); + sq(sk, 2 | ((npeer != NULL)?1:0), L"200", L"%ls", peer->id, L"%ls", peer->nick, NULL); for(i = 0; i < peer->dinum; i++) { if(peer->peerdi[i].datum->datatype == FNPD_INT) - sq(sk, 2, peer->peerdi[i].datum->id, L"%%i", peer->peerdi[i].data.num, NULL); - /* Note: A long long is not the size of an int, so - * sq() can't handle the conversion itself. */ + sq(sk, 2, peer->peerdi[i].datum->id, L"%i", peer->peerdi[i].data.num, NULL); if(peer->peerdi[i].datum->datatype == FNPD_LL) - { - swprintf(buf, 40, L"%lli", peer->peerdi[i].data.lnum); - sq(sk, 2, peer->peerdi[i].datum->id, buf, NULL); - } + sq(sk, 2, peer->peerdi[i].datum->id, L"%ll", peer->peerdi[i].data.lnum, NULL); if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL)) - sq(sk, 2, peer->peerdi[i].datum->id, peer->peerdi[i].data.str, NULL); + sq(sk, 2, peer->peerdi[i].datum->id, L"%ls", peer->peerdi[i].data.str, NULL); } sq(sk, 0, NULL); } @@ -735,7 +777,7 @@ static void cmd_download(struct socket *sk, struct uidata *data, int argc, wchar linktransfer(transfer); } if(argc > 4) - transfersetsize(transfer, wcstol(argv[4], NULL, 0)); + transfersetsize(transfer, wcstoll(argv[4], NULL, 0)); if(argc > 5) { for(i = 5; i < argc; i += 2) @@ -748,7 +790,7 @@ static void cmd_download(struct socket *sk, struct uidata *data, int argc, wchar } } } - sq(sk, 0, L"200", L"%%i", transfer->id, L"Download queued", NULL); + sq(sk, 0, L"200", L"%i", transfer->id, L"Download queued", NULL); transfersetactivity(transfer, L"create"); } @@ -763,11 +805,11 @@ static void cmd_lstrans(struct socket *sk, struct uidata *data, int argc, wchar_ if((transfer->dir != TRNSD_DOWN) || (transfer->owner == data->uid)) { if(pt != NULL) - sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->dir, - L"%%i", pt->state, pt->peerid, + sq(sk, 1, L"200", L"%i", pt->id, L"%i", pt->dir, + L"%i", pt->state, pt->peerid, (pt->peernick == NULL)?L"":(pt->peernick), (pt->path == NULL)?L"":(pt->path), - L"%%i", pt->size, L"%%i", pt->curpos, + L"%oi", pt->size, L"%oi", pt->curpos, (pt->hash == NULL)?L"":unparsehash(pt->hash), NULL); pt = transfer; @@ -776,11 +818,11 @@ static void cmd_lstrans(struct socket *sk, struct uidata *data, int argc, wchar_ if(pt == NULL) sq(sk, 0, L"201", L"No transfers", NULL); else - sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->dir, - L"%%i", pt->state, pt->peerid, + sq(sk, 0, L"200", L"%i", pt->id, L"%i", pt->dir, + L"%i", pt->state, pt->peerid, (pt->peernick == NULL)?L"":(pt->peernick), (pt->path == NULL)?L"":(pt->path), - L"%%i", pt->size, L"%%i", pt->curpos, + L"%oi", pt->size, L"%oi", pt->curpos, (pt->hash == NULL)?L"":unparsehash(pt->hash), NULL); } @@ -810,6 +852,26 @@ static void cmd_cancel(struct socket *sk, struct uidata *data, int argc, wchar_t sq(sk, 0, L"200", L"Transfer cancelled", NULL); } +static void cmd_reset(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) +{ + struct transfer *transfer; + + haveargs(2); + havepriv(PERM_TRANS); + if((transfer = findtransfer(wcstol(argv[1], NULL, 0))) == NULL) + { + sq(sk, 0, L"512", L"No such transfer", NULL); + return; + } + if(transfer->dir == TRNSD_UP) + { + sq(sk, 0, L"512", L"Only applicable to downloads", NULL); + return; + } + resettransfer(transfer); + sq(sk, 0, L"200", L"Transfer reset", NULL); +} + static void cmd_notify(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) { int i, val; @@ -843,6 +905,8 @@ static void cmd_notify(struct socket *sk, struct uidata *data, int argc, wchar_t data->notify.b.trprog = val; } else if(!wcscasecmp(argv[i], L"srch:act")) { data->notify.b.srch = val; + } else if(!wcscasecmp(argv[i], L"msg")) { + data->notify.b.msg = val; } } sq(sk, 0, L"200", L"Notification alteration succeeded", NULL); @@ -951,7 +1015,7 @@ static void cmd_search(struct socket *sk, struct uidata *data, int argc, wchar_t CBREG(srch, search_eta, srcheta, NULL, NULL); CBREG(srch, search_commit, srchcommit, NULL, NULL); CBREG(srch, search_result, srchres, NULL, NULL); - sq(sk, 0, L"200", L"%%i", srch->id, L"%%i", srch->eta - time(NULL), NULL); + sq(sk, 0, L"200", L"%i", srch->id, L"%i", srch->eta - time(NULL), NULL); putsexpr(sexpr); } @@ -968,21 +1032,20 @@ static void cmd_lssrch(struct socket *sk, struct uidata *data, int argc, wchar_t if(!wcscmp(srch->owner, data->username)) { if(pt != NULL) - sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL); + sq(sk, 1, L"200", L"%i", pt->id, L"%i", pt->state, L"%i", pt->eta - now, L"%i", pt->numres, NULL); pt = srch; } } if(pt == NULL) sq(sk, 0, L"201", L"No searches", NULL); else - sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL); + sq(sk, 0, L"200", L"%i", pt->id, L"%i", pt->state, L"%i", pt->eta - now, L"%i", pt->numres, NULL); } static void cmd_lssr(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) { struct search *srch; struct srchres *sr; - wchar_t buf[64]; haveargs(2); havepriv(PERM_SRCH); @@ -997,8 +1060,11 @@ static void cmd_lssr(struct socket *sk, struct uidata *data, int argc, wchar_t * } else { for(sr = srch->results; sr != NULL; sr = sr->next) { - swprintf(buf, 64, L"%f", sr->time); - sq(sk, (sr->next != NULL)?1:0, L"200", L"%%ls", sr->filename, sr->fnet->name, L"%%ls", sr->peerid, L"%%i", sr->size, L"%%i", sr->slots, L"%%i", (sr->fn == NULL)?-1:(sr->fn->id), buf, L"%%ls", (sr->hash == NULL)?L"":unparsehash(sr->hash), NULL); + sq(sk, (sr->next != NULL)?1:0, L"200", L"%ls", sr->filename, + sr->fnet->name, L"%ls", sr->peerid, L"%oi", sr->size, + L"%i", sr->slots, L"%i", (sr->fn == NULL)?-1:(sr->fn->id), + L"%f", sr->time, + L"%ls", (sr->hash == NULL)?L"":unparsehash(sr->hash), NULL); } } } @@ -1054,7 +1120,7 @@ static void fcmderr(struct socket *sk, int err, struct uidata *data) data->fcmdbuf = NULL; } data->fcmdbufsize = data->fcmdbufdata = 0; - sq(data->sk, 0, L"505", L"An error occurred on the pipe to the filtercmd", L"%%s", strerror(err), NULL); + sq(data->sk, 0, L"505", L"An error occurred on the pipe to the filtercmd", L"%s", strerror(err), NULL); return; } putsock(data->fcmdsk); @@ -1079,7 +1145,7 @@ static void fcmderr(struct socket *sk, int err, struct uidata *data) for(p2 = wcschr(p, L'\n'); p2 != NULL; p2 = wcschr(p, L'\n')) { *(p2++) = L'\0'; - sq(data->sk, (*p2 == L'\0')?0:1, L"200", L"%%ls", p, NULL); + sq(data->sk, (*p2 == L'\0')?0:1, L"200", L"%ls", p, NULL); p = p2; } if(*p == L'\0') @@ -1087,7 +1153,7 @@ static void fcmderr(struct socket *sk, int err, struct uidata *data) if(p == wbuf) sq(data->sk, 0, L"201", L"No data returned", NULL); } else { - sq(data->sk, 0, L"200", L"%%ls", p, NULL); + sq(data->sk, 0, L"200", L"%ls", p, NULL); } free(wbuf); } @@ -1110,7 +1176,10 @@ static void cmd_filtercmd(struct socket *sk, struct uidata *data, int argc, wcha sq(sk, 0, L"505", L"System error - Could not fork session", "Internal error", NULL); return; } - if((filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), "dcdl-filtercmd", pwent->pw_dir)) == NULL) + filtercmd = findfile("dc-filtercmd", pwent->pw_dir, 0); + if(filtercmd == NULL) + filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), NULL, 0); + if(filtercmd == NULL) { flog(LOG_WARNING, "could not find filtercmd executable for user %s", pwent->pw_name); sq(sk, 0, L"505", L"System error - Could not fork session", L"Could not find filtercmd executable", NULL); @@ -1126,7 +1195,7 @@ static void cmd_filtercmd(struct socket *sk, struct uidata *data, int argc, wcha for(i = 0; i < cargvdata; i++) free(cargv[i]); free(cargv); - sq(sk, 0, L"504", L"%Could not convert argument %i into local character set", i, L"%%s", strerror(errno), NULL); + sq(sk, 0, L"504", L"Could not convert argument into local character set", L"%i", i, L"%s", strerror(errno), NULL); return; } addtobuf(cargv, argbuf); @@ -1135,7 +1204,7 @@ static void cmd_filtercmd(struct socket *sk, struct uidata *data, int argc, wcha if((pid = forksess(data->uid, data->auth, NULL, NULL, FD_FILE, 0, O_RDWR, "/dev/null", FD_PIPE, 1, O_RDONLY, &pipe, FD_FILE, 2, O_RDWR, "/dev/null", FD_END)) < 0) { flog(LOG_WARNING, "could not fork session in filtercmd: %s", strerror(errno)); - sq(sk, 0, L"505", L"System error - Could not fork session", L"%%s", strerror(errno), NULL); + sq(sk, 0, L"505", L"System error - Could not fork session", L"%s", strerror(errno), NULL); return; } if(pid == 0) @@ -1182,7 +1251,7 @@ static void cmd_lstrarg(struct socket *sk, struct uidata *data, int argc, wchar_ sq(sk, 0, L"201", L"Transfer has no arguments", NULL); } else { for(ta = transfer->args; ta != NULL; ta = ta->next) - sq(sk, ta->next != NULL, L"200", L"%%ls", ta->key, L"%%ls", ta->val, NULL); + sq(sk, ta->next != NULL, L"200", L"%ls", ta->key, L"%ls", ta->val, NULL); } } @@ -1201,19 +1270,95 @@ static void cmd_hashstatus(struct socket *sk, struct uidata *data, int argc, wch hashed++; } } - sq(sk, 0, L"200", L"%%i", total, L"tth", L"%%i", hashed, NULL); + sq(sk, 0, L"200", L"%i", total, L"tth", L"%i", hashed, NULL); } static void cmd_transstatus(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) { - wchar_t *buf1, *buf2; - havepriv(PERM_TRANS); - buf1 = swprintf2(L"%lli", bytesdownload); - buf2 = swprintf2(L"%lli", bytesupload); - sq(sk, 0, L"200", L"%%ls", buf1, L"%%ls", buf2, NULL); - free(buf1); - free(buf2); + sq(sk, 0, L"200", L"down", L"%ll", bytesdownload, L"up", L"%ll", bytesupload, NULL); +} + +static void cmd_register(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) +{ + struct uidata *d2; + + haveargs(2); + if(data->userinfo == NULL) { + sq(sk, 0, L"502", L"Must be logged in", NULL); + return; + } + if(argv[1][0] == L'#') { + sq(sk, 0, L"509", L"Name must not begin with a hash sign", NULL); + return; + } + for(d2 = actives; d2 != NULL; d2 = d2->next) { + if((d2 != data) && (d2->userinfo == data->userinfo) && d2->regname && !wcscmp(d2->regname, argv[1])) { + sq(sk, 0, L"516", L"Name already in use", NULL); + return; + } + } + if(data->regname != NULL) + free(data->regname); + data->regname = swcsdup(argv[1]); + sq(sk, 0, L"200", L"Registered", NULL); +} + +static void cmd_sendmsg(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) +{ + int i, rcptid; + struct uidata *rcpt; + wchar_t *myname; + struct notif *notif; + + haveargs(2); + if(data->userinfo == NULL) { + sq(sk, 0, L"502", L"Must be logged in", NULL); + return; + } + if(argv[1][0] == L'#') { + rcptid = wcstol(argv[1] + 1, NULL, 0); + for(rcpt = actives; rcpt != NULL; rcpt = rcpt->next) { + if((rcpt->userinfo == data->userinfo) && (rcpt->id == rcptid)) + break; + } + } else { + for(rcpt = actives; rcpt != NULL; rcpt = rcpt->next) { + if((rcpt->userinfo == data->userinfo) && rcpt->regname && !wcscmp(rcpt->regname, argv[1])) + break; + } + } + if(rcpt == NULL) { + sq(sk, 0, L"517", L"No such recipient", NULL); + return; + } + if(!rcpt->notify.b.msg) { + sq(sk, 0, L"518", L"Recipient not listening for messages", NULL); + return; + } + if(data->regname != NULL) + myname = swcsdup(data->regname); + else + myname = swprintf2(L"#%i", data->id); + notif = newnotif(rcpt, 640, NOTIF_STR, myname, NOTIF_END); + for(i = 2; i < argc; i++) + notifappend(notif, NOTIF_STR, argv[i], NOTIF_END); + sq(sk, 0, L"200", L"Message sent", NULL); +} + +static void cmd_uptime(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) +{ + sq(sk, 0, L"200", L"%i", time(NULL) - starttime, NULL); +} + +static void cmd_hup(struct socket *sk, struct uidata *data, int argc, wchar_t **argv) +{ + extern volatile int reinit; + + havepriv(PERM_ADMIN); + flog(LOG_NOTICE, "UI HUP request from %ls", data->username); + reinit = 1; + sq(sk, 0, L"200", L"Will reinit", NULL); } #undef haveargs @@ -1242,6 +1387,7 @@ static struct command commands[] = {L"download", cmd_download}, {L"lstrans", cmd_lstrans}, {L"cancel", cmd_cancel}, + {L"reset", cmd_reset}, {L"notify", cmd_notify}, {L"sendchat", cmd_sendchat}, {L"search", cmd_search}, @@ -1252,6 +1398,10 @@ static struct command commands[] = {L"lstrarg", cmd_lstrarg}, {L"hashstatus", cmd_hashstatus}, {L"transstatus", cmd_transstatus}, + {L"register", cmd_register}, + {L"sendmsg", cmd_sendmsg}, + {L"uptime", cmd_uptime}, + {L"hup", cmd_hup}, {NULL, NULL} }; @@ -1275,6 +1425,7 @@ static struct qcommand *unlinkqcmd(struct uidata *data) qcmd = data->queue; if(qcmd != NULL) { + data->queuesize--; data->queue = qcmd->next; if(qcmd == data->queuelast) data->queuelast = qcmd->next; @@ -1297,8 +1448,11 @@ static void notifappendv(struct notif *notif, va_list args) case NOTIF_ID: notif->argv[ca].d.n = va_arg(args, int); break; + case NOTIF_OFF: + notif->argv[ca].d.o = va_arg(args, off_t); + break; case NOTIF_STR: - notif->argv[ca].d.s = wcsdup(va_arg(args, wchar_t *)); + notif->argv[ca].d.s = swcsdup(va_arg(args, wchar_t *)); break; case NOTIF_FLOAT: notif->argv[ca].d.d = va_arg(args, double); @@ -1411,6 +1565,7 @@ static void freeuidata(struct uidata *data) actives = data->next; data->sk->readcb = NULL; data->sk->errcb = NULL; + closesock(data->sk); putsock(data->sk); while((qcmd = unlinkqcmd(data)) != NULL) freequeuecmd(qcmd); @@ -1427,6 +1582,8 @@ static void freeuidata(struct uidata *data) } if(data->auth != NULL) authputhandle(data->auth); + if(data->regname != NULL) + free(data->regname); if(data->username != NULL) { if(data->userinfo != NULL) @@ -1459,14 +1616,17 @@ static void queuecmd(struct uidata *data, struct command *cmd, int argc, wchar_t data->queuelast = new; if(data->queue == NULL) data->queue = new; + data->queuesize++; } static struct uidata *newuidata(struct socket *sk) { struct uidata *data; + static int curid = 0; data = smalloc(sizeof(*data)); memset(data, 0, sizeof(*data)); + data->id = curid++; data->sk = sk; getsock(sk); data->inbuf = smalloc(1024); @@ -1649,6 +1809,11 @@ static void uiread(struct socket *sk, struct uidata *data) break; } } + if(data->cbdata > 16384) + { + /* Kill clients that send us unreasonably long lines */ + data->close = 1; + } } static void uierror(struct socket *sk, int err, struct uidata *data) @@ -1658,7 +1823,7 @@ static void uierror(struct socket *sk, int err, struct uidata *data) freeuidata(data); } -static void uiaccept(struct socket *sk, struct socket *newsk, void *data) +static void uiaccept(struct lport *lp, struct socket *newsk, void *data) { struct uidata *uidata; @@ -1703,7 +1868,7 @@ static int srchres(struct search *srch, struct srchres *sr, void *uudata) { if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username)) { - newnotif(data, 622, NOTIF_ID, srch->id, NOTIF_STR, sr->filename, NOTIF_STR, sr->fnet->name, NOTIF_STR, sr->peerid, NOTIF_INT, sr->size, + newnotif(data, 622, NOTIF_ID, srch->id, NOTIF_STR, sr->filename, NOTIF_STR, sr->fnet->name, NOTIF_STR, sr->peerid, NOTIF_OFF, sr->size, NOTIF_INT, sr->slots, NOTIF_INT, (sr->fn == NULL)?-1:(sr->fn->id), NOTIF_FLOAT, sr->time, NOTIF_STR, (sr->hash == NULL)?L"":unparsehash(sr->hash), NOTIF_END); } } @@ -1866,7 +2031,7 @@ static int transferchattr(struct transfer *transfer, wchar_t *attrib, void *uuda for(data = actives; data != NULL; data = data->next) { if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid))) - newnotif(data, 613, NOTIF_ID, transfer->id, NOTIF_INT, transfer->size, NOTIF_END); + newnotif(data, 613, NOTIF_ID, transfer->id, NOTIF_OFF, transfer->size, NOTIF_END); } } else if(!wcscmp(attrib, L"error")) { for(data = actives; data != NULL; data = data->next) @@ -1900,9 +2065,9 @@ static int transferprog(struct transfer *transfer, void *uudata) if(haspriv(data, PERM_TRANS) && data->notify.b.trprog && ((transfer->owner == 0) || (transfer->owner == data->uid))) { if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 615, transfer->id)) != NULL) - notif->argv[1].d.n = transfer->curpos; + notif->argv[1].d.o = transfer->curpos; else - newnotif(data, 615, NOTIF_ID, transfer->id, NOTIF_INT, transfer->curpos, NOTIF_END)->rlimit = 0.5; + newnotif(data, 615, NOTIF_ID, transfer->id, NOTIF_OFF, transfer->curpos, NOTIF_END)->rlimit = 0.5; } } return(0); @@ -1915,7 +2080,7 @@ static int transferdestroyed(struct transfer *transfer, void *uudata) for(data = actives; data != NULL; data = data->next) { if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid))) - newnotif(data, 617, NOTIF_ID, transfer->id, NOTIF_END); + newnotif(data, 617, NOTIF_ID, transfer->id, NOTIF_STR, (transfer->exitstatus == NULL)?L"":(transfer->exitstatus), NOTIF_END); } return(0); } @@ -2019,7 +2184,7 @@ static void preinit(int hup) if(!hup) { - newuser(L"default", 0); + newuser(L"default", PERM_DISALLOW); } else { for(user = users; user != NULL; user = user->next) { @@ -2029,56 +2194,97 @@ static void preinit(int hup) } } -#ifdef HAVE_IPV6 -static struct sockaddr *getnameforport(int port, socklen_t *len) +static struct sockaddr_un *makeunixname(void) { - static struct sockaddr_in6 addr; + static struct sockaddr_un buf; + char *val; + struct passwd *pwd; + uid_t uid; - memset(&addr, 0, sizeof(addr)); - addr.sin6_family = AF_INET6; - addr.sin6_port = htons(port); - addr.sin6_addr = in6addr_any; - if(len != NULL) - *len = sizeof(addr); - return((struct sockaddr *)&addr); -} -#else -static struct sockaddr *getnameforport(int port, socklen_t *len) -{ - static struct sockaddr_in addr; + memset(&buf, 0, sizeof(buf)); + buf.sun_family = PF_UNIX; + if((val = icswcstombs(confgetstr("ui", "unixsock"), NULL, NULL)) == NULL) { + flog(LOG_WARNING, "could not map Unix socket name into local charset: %s", strerror(errno)); + return(NULL); + } + if(!strcmp(val, "none")) + return(NULL); + if(!strcmp(val, "default")) + { + if((uid = getuid()) == 0) + { + strcpy(buf.sun_path, "/var/run/doldacond.sock"); + return(&buf); + } else { + if((pwd = getpwuid(uid)) == NULL) + { + flog(LOG_ERR, "could not get passwd entry for current user: %s", strerror(errno)); + return(NULL); + } + strcpy(buf.sun_path, "/tmp/doldacond-"); + strcat(buf.sun_path, pwd->pw_name); + return(&buf); + } + } + if(strchr(val, '/')) + { + strcpy(buf.sun_path, val); + return(&buf); + } + flog(LOG_WARNING, "invalid Unix socket name: %s", val); + return(NULL); +} + +static int tcpportupdate(struct configvar *var, void *uudata) +{ + struct lport *newsock; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if(len != NULL) - *len = sizeof(addr); - return((struct sockaddr *)&addr); + newsock = NULL; + if((var->val.num != -1) && ((newsock = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL)) + { + flog(LOG_WARNING, "could not create new TCP UI socket, reverting to old: %s", strerror(errno)); + return(0); + } + if(tcpsocket != NULL) + { + closelport(tcpsocket); + tcpsocket = NULL; + } + tcpsocket = newsock; + return(0); } -#endif -static int portupdate(struct configvar *var, void *uudata) +static int unixsockupdate(struct configvar *var, void *uudata) { - struct sockaddr *addr; - socklen_t addrlen; - struct socket *newsock; + struct lport *newsock; + struct sockaddr_un *un; + mode_t ou; - addr = getnameforport(var->val.num, &addrlen); - if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL) + newsock = NULL; + ou = umask(0111); + if(((un = makeunixname()) != NULL) && ((newsock = netcslistenlocal(SOCK_STREAM, (struct sockaddr *)un, sizeof(*un), uiaccept, NULL)) == NULL)) { - flog(LOG_WARNING, "could not create new UI socket, reverting to old: %s", strerror(errno)); + umask(ou); + flog(LOG_WARNING, "could not create new Unix UI socket, reverting to old: %s", strerror(errno)); return(0); } - if(uisocket != NULL) - putsock(uisocket); - uisocket = newsock; + umask(ou); + if(unixsocket != NULL) + { + closelport(unixsocket); + unixsocket = NULL; + } + unixsocket = newsock; return(0); } static int init(int hup) { - struct sockaddr *addr; - socklen_t addrlen; struct uiuser *user, *next; + struct sockaddr_un *un; + struct passwd *pwd; + wchar_t *wcsname; + mode_t ou; if(hup) { @@ -2091,18 +2297,48 @@ static int init(int hup) } if(!hup) { - if(uisocket != NULL) - putsock(uisocket); - addr = getnameforport(confgetint("ui", "port"), &addrlen); - if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL) + starttime = time(NULL); + if((confgetint("ui", "port") != -1) && ((tcpsocket = netcstcplisten(confgetint("ui", "port"), 1, uiaccept, NULL)) == NULL)) { - flog(LOG_CRIT, "could not create UI socket: %s", strerror(errno)); + flog(LOG_CRIT, "could not create TCP UI socket: %s", strerror(errno)); return(1); } - CBREG(confgetvar("ui", "port"), conf_update, portupdate, NULL, NULL); + CBREG(confgetvar("ui", "port"), conf_update, tcpportupdate, NULL, NULL); + ou = umask(0111); + if(((un = makeunixname()) != NULL) && ((unixsocket = netcslistenlocal(SOCK_STREAM, (struct sockaddr *)un, sizeof(*un), uiaccept, NULL)) == NULL)) + { + umask(ou); + flog(LOG_CRIT, "could not create Unix UI socket: %s", strerror(errno)); + return(1); + } + umask(ou); + CBREG(confgetvar("ui", "unixsock"), conf_update, unixsockupdate, NULL, NULL); GCBREG(newfncb, newfnetnode, NULL); GCBREG(newtransfercb, newtransfernotify, NULL); } + if(getuid() != 0) + { + for(user = users; user != NULL; user = user->next) + { + if(wcscmp(user->name, L"default")) + break; + } + if(!user) + { + if((pwd = getpwuid(getuid())) == NULL) + { + flog(LOG_CRIT, "could not get login info: %s", strerror(errno)); + return(1); + } + if((wcsname = icmbstowcs(pwd->pw_name, NULL)) == NULL) + { + flog(LOG_CRIT, "could not convert user name into wcs: %s", strerror(errno)); + return(1); + } + newuser(wcsname, ~PERM_DISALLOW); + free(wcsname); + } + } return(0); } @@ -2112,7 +2348,6 @@ static int run(void) struct uidata *data, *next; struct qcommand *qcmd; struct notif *notif, *nnotif; - wchar_t buf[64]; for(data = actives; data != NULL; data = next) { @@ -2138,24 +2373,26 @@ static int run(void) } if(findnotif(notif->prev, 0, -1, notif->code, id) != NULL) continue; - sq(data->sk, 2, L"%%i", notif->code, NULL); + sq(data->sk, 2, L"%i", notif->code, NULL); for(i = 0; i < notif->argc; i++) { switch(notif->argv[i].dt) { case NOTIF_INT: case NOTIF_ID: - sq(data->sk, 2, L"%%i", notif->argv[i].d.n, NULL); + sq(data->sk, 2, L"%i", notif->argv[i].d.n, NULL); + break; + case NOTIF_OFF: + sq(data->sk, 2, L"%oi", notif->argv[i].d.o, NULL); break; case NOTIF_STR: if(notif->argv[i].d.s[0] == L'%') - sq(data->sk, 2, L"%%s", notif->argv[i].d.s, NULL); + sq(data->sk, 2, L"%ls", notif->argv[i].d.s, NULL); else sq(data->sk, 2, notif->argv[i].d.s, NULL); break; case NOTIF_FLOAT: - swprintf(buf, 64, L"%f", notif->argv[i].d.d); - sq(data->sk, 2, buf, NULL); + sq(data->sk, 2, L"%f", notif->argv[i].d.d, NULL); break; } } @@ -2174,6 +2411,15 @@ static int run(void) freequeuecmd(qcmd); return(1); } + if(data->queuesize > 10) + { + /* Clients should not be queue up commands at all, since + * they should not send a new command before receiving a + * reply to the previous command. Therefore, we + * mercilessly massacre clients which are stacking up too + * many commands. */ + data->close = 1; + } } return(0); } @@ -2182,13 +2428,38 @@ static void terminate(void) { while(users != NULL) freeuser(users); + if(tcpsocket != NULL) + closelport(tcpsocket); + if(unixsocket != NULL) + closelport(unixsocket); } static struct configvar myvars[] = { + /** If true, UI connections will only be accepted from localhost + * addresses (127.0.0.1, ::1 or ::ffff:127.0.0.1). Unless you are + * completely sure that you know what you are doing, never turn + * this off when auth.authless is on. */ {CONF_VAR_BOOL, "onlylocal", {.num = 1}}, + /** The TCP port number on which to accept UI client connections, + * or -1 to not listen on TCP. */ {CONF_VAR_INT, "port", {.num = 1500}}, + /** + * Controls the the name to use for the Unix socket on which to + * accept UI client connections. If the name contains a slash, it + * is treated as a file name to bind on. If the name is "default", + * the file name will be "/var/run/doldacond.sock" if doldacond + * runs with UID == 0, or "/tmp/doldacond-NAME" otherwise, where + * NAME is the user name of the UID which doldacond runs as. If + * the name is "none", no Unix socket will be used. Otherwise, an + * error is signaled. + */ + {CONF_VAR_STRING, "unixsock", {.str = L"default"}}, + /** The TOS value to use for UI connections (see the TOS VALUES + * section). */ {CONF_VAR_INT, "uitos", {.num = SOCK_TOS_MINDELAY}}, + /** The name of the filtercmd script (see the FILES section for + * lookup information). */ {CONF_VAR_STRING, "filtercmd", {.str = L"dc-filtercmd"}}, {CONF_VAR_END} };