added directory handler to qse_httpd_scb_t

This commit is contained in:
hyung-hwan 2012-10-15 16:39:23 +00:00
parent d9b42ca785
commit 7940a758d0
5 changed files with 241 additions and 65 deletions

View File

@ -65,6 +65,7 @@ enum qse_httpd_option_t
typedef struct qse_httpd_stat_t qse_httpd_stat_t;
struct qse_httpd_stat_t
{
int isdir;
qse_long_t dev;
qse_long_t ino;
qse_foff_t size;
@ -123,6 +124,14 @@ typedef int (*qse_httpd_muxcb_t) (
void* cbarg
);
typedef struct qse_httpd_dirent_t qse_httpd_dirent_t;
struct qse_httpd_dirent_t
{
qse_mchar_t* name;
qse_httpd_stat_t stat;
};
typedef struct qse_httpd_scb_t qse_httpd_scb_t;
struct qse_httpd_scb_t
{
@ -191,6 +200,17 @@ struct qse_httpd_scb_t
const qse_mchar_t* buf, qse_size_t len);
} file;
struct
{
int (*open) (
qse_httpd_t* httpd, const qse_mchar_t* path,
qse_ubi_t* handle);
void (*close) (qse_httpd_t* httpd, qse_ubi_t handle);
int (*read) (
qse_httpd_t* httpd, qse_ubi_t handle,
qse_httpd_dirent_t* ent);
} dir;
struct
{
void (*close) (
@ -225,6 +245,7 @@ struct qse_httpd_scb_t
qse_httpd_t* httpd,
qse_httpd_client_t* client); /* optional */
} client;
};
typedef struct qse_httpd_rcb_t qse_httpd_rcb_t;
@ -363,6 +384,7 @@ struct qse_httpd_rsrc_t
struct
{
const qse_mchar_t* path;
const qse_mchar_t* css;
} dir;
struct
@ -459,6 +481,8 @@ enum qse_httpd_server_xtn_cfg_idx_t
QSE_HTTPD_SERVER_XTN_CFG_USERNAME,
QSE_HTTPD_SERVER_XTN_CFG_PASSWORD,
QSE_HTTPD_SERVER_XTN_CFG_BASICAUTH,
QSE_HTTPD_SERVER_XTN_CFG_DIRCSS, /* can't be too long due to internal buffer size */
QSE_HTTPD_SERVER_XTN_CFG_ERRORCSS,
QSE_HTTPD_SERVER_XTN_CFG_MAX
};
@ -680,6 +704,7 @@ qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* name,
const qse_mchar_t* css,
qse_htre_t* req
);

View File

