/*
* Copyright (c) 2003-2016 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 "cgi.h"
#include "flock.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "pathnames.h"
/* Initialize the session manager. */
int
CGI_SessionMgrInit(void)
{
SetGlobal("CGI_USERNAME_MAX", "%d", CGI_USERNAME_MAX);
SetGlobal("CGI_PASSWORD_MAX", "%d", CGI_PASSWORD_MAX);
SetGlobal("CGI_EMAIL_MAX", "%d", CGI_EMAIL_MAX);
if (mkdir(_PATH_SESSIONS, 0700) == -1 && errno != EEXIST) {
CGI_Log(CGI_LOG_EMERG, "%s: %s", _PATH_SESSIONS, CGI_GetError());
return (-1);
}
return (0);
}
/* Initialize a session structure. */
void
CGI_SessionInit(CGI_Session *sess, CGI_SessionOps *ops)
{
sess->ops = ops;
sess->id[0] = '\0';
TAILQ_INIT(&sess->vars);
if (ops->init != NULL)
ops->init(sess);
}
/* Release a session structure. */
void
CGI_SessionDestroy(CGI_Session *sess)
{
CGI_SessionVar *var, *nvar;
if (sess->ops->destroy != NULL) {
sess->ops->destroy(sess);
}
for (var = TAILQ_FIRST(&sess->vars);
var != TAILQ_END(&sess->vars);
var = nvar) {
nvar = TAILQ_NEXT(var, vars);
free(var);
}
}
/* Free resources allocated by the session manager. */
void
CGI_SessionMgrDestroy(void)
{
/* Nothing to do */
}
/* Terminate a session gracefully. */
void
CGI_CloseSession(CGI_Session *sess)
{
char path[MAXPATHLEN];
int i;
if (sess->ops->sessClose != NULL) {
sess->ops->sessClose(sess);
}
for (i = 0; i < nCgiModules; i++) {
CGI_Module *mod = cgiModules[i];
if (mod->sessClose != NULL)
mod->sessClose(sess);
}
Strlcpy(path, _PATH_SESSIONS, sizeof(path));
Strlcat(path, sess->id, sizeof(path));
unlink(path);
Strlcpy(path, _PATH_SESSIONS, sizeof(path));
Strlcat(path, sess->id, sizeof(path));
Strlcat(path, ".sock", sizeof(path));
unlink(path);
}
/* Load session information from disk. */
int
CGI_SessionLoad(void *pSess, int fd)
{
CGI_Session *sess = pSess;
Uint32 i, count;
if (SYS_ReadUint32(fd) != CGI_SESSION_DATA_MAGIC) {
CGI_SetError("Bad magic");
return (-1);
}
if ((count = SYS_ReadUint32(fd)) == 0 ||
count > CGI_SESSION_VARIABLES_MAX) {
CGI_SetError("Bad SV count: %u", (Uint)count);
return (-1);
}
for (i = 0; i < count; i++) {
CGI_SessionVar *sv;
if (!(sv = TryMalloc(sizeof(CGI_SessionVar)))) {
return (-1);
}
SYS_CopyString(sv->key, fd, sizeof(sv->key));
SYS_CopyString(sv->value, fd, sizeof(sv->value));
TAILQ_INSERT_TAIL(&sess->vars, sv, vars);
}
/* Read any additional session-manager-specific data. */
if (sess->ops->load != NULL &&
sess->ops->load(sess, fd) == -1) {
return (-1);
}
return (0);
}
/* Save session information to disk. */
int
CGI_SessionSaveToFD(void *pSess, int exFd)
{
CGI_Session *sess = pSess;
CGI_SessionVar *sv;
int fd, openedFile;
Uint32 count;
if (exFd != -1) {
fd = exFd;
openedFile = 0;
} else {
char path[MAXPATHLEN];
Strlcpy(path, _PATH_SESSIONS, sizeof(path));
Strlcat(path, sess->id, sizeof(path));
if ((fd = open(path, O_WRONLY|O_CREAT|O_EXLOCK, 0600)) == -1) {
CGI_SetError("%s: %s", path, strerror(errno));
return (-1);
}
openedFile = 1;
}
SYS_WriteUint32(fd, CGI_SESSION_DATA_MAGIC);
count = 0;
TAILQ_FOREACH(sv, &sess->vars, vars) { /* XXX */
count++;
}
SYS_WriteUint32(fd, count);
TAILQ_FOREACH(sv, &sess->vars, vars) {
SYS_WriteString(fd, sv->key);
SYS_WriteString(fd, sv->value);
}
/* Write any extra session-manager specific data */
if (sess->ops->save != NULL) {
sess->ops->save(sess, fd);
}
if (openedFile) {
close(fd);
}
return (0);
}
/* Update a variable in every session opened by a given user. */
int
CGI_SetSV_ALL(CGI_SessionOps *sessOps, const char *user, const char *key,
const char *val)
{
char path[MAXPATHLEN];
struct dirent *dent;
CGI_Session *sess;
const char *sessUser;
DIR *dir;
int fd;
if ((dir = opendir(_PATH_SESSIONS)) == NULL) {
CGI_SetError("%s: %s", _PATH_SESSIONS, strerror(errno));
return (-1);
}
while ((dent = readdir(dir)) != NULL) {
if (strchr(dent->d_name, '.') != NULL) {
continue;
}
Strlcpy(path, _PATH_SESSIONS, sizeof(path));
Strlcat(path, dent->d_name, sizeof(path));
/* Read session data */
if ((fd = open(path, O_RDONLY|O_EXLOCK)) == -1) {
CGI_LogErr("%s: %s (skipping)", dent->d_name,
strerror(errno));
continue;
}
if ((sess = TryMalloc(sessOps->size)) == NULL) {
close(fd);
continue;
}
CGI_SessionInit(sess, sessOps);
CGI_SessionLoad(sess, fd);
close(fd);
if ((sessUser = CGI_GetSV(sess, "user")) == NULL ||
strcmp(sessUser, user) != 0) {
free(sess);
continue;
}
/* Update entry and write session to disk. */
if ((fd = open(path, O_WRONLY|O_EXCL)) == -1) {
CGI_LogErr("%s: %s (not updating)", dent->d_name,
strerror(errno));
free(sess);
continue;
}
/* CGI_LogDebug("SetSV: %s => %s", dent->d_name, key); */
CGI_SetSV_S(sess, key, val);
if (CGI_SessionSaveToFD(sess, fd) == -1) {
CGI_LogErr("%s: %s", dent->d_name, CGI_GetError());
}
close(fd);
free(sess);
}
closedir(dir);
return (0);
}
/* Set the value of a session variable. */
void
CGI_SetSV(void *pSess, const char *key, const char *fmt, ...)
{
CGI_Session *sess = pSess;
CGI_SessionVar *var;
va_list ap;
TAILQ_FOREACH(var, &sess->vars, vars) {
if (strcmp(var->key, key) == 0)
break;
}
if (var == NULL) {
var = Malloc(sizeof(CGI_SessionVar));
Strlcpy(var->key, key, sizeof(var->key));
TAILQ_INSERT_TAIL(&sess->vars, var, vars);
}
va_start(ap, fmt);
vsnprintf(var->value, sizeof(var->value), fmt, ap);
va_end(ap);
}
/* Set the value of a session variable. */
void
CGI_SetSV_S(void *pSess, const char *key, const char *s)
{
CGI_Session *sess = pSess;
CGI_SessionVar *var;
TAILQ_FOREACH(var, &sess->vars, vars) {
if (strcmp(var->key, key) == 0)
break;
}
if (var == NULL) {
var = Malloc(sizeof(CGI_SessionVar));
Strlcpy(var->key, key, sizeof(var->key));
TAILQ_INSERT_TAIL(&sess->vars, var, vars);
}
Strlcpy(var->value, s, sizeof(var->value));
}