aboutsummaryrefslogtreecommitdiff
path: root/dpi/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'dpi/file.c')
-rw-r--r--dpi/file.c806
1 files changed, 440 insertions, 366 deletions
diff --git a/dpi/file.c b/dpi/file.c
index 1f8c80ba..235a474d 100644
--- a/dpi/file.c
+++ b/dpi/file.c
@@ -15,14 +15,13 @@
* With new HTML layout.
*/
-#include <pthread.h>
-
#include <ctype.h> /* for tolower */
#include <errno.h> /* for errno */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -32,6 +31,7 @@
#include <fcntl.h>
#include <time.h>
#include <signal.h>
+#include <netinet/in.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
@@ -42,11 +42,32 @@
*/
#define _MSG(...)
#define MSG(...) printf("[file dpi]: " __VA_ARGS__)
+#define _MSG_RAW(...)
+#define MSG_RAW(...) printf(__VA_ARGS__)
#define MAXNAMESIZE 30
#define HIDE_DOTFILES TRUE
+/*
+ * Communication flags
+ */
+#define FILE_AUTH_OK 1 /* Authentication done */
+#define FILE_READ 2 /* Waiting data */
+#define FILE_WRITE 4 /* Sending data */
+#define FILE_DONE 8 /* Operation done */
+#define FILE_ERR 16 /* Operation error */
+
+
+typedef enum {
+ st_start,
+ st_dpip,
+ st_http,
+ st_content,
+ st_done,
+ st_err
+} FileState;
+
typedef struct {
char *full_path;
const char *filename;
@@ -62,34 +83,32 @@ typedef struct {
typedef struct {
Dsh *sh;
- int status;
+ char *orig_url;
+ char *filename;
+ int file_fd;
+ off_t file_sz;
+ DilloDir *d_dir;
+ FileState state;
+ int err_code;
+ int flags;
int old_style;
- pthread_t thrID;
- int done;
} ClientInfo;
/*
* Forward references
*/
static const char *File_content_type(const char *filename);
-static int File_get_file(ClientInfo *Client,
- const char *filename,
- struct stat *sb,
- const char *orig_url);
-static int File_get_dir(ClientInfo *Client,
- const char *DirName,
- const char *orig_url);
/*
* Global variables
*/
-static volatile int DPIBYE = 0;
-static volatile int ThreadRunning = 0;
+static int DPIBYE = 0;
static int OLD_STYLE = 0;
/* A list for the clients we are serving */
static Dlist *Clients;
-/* a mutex for operations on clients */
-static pthread_mutex_t ClMut;
+/* Set of filedescriptors we're working on */
+fd_set read_set, write_set;
+
/*
* Close a file descriptor, but handling EINTR
@@ -250,6 +269,8 @@ static void File_dillodir_free(DilloDir *Ddir)
int i;
FileInfo *finfo;
+ dReturn_if (Ddir == NULL);
+
for (i = 0; i < dList_length(Ddir->flist); ++i) {
finfo = dList_nth_data(Ddir->flist, i);
dFree(finfo->full_path);
@@ -264,7 +285,7 @@ static void File_dillodir_free(DilloDir *Ddir)
/*
* Output the string for parent directory
*/
-static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
+static void File_print_parent_dir(ClientInfo *client, const char *dirname)
{
if (strcmp(dirname, "/") != 0) { /* Not the root dir */
char *p, *parent, *HUparent, *Uparent;
@@ -278,7 +299,7 @@ static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
Uparent = Escape_uri_str(parent, NULL);
HUparent = Escape_html_str(Uparent);
- a_Dpip_dsh_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<a href='file:%s'>Parent directory</a>", HUparent);
dFree(HUparent);
dFree(Uparent);
@@ -289,20 +310,20 @@ static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
/*
* Given a timestamp, output an HTML-formatted date string.
*/
-static void File_print_mtime(ClientInfo *Client, time_t mtime)
+static void File_print_mtime(ClientInfo *client, time_t mtime)
{
char *ds = ctime(&mtime);
/* Month, day and {hour or year} */
- if (Client->old_style) {
- a_Dpip_dsh_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
+ if (client->old_style) {
+ a_Dpip_dsh_printf(client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
if (time(NULL) - mtime > 15811200) {
- a_Dpip_dsh_printf(Client->sh, 0, " %.4s", ds + 20);
+ a_Dpip_dsh_printf(client->sh, 0, " %.4s", ds + 20);
} else {
- a_Dpip_dsh_printf(Client->sh, 0, " %.5s", ds + 11);
+ a_Dpip_dsh_printf(client->sh, 0, " %.5s", ds + 11);
}
} else {
- a_Dpip_dsh_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<td>%.3s&nbsp;%.2s&nbsp;%.5s", ds + 4, ds + 8,
/* (more than 6 months old) ? year : hour; */
(time(NULL) - mtime > 15811200) ? ds + 20 : ds + 11);
@@ -312,7 +333,7 @@ static void File_print_mtime(ClientInfo *Client, time_t mtime)
/*
* Return a HTML-line from file info.
*/
-static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
+static void File_info2html(ClientInfo *client, FileInfo *finfo, int n)
{
int size;
char *sizeunits;
@@ -355,10 +376,10 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
HUref = Escape_html_str(Uref);
Hname = Escape_html_str(name);
- if (Client->old_style) {
+ if (client->old_style) {
char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..";
int ndots = MAXNAMESIZE - strlen(name);
- a_Dpip_dsh_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"%s<a href='%s'>%s</a>"
" %s"
" %-11s%4d %-5s",
@@ -367,15 +388,15 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
filecont, size, sizeunits);
} else {
- a_Dpip_dsh_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<tr align=center %s><td>%s<td align=left><a href='%s'>%s</a>"
"<td>%s<td>%d&nbsp;%s",
(n & 1) ? "bgcolor=#dcdcdc" : "",
S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname,
filecont, size, sizeunits);
}
- File_print_mtime(Client, finfo->mtime);
- a_Dpip_dsh_write_str(Client->sh, 0, "\n");
+ File_print_mtime(client, finfo->mtime);
+ a_Dpip_dsh_write_str(client->sh, 0, "\n");
dFree(Hname);
dFree(HUref);
@@ -383,81 +404,92 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
}
/*
- * Read a local directory and translate it to html.
+ * Send the HTML directory page in HTTP.
*/
-static void File_transfer_dir(ClientInfo *Client,
- DilloDir *Ddir, const char *orig_url)
+static void File_send_dir(ClientInfo *client)
{
int n;
char *d_cmd, *Hdirname, *Udirname, *HUdirname;
-
- /* Send DPI header */
- d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
- a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
-
- /* Send page title */
- Udirname = Escape_uri_str(Ddir->dirname, NULL);
- HUdirname = Escape_html_str(Udirname);
- Hdirname = Escape_html_str(Ddir->dirname);
-
- a_Dpip_dsh_printf(Client->sh, 0,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: text/html\r\n"
- "\r\n"
- "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
- "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
- " <TITLE>file:%s</TITLE>\n</HEAD>\n"
- "<BODY><H1>Directory listing of %s</H1>\n",
- HUdirname, Hdirname, Hdirname);
- dFree(Hdirname);
- dFree(HUdirname);
- dFree(Udirname);
-
- if (Client->old_style) {
- a_Dpip_dsh_write_str(Client->sh, 0, "<pre>\n");
- }
-
- /* Output the parent directory */
- File_print_parent_dir(Client, Ddir->dirname);
-
- /* HTML style toggle */
- a_Dpip_dsh_write_str(Client->sh, 0,
- "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
-
- if (dList_length(Ddir->flist)) {
- if (Client->old_style) {
- a_Dpip_dsh_write_str(Client->sh, 0, "\n\n");
+ DilloDir *Ddir = client->d_dir;
+
+ if (client->state == st_start) {
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+ dFree(d_cmd);
+ client->state = st_dpip;
+
+ } else if (client->state == st_dpip) {
+ /* send HTTP header and HTML top part */
+
+ /* Send page title */
+ Udirname = Escape_uri_str(Ddir->dirname, NULL);
+ HUdirname = Escape_html_str(Udirname);
+ Hdirname = Escape_html_str(Ddir->dirname);
+
+ a_Dpip_dsh_printf(client->sh, 0,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
+ "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
+ " <TITLE>file:%s</TITLE>\n</HEAD>\n"
+ "<BODY><H1>Directory listing of %s</H1>\n",
+ HUdirname, Hdirname, Hdirname);
+ dFree(Hdirname);
+ dFree(HUdirname);
+ dFree(Udirname);
+
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "<pre>\n");
+ }
+
+ /* Output the parent directory */
+ File_print_parent_dir(client, Ddir->dirname);
+
+ /* HTML style toggle */
+ a_Dpip_dsh_write_str(client->sh, 0,
+ "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
+
+ if (dList_length(Ddir->flist)) {
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "\n\n");
+ } else {
+ a_Dpip_dsh_write_str(client->sh, 0,
+ "<br><br>\n"
+ "<table border=0 cellpadding=1 cellspacing=0"
+ " bgcolor=#E0E0E0 width=100%>\n"
+ "<tr align=center>\n"
+ "<td>\n"
+ "<td width=60%><b>Filename</b>"
+ "<td><b>Type</b>"
+ "<td><b>Size</b>"
+ "<td><b>Modified&nbsp;at</b>\n");
+ }
} else {
- a_Dpip_dsh_write_str(Client->sh, 0,
- "<br><br>\n"
- "<table border=0 cellpadding=1 cellspacing=0"
- " bgcolor=#E0E0E0 width=100%>\n"
- "<tr align=center>\n"
- "<td>\n"
- "<td width=60%><b>Filename</b>"
- "<td><b>Type</b>"
- "<td><b>Size</b>"
- "<td><b>Modified&nbsp;at</b>\n");
+ a_Dpip_dsh_write_str(client->sh, 0, "<br><br>Directory is empty...");
}
- } else {
- a_Dpip_dsh_write_str(Client->sh, 0, "<br><br>Directory is empty...");
- }
+ client->state = st_http;
- /* Output entries */
- for (n = 0; n < dList_length(Ddir->flist); ++n) {
- File_info2html(Client, dList_nth_data(Ddir->flist,n), n+1);
- }
-
- if (dList_length(Ddir->flist)) {
- if (Client->old_style) {
- a_Dpip_dsh_write_str(Client->sh, 0, "</pre>\n");
- } else {
- a_Dpip_dsh_write_str(Client->sh, 0, "</table>\n");
+ } else if (client->state == st_http) {
+ /* send directories as HTML contents */
+ for (n = 0; n < dList_length(Ddir->flist); ++n) {
+ File_info2html(client, dList_nth_data(Ddir->flist,n), n+1);
+ }
+
+ if (dList_length(Ddir->flist)) {
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "</pre>\n");
+ } else {
+ a_Dpip_dsh_write_str(client->sh, 0, "</table>\n");
+ }
}
+
+ a_Dpip_dsh_write_str(client->sh, 1, "</BODY></HTML>\n");
+ client->state = st_content;
+ client->flags |= FILE_DONE;
}
-
- a_Dpip_dsh_write_str(Client->sh, 0, "</BODY></HTML>\n");
}
/*
@@ -522,70 +554,60 @@ static const char *File_content_type(const char *filename)
/*
* Send an error page
*/
-static void File_send_error_page(ClientInfo *Client, int res,
- const char *orig_url)
+static void File_prepare_send_error_page(ClientInfo *client, int res,
+ const char *orig_url)
+{
+ client->state = st_err;
+ client->err_code = res;
+ client->orig_url = dStrdup(orig_url);
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+}
+
+/*
+ * Send an error page
+ */
+static void File_send_error_page(ClientInfo *client)
{
const char *status;
- const char *body;
char *d_cmd;
+ Dstr *body = dStr_sized_new(128);
- switch (res) {
- case EACCES:
+ if (client->err_code == EACCES) {
status = "403 Forbidden";
- break;
- case ENOENT:
+ } else if (client->err_code == ENOENT) {
status = "404 Not Found";
- break;
- default:
+ } else {
/* good enough */
status = "500 Internal Server Error";
}
-
- body = status; /* it's simple */
+ dStr_append(body, status);
+ dStr_append(body, "\n");
+ dStr_append(body, dStrerror(client->err_code));
/* Send DPI command */
- d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
- a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 0, d_cmd);
dFree(d_cmd);
- a_Dpip_dsh_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"HTTP/1.1 %s\r\n"
"Content-Type: text/plain\r\n"
- "Content-Length: %ld\r\n"
+ "Content-Length: %d\r\n"
"\r\n"
"%s",
- status, strlen(body), body);
-}
-
-/*
- * Try to stat the file and determine if it's readable.
- */
-static void File_get(ClientInfo *Client, const char *filename,
- const char *orig_url)
-{
- int res;
- struct stat sb;
+ status, body->len, body->str);
+ dStr_free(body, TRUE);
- if (stat(filename, &sb) != 0) {
- /* stat failed, prepare a file-not-found error. */
- res = ENOENT;
- } else if (S_ISDIR(sb.st_mode)) {
- /* set up for reading directory */
- res = File_get_dir(Client, filename, orig_url);
- } else {
- /* set up for reading a file */
- res = File_get_file(Client, filename, &sb, orig_url);
- }
- if (res != 0) {
- File_send_error_page(Client, res, orig_url);
- }
+ client->flags |= FILE_DONE;
}
/*
- *
+ * Scan the directory, sort and prepare to send it enclosed in HTTP.
*/
-static int File_get_dir(ClientInfo *Client,
- const char *DirName, const char *orig_url)
+static int File_prepare_send_dir(ClientInfo *client,
+ const char *DirName, const char *orig_url)
{
Dstr *ds_dirname;
DilloDir *Ddir;
@@ -599,90 +621,153 @@ static int File_get_dir(ClientInfo *Client,
Ddir = File_dillodir_new(ds_dirname->str);
dStr_free(ds_dirname, TRUE);
if (Ddir) {
- File_transfer_dir(Client, Ddir, orig_url);
- File_dillodir_free(Ddir);
+ /* looks ok, set things accordingly */
+ client->orig_url = dStrdup(orig_url);
+ client->d_dir = Ddir;
+ client->state = st_start;
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
return 0;
} else
return EACCES;
}
/*
- * Send HTTP headers and then send the file itself.
+ * Prepare to send HTTP headers and then the file itself.
+ */
+static int File_prepare_send_file(ClientInfo *client,
+ const char *filename,
+ const char *orig_url)
+{
+ int fd, res = -1;
+ struct stat sb;
+
+ if (stat(filename, &sb) != 0) {
+ /* prepare a file-not-found error */
+ res = ENOENT;
+ } else if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) {
+ /* prepare an error message */
+ res = errno;
+ } else {
+ /* looks ok, set things accordingly */
+ client->file_fd = fd;
+ client->file_sz = sb.st_size;
+ client->d_dir = NULL;
+ client->state = st_start;
+ client->filename = dStrdup(filename);
+ client->orig_url = dStrdup(orig_url);
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+ res = 0;
+ }
+ return res;
+}
+
+/*
+ * Try to stat the file and determine if it's readable.
+ */
+static void File_get(ClientInfo *client, const char *filename,
+ const char *orig_url)
+{
+ int res;
+ struct stat sb;
+
+ if (stat(filename, &sb) != 0) {
+ /* stat failed, prepare a file-not-found error. */
+ res = ENOENT;
+ } else if (S_ISDIR(sb.st_mode)) {
+ /* set up for reading directory */
+ res = File_prepare_send_dir(client, filename, orig_url);
+ } else {
+ /* set up for reading a file */
+ res = File_prepare_send_file(client, filename, orig_url);
+ }
+ if (res != 0) {
+ File_prepare_send_error_page(client, res, orig_url);
+ }
+}
+
+/*
+ * Send HTTP headers and then the file itself.
*/
-static int File_get_file(ClientInfo *Client,
- const char *filename,
- struct stat *sb,
- const char *orig_url)
+static int File_send_file(ClientInfo *client)
{
#define LBUF 16*1024
const char *ct;
const char *unknown_type = "application/octet-stream";
char buf[LBUF], *d_cmd, *name;
- int fd, st, namelen;
+ int st, namelen;
bool_t gzipped = FALSE;
- if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
- return errno;
-
- /* Send DPI command */
- d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
- a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
-
- /* Check for gzipped file */
- namelen = strlen(filename);
- if (namelen > 3 && !dStrcasecmp(filename + namelen - 3, ".gz")) {
- gzipped = TRUE;
- namelen -= 3;
- }
-
- /* Content-Type info is based on filename extension (with ".gz" removed).
- * If there's no known extension, perform data sniffing.
- * If this doesn't lead to a conclusion, use "application/octet-stream".
- */
- name = dStrndup(filename, namelen);
- if (!(ct = File_content_type(name)))
- ct = unknown_type;
- dFree(name);
-
- /* Send HTTP headers */
- a_Dpip_dsh_write_str(Client->sh, 0, "HTTP/1.1 200 OK\r\n");
- if (gzipped) {
- a_Dpip_dsh_write_str(Client->sh, 0, "Content-Encoding: gzip\r\n");
- }
- if (!gzipped || strcmp(ct, unknown_type)) {
- a_Dpip_dsh_printf(Client->sh, 0, "Content-Type: %s\r\n", ct);
- } else {
- /* If we don't know type for gzipped data, let dillo figure it out. */
- }
- a_Dpip_dsh_printf(Client->sh, 0,
- "Content-Length: %ld\r\n"
- "\r\n",
- sb->st_size);
-
- /* Send body -- raw file contents */
- do {
- if ((st = read(fd, buf, LBUF)) > 0) {
- if (a_Dpip_dsh_write(Client->sh, 0, buf, (size_t)st) != 0)
- break;
- } else if (st < 0) {
- perror("[read]");
- if (errno == EINTR || errno == EAGAIN)
- continue;
+ if (client->state == st_start) {
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+ dFree(d_cmd);
+ client->state = st_dpip;
+
+ } else if (client->state == st_dpip) {
+ /* send HTTP header */
+
+ /* Check for gzipped file */
+ namelen = strlen(client->filename);
+ if (namelen > 3 && !dStrcasecmp(client->filename + namelen - 3, ".gz")) {
+ gzipped = TRUE;
+ namelen -= 3;
+ }
+ /* Content-Type info is based on filename extension (with ".gz" removed).
+ * If there's no known extension, perform data sniffing.
+ * If this doesn't lead to a conclusion, use "application/octet-stream".
+ */
+ name = dStrndup(client->filename, namelen);
+ if (!(ct = File_content_type(name)))
+ ct = unknown_type;
+ dFree(name);
+
+ /* Send HTTP headers */
+ a_Dpip_dsh_write_str(client->sh, 0, "HTTP/1.1 200 OK\r\n");
+ if (gzipped) {
+ a_Dpip_dsh_write_str(client->sh, 0, "Content-Encoding: gzip\r\n");
+ }
+ if (!gzipped || strcmp(ct, unknown_type)) {
+ a_Dpip_dsh_printf(client->sh, 0, "Content-Type: %s\r\n", ct);
+ } else {
+ /* If we don't know type for gzipped data, let dillo figure it out. */
+ }
+ a_Dpip_dsh_printf(client->sh, 1,
+ "Content-Length: %ld\r\n"
+ "\r\n",
+ client->file_sz);
+ client->state = st_http;
+
+ } else if (client->state == st_http) {
+ /* Send body -- raw file contents */
+ do {
+ if ((st = read(client->file_fd, buf, LBUF)) > 0) {
+ if (a_Dpip_dsh_write(client->sh, 0, buf, (size_t)st) != 0)
+ break;
+ } else if (st < 0) {
+ perror("[read]");
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ }
+ } while (st > 0);
+
+ /* TODO: It may be better to send an error report to dillo instead of
+ * calling exit() */
+ if (st == -1) {
+ MSG("ERROR while reading from file \"%s\", error was \"%s\"\n",
+ client->filename, dStrerror(errno));
+ exit(1);
+ } else if (st == 0) {
+ client->state = st_content;
+ File_close(client->file_fd);
+ client->flags |= FILE_DONE;
}
- } while (st > 0);
-
- /* TODO: It may be better to send an error report to dillo instead of
- * calling exit() */
- if (st == -1) {
- MSG("ERROR while reading from file \"%s\", error was \"%s\"\n",
- filename, dStrerror(errno));
- exit(1);
}
- File_close(fd);
-
return 0;
}
@@ -714,6 +799,8 @@ static char *File_normalize_path(const char *orig)
{
char *str = (char *) orig, *basename = NULL, *ret = NULL, *p;
+ dReturn_val_if (orig == NULL, ret);
+
/* Make sure the string starts with "file:/" */
if (strncmp(str, "file:/", 5) != 0)
return ret;
@@ -757,13 +844,13 @@ static char *File_normalize_path(const char *orig)
/*
* Set the style flag and ask for a reload, so it shows immediately.
*/
-static void File_toggle_html_style(ClientInfo *Client)
+static void File_toggle_html_style(ClientInfo *client)
{
char *d_cmd;
OLD_STYLE = !OLD_STYLE;
d_cmd = a_Dpip_build_cmd("cmd=%s", "reload_request");
- a_Dpip_dsh_write_str(Client->sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
dFree(d_cmd);
}
@@ -783,182 +870,180 @@ static void termination_handler(int signum)
*/
static ClientInfo *File_add_client(int sock_fd)
{
- ClientInfo *NewClient;
-
- NewClient = dNew(ClientInfo, 1);
- NewClient->sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
- NewClient->status = 0;
- NewClient->done = 0;
- NewClient->old_style = OLD_STYLE;
- pthread_mutex_lock(&ClMut);
- dList_append(Clients, NewClient);
- pthread_mutex_unlock(&ClMut);
- return NewClient;
-}
-
-/*
- * Get client record by number
- */
-static void *File_get_client_n(uint_t n)
-{
- void *client;
-
- pthread_mutex_lock(&ClMut);
- client = dList_nth_data(Clients, n);
- pthread_mutex_unlock(&ClMut);
-
- return client;
+ ClientInfo *new_client;
+
+ new_client = dNew(ClientInfo, 1);
+ new_client->sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+ new_client->orig_url = NULL;
+ new_client->filename = NULL;
+ new_client->file_fd = -1;
+ new_client->file_sz = 0;
+ new_client->d_dir = NULL;
+ new_client->state = 0;
+ new_client->err_code = 0;
+ new_client->flags = FILE_READ;
+ new_client->old_style = OLD_STYLE;
+ dList_append(Clients, new_client);
+ return new_client;
}
/*
* Remove a client from the list.
*/
-static void File_remove_client_n(uint_t n)
+static void File_remove_client(ClientInfo *client)
{
- ClientInfo *Client;
-
- pthread_mutex_lock(&ClMut);
- Client = dList_nth_data(Clients, n);
- dList_remove(Clients, (void *)Client);
- pthread_mutex_unlock(&ClMut);
+ dList_remove(Clients, (void *)client);
_MSG("Closing Socket Handler\n");
- a_Dpip_dsh_close(Client->sh);
- a_Dpip_dsh_free(Client->sh);
- dFree(Client);
-}
-
-/*
- * Return the number of clients.
- */
-static int File_num_clients(void)
-{
- uint_t n;
-
- pthread_mutex_lock(&ClMut);
- n = dList_length(Clients);
- pthread_mutex_unlock(&ClMut);
+ a_Dpip_dsh_close(client->sh);
+ a_Dpip_dsh_free(client->sh);
+ dFree(client->orig_url);
+ dFree(client->filename);
+ File_dillodir_free(client->d_dir);
- return n;
+ dFree(client);
}
/*
* Serve this client.
- * (this function runs on its own thread)
*/
-static void File_serve_client(void *data)
+static void File_serve_client(void *data, int f_write)
{
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *path;
- ClientInfo *Client = data;
+ ClientInfo *client = data;
int st;
- /* Authenticate our client... */
- do {
- dpip_tag = a_Dpip_dsh_read_token(Client->sh);
- } while (!dpip_tag && Client->sh->status == DPIP_EAGAIN);
- _MSG("auth={%s}\n", dpip_tag);
- st = a_Dpip_check_auth(dpip_tag);
- dFree(dpip_tag);
- _MSG("a_Dpip_check_auth returned %d\n", st);
- dReturn_if (st == -1);
-
- /* Read the dpi command */
- do {
- dpip_tag = a_Dpip_dsh_read_token(Client->sh);
- } while (!dpip_tag && Client->sh->status == DPIP_EAGAIN);
- _MSG("dpip_tag={%s}\n", dpip_tag);
-
- if (dpip_tag) {
- cmd = a_Dpip_get_attr(dpip_tag, "cmd");
- if (cmd) {
- if (strcmp(cmd, "DpiBye") == 0) {
- DPIBYE = 1;
- MSG("file.dpi:: (pid %d): Got DpiBye.\n", (int)getpid());
+ while (1) {
+ MSG("File_serve_client %p, flags=%d\n", client, client->flags);
+ if (client->flags & (FILE_DONE | FILE_ERR))
+ break;
+ if (client->flags & FILE_READ) {
+ dpip_tag = a_Dpip_dsh_read_token(client->sh, 0);
+ MSG("dpip_tag={%s}\n", dpip_tag);
+ if (!dpip_tag)
+ break;
+ }
+
+ if (client->flags & FILE_READ) {
+ if (!(client->flags & FILE_AUTH_OK)) {
+ /* Authenticate our client... */
+ st = a_Dpip_check_auth(dpip_tag);
+ MSG("a_Dpip_check_auth returned %d\n", st);
+ client->flags |= (st == 1) ? FILE_AUTH_OK : FILE_ERR;
} else {
+ /* Get file request */
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
url = a_Dpip_get_attr(dpip_tag, "url");
- if (!url)
- MSG("file.dpi:: Failed to parse 'url'\n");
+ path = File_normalize_path(url);
+ if (cmd) {
+ if (strcmp(cmd, "DpiBye") == 0) {
+ DPIBYE = 1;
+ MSG("(pid %d): Got DpiBye.\n", (int)getpid());
+ client->flags |= FILE_DONE;
+ } else if (url && strcmp(url, "dpi:/file/toggle") == 0) {
+ File_toggle_html_style(client);
+ } else if (path) {
+ File_get(client, path, url);
+ } else {
+ client->flags |= FILE_ERR;
+ MSG("ERROR: URL was %s\n", url);
+ }
+ }
+ dFree(path);
+ dFree(url);
+ dFree(cmd);
+ break;
}
+ dFree(dpip_tag);
+
+ } else if (f_write) {
+ /* send our answer */
+ if (client->state == st_err)
+ File_send_error_page(client);
+ else if (client->d_dir)
+ File_send_dir(client);
+ else
+ File_send_file(client);
+ break;
}
- dFree(cmd);
- }
- dFree(dpip_tag);
-
- if (!DPIBYE && url) {
- _MSG("url = '%s'\n", url);
-
- path = File_normalize_path(url);
- if (path) {
- _MSG("path = '%s'\n", path);
- File_get(Client, path, url);
- } else if (strcmp(url, "dpi:/file/toggle") == 0) {
- File_toggle_html_style(Client);
- } else {
- MSG("ERROR: URL path was %s\n", url);
- }
- dFree(path);
- }
- dFree(url);
+ } /*while*/
- /* flag the the transfer finished */
- Client->done = 1;
+ client->flags |= (client->sh->status & DPIP_ERROR) ? FILE_ERR : 0;
+ client->flags |= (client->sh->status & DPIP_EOF) ? FILE_DONE : 0;
}
/*
* Serve the client queue.
- * (this function runs on its own thread)
*/
-static void *File_serve_clients(void *client)
+static void File_serve_clients()
{
- /* switch to detached state */
- pthread_detach(pthread_self());
-
- while (File_num_clients()) {
- client = File_get_client_n((uint_t)0);
- File_serve_client(client);
- File_remove_client_n((uint_t)0);
+ int i, f_read, f_write;
+ ClientInfo *client;
+
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ f_read = FD_ISSET(client->sh->fd_in, &read_set);
+ f_write = FD_ISSET(client->sh->fd_out, &write_set);
+ if (!f_read && !f_write)
+ continue;
+ File_serve_client(client, f_write);
+ if (client->flags & (FILE_DONE | FILE_ERR)) {
+ File_remove_client(client);
+ --i;
+ }
}
- ThreadRunning = 0;
-
- return NULL;
}
/* --------------------------------------------------------------------------*/
/*
- * Check a fd for activity, with a max timeout.
+ * Check the fd sets for activity, with a max timeout.
* return value: 0 if timeout, 1 if input available, -1 if error.
*/
-static int File_check_fd(int filedes, uint_t seconds)
+static int File_check_fds(uint_t seconds)
{
- int st;
- fd_set set;
- struct timeval timeout;
-
- /* Initialize the file descriptor set. */
- FD_ZERO (&set);
- FD_SET (filedes, &set);
-
- /* Initialize the timeout data structure. */
- timeout.tv_sec = seconds;
- timeout.tv_usec = 0;
+ int i, st;
+ ClientInfo *client;
+ struct timeval timeout;
+
+ /* initialize observed file descriptors */
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_SET (STDIN_FILENO, &read_set);
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ if (client->flags & FILE_READ)
+ FD_SET (client->sh->fd_in, &read_set);
+ if (client->flags & FILE_WRITE)
+ FD_SET (client->sh->fd_out, &write_set);
+ }
+ MSG("Watching %d fds:", dList_length(Clients) + 1);
- do {
- st = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
- } while (st == -1 && errno == EINTR);
+ /* Initialize the timeout data structure. */
+ timeout.tv_sec = seconds;
+ timeout.tv_usec = 0;
- return st;
+ do {
+ st = select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout);
+ } while (st == -1 && errno == EINTR);
+
+ MSG_RAW(" (%d%s%s)", STDIN_FILENO,
+ FD_ISSET(STDIN_FILENO, &read_set) ? "R" : "",
+ FD_ISSET(STDIN_FILENO, &write_set) ? "W" : "");
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ MSG_RAW(" (%d%s%s)", client->sh->fd_in,
+ FD_ISSET(client->sh->fd_in, &read_set) ? "R" : "",
+ FD_ISSET(client->sh->fd_out, &write_set) ? "W" : "");
+ }
+ MSG_RAW("\n");
+
+ return st;
}
int main(void)
{
- ClientInfo *NewClient;
- struct sockaddr_un spun;
- int temp_sock_descriptor;
+ struct sockaddr_in sin;
socklen_t address_size;
- int c_st, st = 1;
- uint_t i;
+ int tmp_fd, c_st, st = 1;
/* Arrange the cleanup function for abnormal terminations */
if (signal (SIGINT, termination_handler) == SIG_IGN)
@@ -968,57 +1053,46 @@ int main(void)
if (signal (SIGTERM, termination_handler) == SIG_IGN)
signal (SIGTERM, SIG_IGN);
- MSG("(v.1) accepting connections...\n");
+ MSG("(v.2) accepting connections...\n");
- /* initialize mutex */
- pthread_mutex_init(&ClMut, NULL);
+ /* initialize observed file descriptors */
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_SET (STDIN_FILENO, &read_set);
/* initialize Clients list */
Clients = dList_new(512);
/* some OSes may need this... */
- address_size = sizeof(struct sockaddr_un);
+ address_size = sizeof(struct sockaddr_in);
/* start the service loop */
while (!DPIBYE) {
- /* wait for a connection */
+ /* wait for activity */
do {
- c_st = File_check_fd(STDIN_FILENO, 1);
+ c_st = File_check_fds(10);
} while (c_st == 0 && !DPIBYE);
if (c_st < 0) {
- perror("[select]");
+ MSG(" select() %s\n", dStrerror(errno));
break;
}
if (DPIBYE)
break;
- temp_sock_descriptor =
- accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
-
- if (temp_sock_descriptor == -1) {
- perror("[accept]");
- break;
- }
-
- /* Create and initialize a new client */
- NewClient = File_add_client(temp_sock_descriptor);
-
- if (!ThreadRunning) {
- ThreadRunning = 1;
- /* Serve the client from a thread (avoids deadlocks) */
- if (pthread_create(&NewClient->thrID, NULL,
- File_serve_clients, NewClient) != 0) {
- perror("[pthread_create]");
- ThreadRunning = 0;
+ if (FD_ISSET(STDIN_FILENO, &read_set)) {
+ /* accept the incoming connection */
+ tmp_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &address_size);
+ if (tmp_fd == -1) {
+ MSG(" accept() %s\n", dStrerror(errno));
break;
+ } else {
+ /* Create and initialize a new client */
+ File_add_client(tmp_fd);
}
+ continue;
}
- }
- /* TODO: handle a running thread better. */
- for (i = 0; i < 5 && ThreadRunning; ++i) {
- MSG("sleep i=%u", i);
- sleep(i);
+ File_serve_clients();
}
if (DPIBYE)