@ -24,22 +24,13 @@
#include <qse/cmn/fmt.h>
#include <qse/cmn/path.h>
#if defined(_WIN32)
/* TODO: */
#elif defined(__OS2__)
/* TODO: */
#elif defined(__DOS__)
/* TODO: */
#else
# include "../cmn/syscall.h"
#endif
#include <qse/cmn/stdio.h> /* TODO: remove this */
typedef struct task_dir_t task_dir_t;
struct task_dir_t
{
qse_mcstr_t path;
qse_mcstr_t css;
qse_mcstr_t qpath;
qse_http_version_t version;
int keepalive;
@ -53,19 +44,24 @@ struct task_dseg_t
int chunked;
qse_mcstr_t path;
qse_mcstr_t css;
qse_mcstr_t qpath;
qse_dir_t* handle;
qse_dirent_t* dent;
qse_ubi_t handle;
qse_httpd_dirent_t dent;
#define HEADER_ADDED (1 << 0)
#define FOOTER_ADDED (1 << 1)
#define FOOTER_PENDING (1 << 2)
#define DIRENT_PENDING (1 << 3)
#define DIRENT_NOMORE (1 << 4)
int state;
qse_size_t tcount; /* total directory entries */
qse_size_t dcount; /* the number of items in the buffer */
qse_mchar_t tmbuf[128];
qse_mchar_t fszbuf[128];
qse_mchar_t buf[4096];
qse_size_t bufpos;
qse_size_t buflen;
@ -83,7 +79,9 @@ static int task_init_dseg (
xtn->path.ptr = (qse_mchar_t*)(xtn + 1);
qse_mbscpy ((qse_mchar_t*)xtn->path.ptr, arg->path.ptr);
xtn->qpath.ptr = xtn->path.ptr + xtn->path.len + 1;
xtn->css.ptr = xtn->path.ptr + xtn->path.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->css.ptr, arg->css.ptr);
xtn->qpath.ptr = xtn->css.ptr + xtn->css.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->qpath.ptr, arg->qpath.ptr);
task->ctx = xtn;
@ -95,7 +93,7 @@ static void task_fini_dseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* ctx = (task_dseg_t*)task->ctx;
QSE_CLOSEDIR (ctx->handle);
httpd->scb->dir.close (httpd, ctx->handle);
}
#define SIZE_CHLEN 4 /* the space size to hold the hexadecimal chunk length */
@ -145,13 +143,13 @@ static int add_footer (task_dseg_t* ctx)
{
x = snprintf (
&ctx->buf[ctx->buflen], ctx->bufrem,
QSE_MT("</ul>Total %lu entries</body></html>\r\n0\r\n"), (unsigned long)ctx->tcount);
QSE_MT("</table></body></html>\r\n0\r\n"));
}
else
{
x = snprintf (
&ctx->buf[ctx->buflen], ctx->bufrem,
QSE_MT("</ul>Total %lu entries</body></html>"), (unsigned long)ctx->tcount);
QSE_MT("</table></body></html>"));
}
if (x == -1 || x >= ctx->bufrem)
@ -254,8 +252,12 @@ static int task_main_dseg (
/* TODO: html escaping of ctx->qpath.ptr */
x = snprintf (
&ctx->buf[ctx->buflen], ctx->bufrem,
QSE_MT("<html><head></head><body><b>%s</b><ul>%s"),
ctx->qpath.ptr, (is_root? QSE_MT(""): QSE_MT("<li><a href='../'>..</a></li>"))
QSE_MT("<html><head>%s%s%s</head><body><b>%s</b><table>%s"),
(ctx->css.len > 0? QSE_MT("<style type='text/css'>"): QSE_MT("")),
(ctx->css.len > 0? ctx->css.ptr: QSE_MT("")),
(ctx->css.len > 0? QSE_MT("</style>"): QSE_MT("")),
ctx->qpath.ptr,
(is_root? QSE_MT(""): QSE_MT("<tr><td><a href='../'>..</a></td><td></td><td></td></tr>"))
);
if (x == -1 || x >= ctx->bufrem)
{
@ -273,18 +275,22 @@ static int task_main_dseg (
ctx->dcount++;
}
/*if (!ctx->dent) ctx->dent = QSE_READDIR (ctx->handle); */
if (ctx->state & DIRENT_PENDING)
{
ctx->state &= ~DIRENT_PENDING;
}
else
ctx->dent = QSE_READDIR (ctx->handle);
{
if (httpd->scb->dir.read (httpd, ctx->handle, &ctx->dent) <= 0)
ctx->state |= DIRENT_NOMORE;
}
do
{
if (!ctx->dent)
if (ctx->state & DIRENT_NOMORE)
{
/* TODO: check if errno has changed from before QSE_READDIR().
and return -1 if so. */
/* no more directory entry */
if (add_footer (ctx) <= -1)
{
/* failed to add the footer part */
@ -298,31 +304,55 @@ static int task_main_dseg (
else if (ctx->chunked) fill_chunk_length (ctx);
break;
}
else if (qse_mbscmp (ctx->dent->d_name, QSE_MT(".")) != 0 &&
qse_mbscmp (ctx->dent->d_name, QSE_MT("..")) != 0)
if (qse_mbscmp (ctx->dent.name, QSE_MT(".")) != 0 &&
qse_mbscmp (ctx->dent.name, QSE_MT("..")) != 0)
{
qse_mchar_t* encname;
qse_btime_t bt;
/* TODO: better buffer management in case there are
* a lot of file names to escape. */
encname = qse_perenchttpstrdup (ctx->dent->d_name, httpd->mmgr);
encname = qse_perenchttpstrdup (ctx->dent.name, httpd->mmgr);
if (encname == QSE_NULL)
{
httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
}
qse_printf (QSE_T("ADDING [%hs]\n"), ctx->dent.name);
qse_localtime (ctx->dent.stat.mtime, &bt);
snprintf (ctx->tmbuf, QSE_COUNTOF(ctx->tmbuf),
QSE_MT("%04d-%02d-%02d %02d:%02d:%02d"),
bt.year + QSE_BTIME_YEAR_BASE, bt.mon + 1, bt.mday,
bt.hour, bt.min, bt.sec);
if (ctx->dent.stat.isdir)
{
ctx->fszbuf[0] = QSE_MT('\0');
}
else
{
qse_fmtuintmaxtombs (
ctx->fszbuf, QSE_COUNTOF(ctx->fszbuf),
ctx->dent.stat.size, 10, -1, QSE_MT('\0'), QSE_NULL
);
}
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("<li><a href='%s%s'>%s%s</a></li>"),
QSE_MT("<tr><td><a href='%s%s'>%s%s</a></td><td>%s</td><td align='right'>%s</td></tr>"),
encname,
(ctx->dent->d_type == DT_DIR? QSE_MT("/"): QSE_MT("")),
ctx->dent->d_name, /* TODO: html escaping */
(ctx->dent->d_type == DT_DIR? QSE_MT("/"): QSE_MT(""))
(ctx->dent.stat.isdir? QSE_MT("/"): QSE_MT("")),
ctx->dent.name, /* TODO: html escaping */
(ctx->dent.stat.isdir? QSE_MT("/"): QSE_MT("")),
ctx->tmbuf, ctx->fszbuf
);
if (encname != ctx->dent->d_name) QSE_MMGR_FREE (httpd->mmgr, encname);
if (encname != ctx->dent.name) QSE_MMGR_FREE (httpd->mmgr, encname);
if (x == -1 || x >= ctx->bufrem)
{
@ -355,7 +385,8 @@ static int task_main_dseg (
}
}
ctx->dent = QSE_READDIR (ctx->handle);
if (httpd->scb->dir.read (httpd, ctx->handle, &ctx->dent) <= 0)
ctx->state |= DIRENT_NOMORE;
}
while (1);
@ -369,12 +400,12 @@ send_dirlist:
/* NOTE if (n == 0), it will enter an infinite loop */
ctx->bufpos += n;
return (ctx->bufpos < ctx->buflen || (ctx->state & FOOTER_PENDING) || ctx->dent)? 1: 0;
return (ctx->bufpos < ctx->buflen || (ctx->state & FOOTER_PENDING) || !(ctx->state & DIRENT_NOMORE))? 1: 0;
}
static qse_httpd_task_t* entask_directory_segment (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, qse_dir_t* handle, task_dir_t* dir)
qse_httpd_task_t* pred, qse_ubi_t handle, task_dir_t* dir)
{
qse_httpd_task_t task;
task_dseg_t data;
@ -385,6 +416,7 @@ static qse_httpd_task_t* entask_directory_segment (
data.keepalive = dir->keepalive;
data.chunked = dir->keepalive;
data.path = dir->path;
data.css = dir->css;
data.qpath = dir->qpath;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
@ -393,7 +425,7 @@ static qse_httpd_task_t* entask_directory_segment (
task.fini = task_fini_dseg;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + data.path.len + 1 + data.qpath.len + 1);
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + data.path.len + 1 + data.css.len + 1 + data.qpath.len + 1);
}
/*------------------------------------------------------------------------*/
@ -409,7 +441,9 @@ static int task_init_dir (
xtn->path.ptr = (qse_mchar_t*)(xtn + 1);
qse_mbscpy ((qse_mchar_t*)xtn->path.ptr, arg->path.ptr);
xtn->qpath.ptr = xtn->path.ptr + xtn->path.len + 1;
xtn->css.ptr = xtn->path.ptr + xtn->path.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->css.ptr, arg->css.ptr);
xtn->qpath.ptr = xtn->css.ptr + xtn->css.len + 1;
qse_mbscpy ((qse_mchar_t*)xtn->qpath.ptr, arg->qpath.ptr);
/* switch the context to the extension area */
@ -423,15 +457,25 @@ static QSE_INLINE int task_main_dir (
{
task_dir_t* dir;
qse_httpd_task_t* x;
qse_dir_t* handle = QSE_NULL;
qse_ubi_t handle;
dir = (task_dir_t*)task->ctx;
x = task;
if (qse_mbsend (dir->path.ptr, QSE_MT("/")))
{
handle = QSE_OPENDIR (dir->path.ptr);
if (handle)
if (httpd->scb->dir.open (httpd, dir->path.ptr, &handle) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_error (
httpd, client, x, http_errnum,
&dir->version, dir->keepalive);
return (x == QSE_NULL)? -1: 0;
}
else
{
x = qse_httpd_entaskformat (
httpd, client, x,
@ -445,21 +489,9 @@ static QSE_INLINE int task_main_dir (
if (x) x = entask_directory_segment (httpd, client, x, handle, dir);
if (x) return 0;
QSE_CLOSEDIR (handle);
httpd->scb->dir.close (httpd, handle);
return -1;
}
else
{
int http_errnum;
http_errnum = (errno == ENOENT)? 404:
(errno == EACCES)? 403: 500;
x = qse_httpd_entask_error (
httpd, client, x, http_errnum,
&dir->version, dir->keepalive);
QSE_CLOSEDIR (handle);
return (x == QSE_NULL)? -1: 0;
}
}
else
{
@ -481,14 +513,19 @@ qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
const qse_mchar_t* css,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_dir_t data;
if (css == QSE_NULL) css = QSE_MT("");
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path.ptr = path;
data.path.len = qse_mbslen(data.path.ptr);
data.css.ptr = css;
data.css.len = qse_mbslen(data.css.ptr);
data.qpath.ptr = qse_htre_getqpath(req);
data.qpath.len = qse_mbslen(data.qpath.ptr);
data.version = *qse_htre_getversion(req);
@ -500,6 +537,6 @@ qse_httpd_task_t* qse_httpd_entaskdir (
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_dir_t) + data.path.len + 1 + data.qpath.len + 1);
QSE_SIZEOF(task_dir_t) + data.path.len + 1 + data.css.len + 1 + data.qpath.len + 1);
}

View File

@ -75,6 +75,7 @@
#define SERVER_XTN_CFG_USERNAME QSE_HTTPD_SERVER_XTN_CFG_USERNAME
#define SERVER_XTN_CFG_PASSWORD QSE_HTTPD_SERVER_XTN_CFG_PASSWORD
#define SERVER_XTN_CFG_BASICAUTH QSE_HTTPD_SERVER_XTN_CFG_BASICAUTH
#define SERVER_XTN_CFG_DIRCSS QSE_HTTPD_SERVER_XTN_CFG_DIRCSS
/* ------------------------------------------------------------------- */
#if defined(_WIN32)
@ -1042,20 +1043,14 @@ static int mux_writable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec)
/* ------------------------------------------------------------------- */
static int file_executable (qse_httpd_t* httpd, const qse_mchar_t* path)
{
if (access (path, X_OK) == -1)
return (errno == EACCES)? 0 /*no*/: -1 /*error*/;
return 1; /* yes */
}
static int file_stat (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_stat_t* hst)
static int stat_file (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_stat_t* hst, int regonly)
{
struct stat st;
/* TODO: lstat? or stat? */
if (stat (path, &st) <= -1)
if (QSE_STAT (path, &st) <= -1)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1;
@ -1063,7 +1058,7 @@ static int file_stat (
/* stating for a file. it should be a regular file.
* i don't allow other file types. */
if (!S_ISREG(st.st_mode))
if (regonly && !S_ISREG(st.st_mode))
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_EACCES);
return -1;
@ -1071,6 +1066,7 @@ static int file_stat (
QSE_MEMSET (hst, 0, QSE_SIZEOF(*hst));
hst->isdir = S_ISDIR(st.st_mode);
hst->dev = st.st_dev;
hst->ino = st.st_ino;
hst->size = st.st_size;
@ -1085,6 +1081,21 @@ static int file_stat (
return 0;
}
/* ------------------------------------------------------------------- */
static int file_executable (qse_httpd_t* httpd, const qse_mchar_t* path)
{
if (access (path, X_OK) == -1)
return (errno == EACCES)? 0 /*no*/: -1 /*error*/;
return 1; /* yes */
}
static int file_stat (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_httpd_stat_t* hst)
{
return stat_file (httpd, path, hst, 1);
}
static int file_ropen (
qse_httpd_t* httpd, const qse_mchar_t* path, qse_ubi_t* handle)
{
@ -1156,6 +1167,93 @@ static qse_ssize_t file_write (
return QSE_WRITE (handle.i, buf, len);
}
/* ------------------------------------------------------------------- */
typedef struct dir_t dir_t;
struct dir_t
{
qse_mchar_t* path;
qse_dir_t* dp;
};
static int dir_open (qse_httpd_t* httpd, const qse_mchar_t* path, qse_ubi_t* handle)
{
dir_t* d;
d = QSE_MMGR_ALLOC (httpd->mmgr, QSE_SIZEOF(*d));
if (d == QSE_NULL)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
return -1;
}
d->path = qse_mbsdup (path, httpd->mmgr);
if (d->path == QSE_NULL)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
QSE_MMGR_FREE (httpd->mmgr, d);
return -1;
}
d->dp = QSE_OPENDIR (path);
if (d->dp == QSE_NULL)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
QSE_MMGR_FREE (httpd->mmgr, d->path);
QSE_MMGR_FREE (httpd->mmgr, d);
return -1;
}
handle->ptr = d;
return 0;
}
static void dir_close (qse_httpd_t* httpd, qse_ubi_t handle)
{
dir_t* d;
d = (dir_t*)handle.ptr;
QSE_CLOSEDIR (d->dp);
QSE_MMGR_FREE (httpd->mmgr, d->path);
QSE_MMGR_FREE (httpd->mmgr, d);
}
static int dir_read (qse_httpd_t* httpd, qse_ubi_t handle, qse_httpd_dirent_t* dirent)
{
dir_t* d;
qse_dirent_t* de;
qse_mchar_t* fpath;
int n;
d = (dir_t*)handle.ptr;
errno = 0;
de = QSE_READDIR (d->dp);
if (de == QSE_NULL)
{
if (errno == 0) return 0;
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
return -1;
}
/* i assume that d->path ends with a slash */
fpath = qse_mbsdup2 (d->path, de->d_name, httpd->mmgr);
if (fpath == QSE_NULL)
{
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOMEM);
return -1;
}
n = stat_file (httpd, fpath, &dirent->stat, 0);
QSE_MMGR_FREE (httpd->mmgr, fpath);
if (n <= -1) QSE_MEMSET (dirent, 0, QSE_SIZEOF(*dirent));
dirent->name = de->d_name;
return 1;
}
/* ------------------------------------------------------------------- */
static void client_close (
qse_httpd_t* httpd, qse_httpd_client_t* client)
@ -1520,6 +1618,13 @@ static qse_httpd_scb_t httpd_system_callbacks =
file_write
},
/* directory operation */
{ dir_open,
dir_close,
dir_read
},
/* client connection */
{ client_close,
client_shutdown,
@ -1749,6 +1854,7 @@ auth_ok:
target->type = QSE_HTTPD_RSRC_DIR;
target->u.dir.path = xpath;
target->u.dir.css = server_xtn->cfg[SERVER_XTN_CFG_DIRCSS].ptr;
}
else
{

View File

@ -390,7 +390,7 @@ qse_httpd_task_t* qse_httpd_entaskrsrc (
case QSE_HTTPD_RSRC_DIR:
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskdir (httpd, client, QSE_NULL, rsrc->u.dir.path, req);
task = qse_httpd_entaskdir (httpd, client, QSE_NULL, rsrc->u.dir.path, rsrc->u.dir.css, req);
break;
case QSE_HTTPD_RSRC_ERROR:

View File

@ -59,12 +59,20 @@ static int httpd_main (int argc, qse_char_t* argv[])
for (i = 1; i < argc; i++)
{
if (qse_httpd_attachserverstd (httpd, argv[i], QSE_NULL, 0) == QSE_NULL)
qse_httpd_server_t* server;
qse_httpd_server_xtn_t* server_xtn;
server = qse_httpd_attachserverstd (httpd, argv[i], QSE_NULL, 0);
if (server == QSE_NULL)
{
qse_fprintf (QSE_STDERR,
QSE_T("Failed to add httpd listener - %s\n"), argv[i]);
goto oops;
}
server_xtn = qse_httpd_getserverxtn (httpd, server);
server_xtn->cfg[QSE_HTTPD_SERVER_XTN_CFG_DIRCSS].ptr = QSE_MT("body { background-color:#d0e4fe; font-size: 0.9em; font-family: Ubuntu,'Trebuchet MS',sans-serif; }");
server_xtn->cfg[QSE_HTTPD_SERVER_XTN_CFG_DIRCSS].len = qse_mbslen(server_xtn->cfg[QSE_HTTPD_SERVER_XTN_CFG_DIRCSS].ptr);
}
g_httpd = httpd;