#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
+#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
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;
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;
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;
+static time_t starttime;
static wchar_t *quoteword(wchar_t *word)
{
static void cmd_connect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
{
int valid;
+ struct in6_addr mv4lo;
if(confgetint("ui", "onlylocal"))
{
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));
+ inet_pton(AF_INET6, "::ffff:127.0.0.1", &mv4lo);
+ valid = 0;
+ if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))
+ valid = 1;
+ if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback)))
+ valid = 1;
break;
default:
valid = 0;
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, formataddress(sk->remote, sk->remotelen));
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, formataddress(sk->remote, sk->remotelen));
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, formataddress(sk->remote, sk->remotelen));
}
break;
case AUTH_DENIED:
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, formataddress(sk->remote, sk->remotelen));
logout(data);
break;
case AUTH_PASS:
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, formataddress(sk->remote, sk->remotelen));
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, formataddress(sk->remote, sk->remotelen));
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, formataddress(sk->remote, sk->remotelen));
}
break;
case AUTH_DENIED:
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, formataddress(sk->remote, sk->remotelen));
logout(data);
break;
case AUTH_PASS:
char *buf;
int err;
struct fnetnode *fn;
+ struct wcspair *args;
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);
return;
}
- fn = fnetinitconnect(argv[1], buf);
+ args = NULL;
+ for(i = 3; i < argc - 1; i += 2)
+ newwcspair(argv[i], argv[i + 1], &args);
+ fn = fnetinitconnect(argv[1], buf, args);
err = errno;
free(buf);
if(fn == NULL)
sq(sk, 0, L"509", L"Could not parse the address", L"%%s", strerror(err), NULL);
return;
}
- for(i = 3; i < argc - 1; i += 2)
- {
- if(!wcscmp(argv[i], L"nick"))
- fnetsetnick(fn, argv[i + 1]);
- }
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)
}
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);
}
}
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);
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)
+ if((filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), "dcdl-filtercmd", pwent->pw_dir, 0)) == 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);
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);
+}
+
+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);
+}
+
#undef haveargs
#undef havepriv
{L"filtercmd", cmd_filtercmd},
{L"lstrarg", cmd_lstrarg},
{L"hashstatus", cmd_hashstatus},
+ {L"transstatus", cmd_transstatus},
+ {L"register", cmd_register},
+ {L"sendmsg", cmd_sendmsg},
+ {L"uptime", cmd_uptime},
{NULL, NULL}
};
}
if(data->auth != NULL)
authputhandle(data->auth);
+ if(data->regname != NULL)
+ free(data->regname);
if(data->username != NULL)
{
if(data->userinfo != NULL)
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);
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);
}
}
}
-#ifdef HAVE_IPV6
-static struct sockaddr *getnameforport(int port, socklen_t *len)
-{
- static struct sockaddr_in6 addr;
-
- 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(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- if(len != NULL)
- *len = sizeof(addr);
- return((struct sockaddr *)&addr);
-}
-#endif
-
static int portupdate(struct configvar *var, void *uudata)
{
- struct sockaddr *addr;
- socklen_t addrlen;
struct socket *newsock;
- addr = getnameforport(var->val.num, &addrlen);
- if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL)
+ if((uisocket = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL)
{
flog(LOG_WARNING, "could not create new UI socket, reverting to old: %s", strerror(errno));
return(0);
static int init(int hup)
{
- struct sockaddr *addr;
- socklen_t addrlen;
struct uiuser *user, *next;
if(hup)
}
if(!hup)
{
+ starttime = time(NULL);
if(uisocket != NULL)
putsock(uisocket);
- addr = getnameforport(confgetint("ui", "port"), &addrlen);
- if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL)
+ if((uisocket = netcstcplisten(confgetint("ui", "port"), 1, uiaccept, NULL)) == NULL)
{
flog(LOG_CRIT, "could not create UI socket: %s", strerror(errno));
return(1);