Mercurial > dillo_port1.3
view dpi/file.c @ 2084:5b8e5855ab8e
support !important in style attributes
author | Johannes Hofmann <Johannes.Hofmann@gmx.de> |
---|---|
date | Tue, 07 Jun 2011 12:06:52 +0200 |
parents | 0399d0ecb3b9 |
children |
line wrap: on
line source
/* * File: file.c :) * * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ /* * Directory scanning is no longer streamed, but it gets sorted instead! * Directory entries on top, files next. * With new HTML layout. */ #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> #include <sys/time.h> #include <sys/un.h> #include <dirent.h> #include <fcntl.h> #include <time.h> #include <signal.h> #include <netinet/in.h> #include "../dpip/dpip.h" #include "dpiutil.h" #include "d_size.h" /* * Debugging macros */ #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 = 10, st_dpip, st_http, st_content, st_done, st_err } FileState; typedef struct { char *full_path; const char *filename; off_t size; mode_t mode; time_t mtime; } FileInfo; typedef struct { char *dirname; Dlist *flist; /* List of files and subdirectories (for sorting) */ } DilloDir; typedef struct { Dsh *sh; 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; } ClientInfo; /* * Forward references */ static const char *File_content_type(const char *filename); /* * Global variables */ static int DPIBYE = 0; static int OLD_STYLE = 0; /* A list for the clients we are serving */ static Dlist *Clients; /* Set of filedescriptors we're working on */ fd_set read_set, write_set; /* * Close a file descriptor, but handling EINTR */ static void File_close(int fd) { while (fd >= 0 && close(fd) < 0 && errno == EINTR) ; } /* * Detects 'Content-Type' when the server does not supply one. * It uses the magic(5) logic from file(1). Currently, it * only checks the few mime types that Dillo supports. * * 'Data' is a pointer to the first bytes of the raw data. * (this is based on a_Misc_get_content_type_from_data()) */ static const char *File_get_content_type_from_data(void *Data, size_t Size) { static const char *Types[] = { "application/octet-stream", "text/html", "text/plain", "image/gif", "image/png", "image/jpeg", }; int Type = 0; char *p = Data; size_t i, non_ascci; _MSG("File_get_content_type_from_data:: Size = %d\n", Size); /* HTML try */ for (i = 0; i < Size && dIsspace(p[i]); ++i); if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) || (Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) || (Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) || (Size - i >= 14 && !dStrncasecmp(p+i, "<!doctype html", 14)) || /* this line is workaround for FTP through the Squid proxy */ (Size - i >= 17 && !dStrncasecmp(p+i, "<!-- HTML listing", 17))) { Type = 1; /* Images */ } else if (Size >= 4 && !dStrncasecmp(p, "GIF8", 4)) { Type = 3; } else if (Size >= 4 && !dStrncasecmp(p, "\x89PNG", 4)) { Type = 4; } else if (Size >= 2 && !dStrncasecmp(p, "\xff\xd8", 2)) { /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking * at the character representation should be machine independent. */ Type = 5; /* Text */ } else { /* We'll assume "text/plain" if the set of chars above 127 is <= 10 * in a 256-bytes sample. Better heuristics are welcomed! :-) */ non_ascci = 0; Size = MIN (Size, 256); for (i = 0; i < Size; i++) if ((uchar_t) p[i] > 127) ++non_ascci; if (Size == 256) { Type = (non_ascci > 10) ? 0 : 2; } else { Type = (non_ascci > 0) ? 0 : 2; } } return (Types[Type]); } /* * Compare two FileInfo pointers * This function is used for sorting directories */ static int File_comp(const FileInfo *f1, const FileInfo *f2) { if (S_ISDIR(f1->mode)) { if (S_ISDIR(f2->mode)) { return strcmp(f1->filename, f2->filename); } else { return -1; } } else { if (S_ISDIR(f2->mode)) { return 1; } else { return strcmp(f1->filename, f2->filename); } } } /* * Allocate a DilloDir structure, set safe values in it and sort the entries. */ static DilloDir *File_dillodir_new(char *dirname) { struct stat sb; struct dirent *de; DIR *dir; DilloDir *Ddir; FileInfo *finfo; char *fname; int dirname_len; if (!(dir = opendir(dirname))) return NULL; Ddir = dNew(DilloDir, 1); Ddir->dirname = dStrdup(dirname); Ddir->flist = dList_new(512); dirname_len = strlen(Ddir->dirname); /* Scan every name and sort them */ while ((de = readdir(dir)) != 0) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; /* skip "." and ".." */ if (HIDE_DOTFILES) { /* Don't add hidden files or backup files to the list */ if (de->d_name[0] == '.' || de->d_name[0] == '#' || (de->d_name[0] != '\0' && de->d_name[strlen(de->d_name) - 1] == '~')) continue; } fname = dStrconcat(Ddir->dirname, de->d_name, NULL); if (stat(fname, &sb) == -1) { dFree(fname); continue; /* ignore files we can't stat */ } finfo = dNew(FileInfo, 1); finfo->full_path = fname; finfo->filename = fname + dirname_len; finfo->size = sb.st_size; finfo->mode = sb.st_mode; finfo->mtime = sb.st_mtime; dList_append(Ddir->flist, finfo); } closedir(dir); /* sort the entries */ dList_sort(Ddir->flist, (dCompareFunc)File_comp); return Ddir; } /* * Deallocate a DilloDir structure. */ 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); dFree(finfo); } dList_free(Ddir->flist); dFree(Ddir->dirname); dFree(Ddir); } /* * Output the string for parent directory */ static void File_print_parent_dir(ClientInfo *client, const char *dirname) { if (strcmp(dirname, "/") != 0) { /* Not the root dir */ char *p, *parent, *HUparent, *Uparent; parent = dStrdup(dirname); /* cut trailing slash */ parent[strlen(parent) - 1] = '\0'; /* make 'parent' have the parent dir path */ if ((p = strrchr(parent, '/'))) *(p + 1) = '\0'; Uparent = Escape_uri_str(parent, NULL); HUparent = Escape_html_str(Uparent); a_Dpip_dsh_printf(client->sh, 0, "<a href='file:%s'>Parent directory</a>", HUparent); dFree(HUparent); dFree(Uparent); dFree(parent); } } /* * Given a timestamp, output an HTML-formatted date string. */ 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 (time(NULL) - mtime > 15811200) { a_Dpip_dsh_printf(client->sh, 0, " %.4s", ds + 20); } else { a_Dpip_dsh_printf(client->sh, 0, " %.5s", ds + 11); } } else { 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); } } /* * Return a HTML-line from file info. */ static void File_info2html(ClientInfo *client, FileInfo *finfo, int n) { int size; char *sizeunits; char namebuf[MAXNAMESIZE + 1]; char *Uref, *HUref, *Hname; const char *ref, *filecont, *name = finfo->filename; if (finfo->size <= 9999) { size = finfo->size; sizeunits = "bytes"; } else if (finfo->size / 1024 <= 9999) { size = finfo->size / 1024 + (finfo->size % 1024 >= 1024 / 2); sizeunits = "Kb"; } else { size = finfo->size / 1048576 + (finfo->size % 1048576 >= 1048576 / 2); sizeunits = "Mb"; } /* we could note if it's a symlink... */ if S_ISDIR (finfo->mode) { filecont = "Directory"; } else if (finfo->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { filecont = "Executable"; } else { filecont = File_content_type(finfo->full_path); if (!filecont || !strcmp(filecont, "application/octet-stream")) filecont = "unknown"; } ref = name; if (strlen(name) > MAXNAMESIZE) { memcpy(namebuf, name, MAXNAMESIZE - 3); strcpy(namebuf + (MAXNAMESIZE - 3), "..."); name = namebuf; } /* escape problematic filenames */ Uref = Escape_uri_str(ref, NULL); HUref = Escape_html_str(Uref); Hname = Escape_html_str(name); if (client->old_style) { char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .."; int ndots = MAXNAMESIZE - strlen(name); a_Dpip_dsh_printf(client->sh, 0, "%s<a href='%s'>%s</a>" " %s" " %-11s%4d %-5s", S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname, dots + 50 - (ndots > 0 ? ndots : 0), filecont, size, sizeunits); } else { 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"); dFree(Hname); dFree(HUref); dFree(Uref); } /* * Send the HTML directory page in HTTP. */ static void File_send_dir(ClientInfo *client) { int n; char *d_cmd, *Hdirname, *Udirname, *HUdirname; 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>Directory is empty..."); } client->state = st_http; } 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 (client->old_style) { a_Dpip_dsh_write_str(client->sh, 0, "</pre>\n"); } else if (dList_length(Ddir->flist)) { 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; } } /* * Return a content type based on the extension of the filename. */ static const char *File_ext(const char *filename) { char *e; if (!(e = strrchr(filename, '.'))) return NULL; e++; if (!dStrcasecmp(e, "gif")) { return "image/gif"; } else if (!dStrcasecmp(e, "jpg") || !dStrcasecmp(e, "jpeg")) { return "image/jpeg"; } else if (!dStrcasecmp(e, "png")) { return "image/png"; } else if (!dStrcasecmp(e, "html") || !dStrcasecmp(e, "htm") || !dStrcasecmp(e, "shtml")) { return "text/html"; } else if (!dStrcasecmp(e, "txt")) { return "text/plain"; } else { return NULL; } } /* * Based on the extension, return the content_type for the file. * (if there's no extension, analyze the data and try to figure it out) */ static const char *File_content_type(const char *filename) { int fd; struct stat sb; const char *ct; char buf[256]; ssize_t buf_size; if (!(ct = File_ext(filename))) { /* everything failed, let's analyze the data... */ if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) != -1) { if ((buf_size = read(fd, buf, 256)) == 256 ) { ct = File_get_content_type_from_data(buf, (size_t)buf_size); } else if (stat(filename, &sb) != -1 && buf_size > 0 && buf_size == sb.st_size) { ct = File_get_content_type_from_data(buf, (size_t)buf_size); } File_close(fd); } } _MSG("File_content_type: name=%s ct=%s\n", filename, ct); return ct; } /* * Send an error page */ 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; char *d_cmd; Dstr *body = dStr_sized_new(128); if (client->err_code == EACCES) { status = "403 Forbidden"; } else if (client->err_code == ENOENT) { status = "404 Not Found"; } else { /* good enough */ status = "500 Internal Server Error"; } 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", client->orig_url); a_Dpip_dsh_write_str(client->sh, 0, d_cmd); dFree(d_cmd); a_Dpip_dsh_printf(client->sh, 0, "HTTP/1.1 %s\r\n" "Content-Type: text/plain\r\n" "Content-Length: %d\r\n" "\r\n" "%s", status, body->len, body->str); dStr_free(body, TRUE); client->flags |= FILE_DONE; } /* * Scan the directory, sort and prepare to send it enclosed in HTTP. */ static int File_prepare_send_dir(ClientInfo *client, const char *DirName, const char *orig_url) { Dstr *ds_dirname; DilloDir *Ddir; /* Let's make sure this directory url has a trailing slash */ ds_dirname = dStr_new(DirName); if (ds_dirname->str[ds_dirname->len - 1] != '/') dStr_append(ds_dirname, "/"); /* Let's get a structure ready for transfer */ Ddir = File_dillodir_new(ds_dirname->str); dStr_free(ds_dirname, TRUE); if (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; } /* * 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_send_file(ClientInfo *client) { //#define LBUF 1 #define LBUF 16*1024 const char *ct; const char *unknown_type = "application/octet-stream"; char buf[LBUF], *d_cmd, *name; int st, st2, namelen; bool_t gzipped = FALSE; 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 */ if ((st = a_Dpip_dsh_tryflush(client->sh)) < 0) { client->flags |= (st == -3) ? FILE_ERR : 0; } else { /* no pending data, let's send new data */ do { st2 = read(client->file_fd, buf, LBUF); } while (st2 < 0 && errno == EINTR); if (st2 < 0) { MSG("\nERROR while reading from file '%s': %s\n\n", client->filename, dStrerror(errno)); client->flags |= FILE_ERR; } else if (st2 == 0) { client->state = st_content; client->flags |= FILE_DONE; } else { /* ok to write */ st = a_Dpip_dsh_trywrite(client->sh, buf, st2); client->flags |= (st == -3) ? FILE_ERR : 0; } } } return 0; } /* * Given a hex octet (e3, 2F, 20), return the corresponding * character if the octet is valid, and -1 otherwise */ static int File_parse_hex_octet(const char *s) { int hex_value; char *tail, hex[3]; if ((hex[0] = s[0]) && (hex[1] = s[1])) { hex[2] = 0; hex_value = strtol(hex, &tail, 16); if (tail - hex == 2) return hex_value; } return -1; } /* * Make a file URL into a human (and machine) readable path. * The idea is to always have a path that starts with only one slash. * Embedded slashes are ignored. */ 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; str += 5; /* Skip "localhost" */ if (dStrncasecmp(str, "//localhost/", 12) == 0) str += 11; /* Skip packed slashes, and leave just one */ while (str[0] == '/' && str[1] == '/') str++; { int i, val; Dstr *ds = dStr_sized_new(32); dStr_sprintf(ds, "%s%s%s", basename ? basename : "", basename ? "/" : "", str); dFree(basename); /* Parse possible hexadecimal octets in the URI path */ for (i = 0; ds->str[i]; ++i) { if (ds->str[i] == '%' && (val = File_parse_hex_octet(ds->str + i+1)) != -1) { ds->str[i] = val; dStr_erase(ds, i+1, 2); } } /* Remove the fragment if not a part of the filename */ if ((p = strrchr(ds->str, '#')) != NULL && access(ds->str, F_OK) != 0) dStr_truncate(ds, p - ds->str); ret = ds->str; dStr_free(ds, 0); } return ret; } /* * Set the style flag and ask for a reload, so it shows immediately. */ 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); dFree(d_cmd); } /* * Perform any necessary cleanups upon abnormal termination */ static void termination_handler(int signum) { MSG("\nexit(signum), signum=%d\n\n", signum); exit(signum); } /* Client handling ----------------------------------------------------------*/ /* * Add a new client to the list. */ static ClientInfo *File_add_client(int sock_fd) { 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(ClientInfo *client) { dList_remove(Clients, (void *)client); _MSG("Closing Socket Handler\n"); a_Dpip_dsh_close(client->sh); a_Dpip_dsh_free(client->sh); File_close(client->file_fd); dFree(client->orig_url); dFree(client->filename); File_dillodir_free(client->d_dir); dFree(client); } /* * Serve this client. */ static void File_serve_client(void *data, int f_write) { char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *path; ClientInfo *client = data; int st; while (1) { _MSG("File_serve_client %p, flags=%d state=%d\n", client, client->flags, client->state); 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"); 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); dFree(dpip_tag); 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; } } /*while*/ client->flags |= (client->sh->status & DPIP_ERROR) ? FILE_ERR : 0; client->flags |= (client->sh->status & DPIP_EOF) ? FILE_DONE : 0; } /* * Serve the client queue. */ static void File_serve_clients() { 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; } } } /* --------------------------------------------------------------------------*/ /* * 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_fds(uint_t seconds) { 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\n", dList_length(Clients) + 1); /* Initialize the timeout data structure. */ timeout.tv_sec = seconds; timeout.tv_usec = 0; 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) { struct sockaddr_in sin; socklen_t sin_sz; int sock_fd, c_st, st = 1; /* Arrange the cleanup function for abnormal terminations */ if (signal (SIGINT, termination_handler) == SIG_IGN) signal (SIGINT, SIG_IGN); if (signal (SIGHUP, termination_handler) == SIG_IGN) signal (SIGHUP, SIG_IGN); if (signal (SIGTERM, termination_handler) == SIG_IGN) signal (SIGTERM, SIG_IGN); MSG("(v.2) accepting connections...\n"); //sleep(20); /* initialize observed file descriptors */ FD_ZERO (&read_set); FD_ZERO (&write_set); FD_SET (STDIN_FILENO, &read_set); /* Set STDIN socket nonblocking (to ensure accept() never blocks) */ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK | fcntl(STDIN_FILENO, F_GETFL)); /* initialize Clients list */ Clients = dList_new(512); /* some OSes may need this... */ sin_sz = sizeof(sin); /* start the service loop */ while (!DPIBYE) { /* wait for activity */ do { c_st = File_check_fds(10); } while (c_st == 0 && !DPIBYE); if (c_st < 0) { MSG(" select() %s\n", dStrerror(errno)); break; } if (DPIBYE) break; if (FD_ISSET(STDIN_FILENO, &read_set)) { /* accept the incoming connection */ do { sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &sin_sz); } while (sock_fd < 0 && errno == EINTR); if (sock_fd == -1) { if (errno == EAGAIN) continue; MSG(" accept() %s\n", dStrerror(errno)); break; } else { _MSG(" accept() fd=%d\n", sock_fd); /* Set nonblocking */ fcntl(sock_fd, F_SETFL, O_NONBLOCK | fcntl(sock_fd, F_GETFL)); /* Create and initialize a new client */ File_add_client(sock_fd); } continue; } File_serve_clients(); } if (DPIBYE) st = 0; return st; }