📄 file.c
字号:
/* * File: file.c :) * * Copyright (C) 2000 - 2004 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 2 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 <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/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 <glib.h>#include "../dpip/dpip.h"#include "dpiutil.h"#define MAXNAMESIZE 30#define HIDE_DOTFILES TRUE#define _MSG(fmt...)#define MSG(fmt...) g_print("[file dpi]: " fmt)enum { FILE_OK, FILE_NOT_FOUND, FILE_NO_ACCESS};typedef struct { char *full_path; const char *filename; off_t size; mode_t mode; time_t mtime;} FileInfo;typedef struct { char *dirname; GList *flist; /* List of files and subdirectories (for sorting) */} DilloDir;typedef struct { SockHandler *sh; gint status; gint old_style; pthread_t thrID; gint done;} ClientInfo;/* * Forward references */static const char *File_content_type(const char *filename);static gint File_get_file(ClientInfo *Client, const gchar *filename, struct stat *sb, const char *orig_url);static gint File_get_dir(ClientInfo *Client, const gchar *DirName, const char *orig_url);/* * Global variables */static volatile gint DPIBYE = 0;static volatile gint ThreadRunning = 0;static gint OLD_STYLE = 0;/* A list for the clients we are serving */static GList *Clients = NULL;/* a mutex for operations on clients */static pthread_mutex_t ClMut;/* * Close a file descriptor, but handling EINTR */static void File_close(int fd){ while (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 gchar *File_get_content_type_from_data(void *Data, size_t Size){ static const gchar *Types[] = { "application/octet-stream", "text/html", "text/plain", "image/gif", "image/png", "image/jpeg", }; gint Type = 0; gchar *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 && isspace(p[i]); ++i); if ((Size - i >= 5 && !g_strncasecmp(p+i, "<html", 5)) || (Size - i >= 5 && !g_strncasecmp(p+i, "<head", 5)) || (Size - i >= 6 && !g_strncasecmp(p+i, "<title", 6)) || (Size - i >= 14 && !g_strncasecmp(p+i, "<!doctype html", 14)) || /* this line is workaround for FTP through the Squid proxy */ (Size - i >= 17 && !g_strncasecmp(p+i, "<!-- HTML listing", 17))) { Type = 1; /* Images */ } else if (Size >= 4 && !g_strncasecmp(p, "GIF8", 4)) { Type = 3; } else if (Size >= 4 && !g_strncasecmp(p, "\x89PNG", 4)) { Type = 4; } else if (Size >= 2 && !g_strncasecmp(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 ((unsigned char) 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 gint File_comp(gconstpointer a, gconstpointer b){ FileInfo *f1 = (FileInfo *) a; FileInfo *f2 = (FileInfo *) b; 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 = g_new(DilloDir, 1); Ddir->dirname = g_strdup(dirname); Ddir->flist = NULL; 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 = g_strdup_printf("%s/%s", Ddir->dirname, de->d_name); if (stat(fname, &sb) == -1) { g_free(fname); continue; /* ignore files we can't stat */ } finfo = g_new(FileInfo, 1); finfo->full_path = fname; finfo->filename = fname + dirname_len + 1; finfo->size = sb.st_size; finfo->mode = sb.st_mode; finfo->mtime = sb.st_mtime; Ddir->flist = g_list_prepend(Ddir->flist, finfo); } closedir(dir); /* sort the entries */ Ddir->flist = g_list_sort(Ddir->flist, File_comp); return Ddir;}/* * Deallocate a DilloDir structure. */static void File_dillodir_free(DilloDir *Ddir){ GList *list; for (list = Ddir->flist; list; list = g_list_next(list)) { FileInfo *finfo = list->data; g_free(finfo->full_path); g_free(finfo); } g_list_free(Ddir->flist); g_free(Ddir->dirname); g_free(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 = g_strdup(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); sock_handler_printf(Client->sh, 0, "<a href='file:%s'>Parent directory</a>", HUparent); g_free(HUparent); g_free(Uparent); g_free(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) { sock_handler_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8); if (time(NULL) - mtime > 15811200) { sock_handler_printf(Client->sh, 0, " %.4s", ds + 20); } else { sock_handler_printf(Client->sh, 0, " %.5s", ds + 11); } } else { sock_handler_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, const char *dirname, gint n){ gint 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 = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .."; gint ndots = MAXNAMESIZE - strlen(name); sock_handler_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 { sock_handler_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); sock_handler_printf(Client->sh, 0, "\n"); g_free(Hname); g_free(HUref); g_free(Uref);}/* * Read a local directory and translate it to html. */static void File_transfer_dir(ClientInfo *Client, DilloDir *Ddir, const char *orig_url){ gint n; GList *list; char *d_cmd, *Hdirname, *Udirname, *HUdirname; /* Send DPI header */ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url); sock_handler_write_str(Client->sh, d_cmd, 1); g_free(d_cmd); /* Send page title */ Udirname = Escape_uri_str(Ddir->dirname, NULL); HUdirname = Escape_html_str(Udirname); Hdirname = Escape_html_str(Ddir->dirname); sock_handler_printf(Client->sh, 0, "Content-Type: text/html\n\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); g_free(Hdirname); g_free(HUdirname); g_free(Udirname); if (Client->old_style) { sock_handler_printf(Client->sh, 0, "<pre>\n"); } /* Output the parent directory */ File_print_parent_dir(Client, Ddir->dirname); /* HTML style toggle */ sock_handler_printf(Client->sh, 0, " <a href='dpi:/file/toggle'>%%</a>\n"); if (Ddir->flist) { if (Client->old_style) { sock_handler_printf(Client->sh, 0, "\n\n"); } else { sock_handler_printf(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 { sock_handler_printf(Client->sh, 0, "<br><br>Directory is empty..."); } /* Output entries */ for (n = 0, list = Ddir->flist; list; list = g_list_next(list)) { File_info2html(Client, list->data, Ddir->dirname, ++n); } if (Ddir->flist) { if (Client->old_style) { sock_handler_printf(Client->sh, 0, "</pre>\n"); } else { sock_handler_printf(Client->sh, 0, "</table>\n"); } } sock_handler_printf(Client->sh, 0, "</BODY></HTML>\n");}/* * 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 (!strcasecmp(e, "gif")) { return "image/gif"; } else if (!strcasecmp(e, "jpg") || !strcasecmp(e, "jpeg")) { return "image/jpeg"; } else if (!strcasecmp(e, "png")) { return "image/png"; } else if (!strcasecmp(e, "html") || !strcasecmp(e, "htm") || !strcasecmp(e, "shtml")) { return "text/html"; } 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){ gint fd; struct stat sb;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -