/*
* Copyright (c) 2005-2007 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "agarrcsd.h"
#include "pathnames.h"
#include
const char *UserLangDefault = "en";
void
UserLink(User *u)
{
AG_ObjectAttach(&UserMgr, u);
}
void
UserUnlink(User *u)
{
AG_ObjectDetach(u);
}
User *
UserLookup(const char *name)
{
return (AG_ObjectFindF("/%s", name));
}
static void
Init(void *obj)
{
User *u = obj;
u->name[0] = '\0';
u->pass[0] = '\0';
u->real_name[0] = '\0';
u->email[0] = '\0';
u->lang[0] = 'e';
u->lang[1] = 'n';
u->lang[2] = '\0';
u->country[0] = '\0';
u->comments[0] = '\0';
u->flags = 0;
Strlcpy(u->lang, "en", sizeof(u->lang));
}
static int
Load(void *p, AG_DataSource *buf, const AG_Version *ver)
{
User *u = p;
Uint32 i, count;
AG_CopyString(u->name, buf, sizeof(u->name));
AG_CopyString(u->pass, buf, sizeof(u->pass));
AG_CopyString(u->real_name, buf, sizeof(u->real_name));
AG_CopyString(u->email, buf, sizeof(u->email));
AG_CopyString(u->lang, buf, sizeof(u->lang));
AG_CopyString(u->country, buf, sizeof(u->country));
AG_CopyString(u->comments, buf, sizeof(u->comments));
u->flags = (Uint)AG_ReadUint32(buf);
return (0);
}
static int
Save(void *p, AG_DataSource *buf)
{
User *u = p;
FILE *f;
AG_WriteString(buf, u->name);
AG_WriteString(buf, u->pass);
AG_WriteString(buf, u->real_name);
AG_WriteString(buf, u->email);
AG_WriteString(buf, u->lang);
AG_WriteString(buf, u->country);
AG_WriteString(buf, u->comments);
AG_WriteUint32(buf, (Uint32)u->flags);
return (0);
}
static int
UserNameValid(const char *name)
{
const char *c = name;
for (c = name; *c != '\0'; c++) {
if (c == name && !isalpha(*c)) {
AG_SetError("Username must begin with an alphabetic "
"character.");
return (0);
}
if (!isalnum(*c) && *c != '_' && *c != '.') {
AG_SetError("Username is limited to alphanumeric "
"characters, underscores and periods.");
return (0);
}
if (*c == '.') {
if (c[1] == '.') {
AG_SetError("Username cannot contain two "
"consecutive periods.");
return (0);
} else {
AG_SetError("Usernames cannot end with a "
"period.");
return (0);
}
}
}
return (1);
}
static int
UserEmailValid(const char *name)
{
const char *c = name;
int at = 0;
if (name[0] == '\0') {
AG_SetError("E-mail address was not specified.");
return (0);
}
for (c = name; *c != '\0'; c++) {
if (*c == '@')
at++;
}
if (at != 1) {
AG_SetError("Malformed e-mail address");
return (0);
}
/* TODO more checks */
return (1);
}
static int
SetInfosFromCommand(NS_Command *cmd, User *u)
{
char *pass = NS_CommandString(cmd, "pass");
char *real_name = NS_CommandString(cmd, "real_name");
char *email = NS_CommandString(cmd, "email");
char *lang = NS_CommandString(cmd, "lang");
char *country = NS_CommandString(cmd, "country");
Uint flags = 0;
NS_CommandInt(cmd, "flags", &flags);
if (real_name == NULL || real_name[0] == '\0' ||
pass == NULL || pass[0] == '\0' ||
email == NULL || email[0] == '\0' ||
country == NULL || country[0] == '\0') {
AG_SetError("Missing parameter.");
return (-1);
}
if (!UserEmailValid(u->email))
return (-1);
Strlcpy(u->pass, pass, sizeof(u->pass));
Strlcpy(u->real_name, real_name, sizeof(u->real_name));
Strlcpy(u->email, email, sizeof(u->email));
Strlcpy(u->country, country, sizeof(u->country));
NS_CommandCopyString(u->comments, cmd, "comments", sizeof(u->comments));
if (lang != NULL && lang[0] != '\0' && strlen(lang) == 2) {
Strlcpy(u->lang, lang, sizeof(u->lang));
} else {
Strlcpy(u->lang, UserLangDefault, sizeof(u->lang));
}
u->flags &= ~(USER_SETTABLE_FLAGS);
u->flags |= flags & USER_SETTABLE_FLAGS;
return (0);
}
/* Obtain user information. */
int
user_get_infos(NS_Command *cmd, void *p)
{
char *name = NS_CommandString(cmd, "name");
User *u;
if ((u = UserLookup(name)) == NULL)
return (-1);
printf("0 ");
printf("name=%s:", u->name);
printf("real_name=%s:", u->real_name);
printf("email=%s:", u->email);
printf("lang=%s:", u->lang);
printf("country=%s:", u->country);
printf("comments=%s:", u->comments);
printf("flags=%x:", u->flags);
printf("\n");
return (0);
}
/* Set user information. */
int
user_set_infos(NS_Command *cmd, void *p)
{
char *name = NS_CommandString(cmd, "name");
User *u;
if ((u = UserLookup(name)) == NULL) {
return (-1);
}
return (SetInfosFromCommand(cmd, u));
}
int
user_show(NS_Command *cmd, void *p)
{
User *u;
int nmatches = 0;
fputs("0 ", stdout);
AGOBJECT_FOREACH_CLASS(u, &UserMgr, user, "User:*") {
fputs(u->name, stdout);
fputc(':', stdout);
nmatches++;
}
fputc('\n', stdout);
NS_Log(NS_DEBUG, "%d users", nmatches);
if (nmatches == 0) {
AG_SetError("The user list is empty.");
return (-1);
}
return (0);
}
/* Activate a new account (admin). */
int
user_activate(NS_Command *cmd, void *p)
{
char *name = NS_CommandString(cmd, "name");
Uint flags = 0;
unsigned int h;
User *u;
NS_CommandInt(cmd, "flags", &flags);
if (name == NULL || name[0] == '\0') {
AG_SetError("Username was not specified.");
return (-1);
}
if (!UserNameValid(name)) {
return (-1);
}
if (UserLookup(name) != NULL) {
AG_SetError("Username `%s' is already taken.", name);
return (-1);
}
u = Malloc(sizeof(User));
AG_ObjectInit(u, &UserClass);
AG_ObjectSetName(u, "%s", name);
Strlcpy(u->name, name, sizeof(u->name));
if (SetInfosFromCommand(cmd, u) == -1)
goto fail;
if (AG_ObjectSave(u) == -1) {
goto fail;
}
UserLink(u);
NS_Log(NS_INFO, "Activated account `%s'.", u->name);
printf("0 ok\n");
/* Rehash the user table. */
kill(getppid(), SIGHUP);
return (0);
fail:
AG_ObjectDestroy(u);
return (-1);
}
static void *
Edit(void *p)
{
User *u = p;
AG_Window *win;
AG_Textbox *tb;
win = AG_WindowNew(0);
AG_WindowSetCaption(win, "User: %s", AGOBJECT(u)->name);
AG_WindowSetPosition(win, AG_WINDOW_LOWER_CENTER, 1);
tb = AG_TextboxNew(win, 0, "Name: ");
AG_TextboxBindUTF8(tb, u->name, sizeof(u->name));
AG_WidgetFocus(tb);
tb = AG_TextboxNew(win, 0, "Password: ");
AG_TextboxSetPassword(tb, 1);
AG_TextboxBindUTF8(tb, u->pass, sizeof(u->pass));
tb = AG_TextboxNew(win, 0, "Real name: ");
AG_TextboxBindUTF8(tb, u->real_name, sizeof(u->real_name));
tb = AG_TextboxNew(win, 0, "E-mail address: ");
AG_TextboxBindUTF8(tb, u->email, sizeof(u->email));
tb = AG_TextboxNew(win, 0, "Language (ISO639): ");
AG_TextboxBindUTF8(tb, u->lang, sizeof(u->lang));
tb = AG_TextboxNew(win, 0, "Country (ISO3166): ");
AG_TextboxBindUTF8(tb, u->country, sizeof(u->country));
tb = AG_TextboxNew(win, 0, "Comments: ");
AG_TextboxBindUTF8(tb, u->comments, sizeof(u->comments));
AG_CheckboxNewFlag(win, 0, "Write access", &u->flags, USER_WRITE);
AG_CheckboxNewFlag(win, 0, "Admin access", &u->flags, USER_ADMIN);
AG_CheckboxNewFlag(win, 0, "Send e-mail notices",
&u->flags, USER_EMAIL_NOTICES);
return (win);
}
AG_ObjectClass UserClass = {
"User",
sizeof(User),
{ 8, 0 },
Init,
NULL, /* free */
NULL, /* destroy */
Load,
Save,
Edit
};