diff options
Diffstat (limited to 'dpi/file.c')
-rw-r--r-- | dpi/file.c | 806 |
1 files changed, 440 insertions, 366 deletions
@@ -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 %.2s %.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 %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, - " <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, + " <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 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 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) |