📄 http-core.c
字号:
/* * http-core.c - http core functionality * * Copyright (C) 2000, 2001 Stefan Jahn <stefan@lkcc.org> * * This 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, or (at your option) * any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * $Id: http-core.c,v 1.42 2001/08/03 18:09:04 ela Exp $ * */#if HAVE_CONFIG_H# include <config.h>#endif#if ENABLE_HTTP_PROTO#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <time.h>#include <sys/types.h>#if HAVE_UNISTD_H# include <unistd.h>#endif#if HAVE_PWD_H && !defined (__MINGW32__)# include <pwd.h>#endif#ifndef __MINGW32__# include <netinet/in.h>#endif#ifdef __MINGW32__# include <winsock2.h># include <io.h># include <lm.h># include <lmerr.h>#endif#include "libserveez.h"#include "http-proto.h"#include "http-core.h"/* the current http header structure */http_header_t http_header;/* * In Win32 OS's both of these defines are necessary for portability. */#if defined (__CYGWIN__) || defined (__MINGW32__)# define timezone _timezone# ifndef daylight# define daylight _daylight# endif#elif !HAVE_TIMEZONE/* * For some reason FreeBSD 3.2 does not provide `timezone' and `daylight'. */# define timezone ((long int) 0)# define daylight ((int) 0)#endif#ifdef __MINGW32__/* * Handle and function definitions for the NetApi interface. */typedef NET_API_STATUS (__stdcall *GetUserInfoProc) (WCHAR *, WCHAR *, DWORD, LPBYTE *);typedef NET_API_STATUS (__stdcall *FreeUserInfoProc) (void *);static FreeUserInfoProc FreeUserInfo = NULL;static GetUserInfoProc GetUserInfo = NULL;static HMODULE netapiHandle = NULL;/* * Unload the `netapi32.dll'. */voidhttp_stop_netapi (void){ if (netapiHandle) { FreeLibrary (netapiHandle); netapiHandle = NULL; }}/* * Load `netapi32.dll' and get function pointers necessary to obtain * a user's home directory. */voidhttp_start_netapi (void){ if ((netapiHandle = LoadLibrary ("netapi32.dll")) != NULL) { FreeUserInfo = (FreeUserInfoProc) GetProcAddress (netapiHandle, "NetApiBufferFree"); GetUserInfo = (GetUserInfoProc) GetProcAddress (netapiHandle, "NetUserGetInfo"); if (GetUserInfo == NULL) { svz_log (LOG_ERROR, "http: GetProcAddress: %s\n", SYS_ERROR); FreeLibrary (netapiHandle); netapiHandle = NULL; } }}#endif /* not __MINGW32__ *//* * If the given request has a leading `~' we try to get the appropriate * user's home directory and put it in front of the request. */char *http_userdir (svz_socket_t *sock, char *uri){ http_config_t *cfg = sock->cfg; char *p = uri + 1; char *user, *file;#if HAVE_GETPWNAM struct passwd *entry;#elif defined (__MINGW32__) USER_INFO_1 *entry = NULL; NET_API_STATUS status;#endif if (*uri && *p++ == '~' && cfg->userdir) { /* parse onto end of user name */ while (*p && *p != '/') p++; if (p - uri <= 2) return NULL; user = svz_malloc (p - uri - 1); memcpy (user, uri + 2, p - uri - 2); user[p - uri - 2] = '\0'; #if HAVE_GETPWNAM if ((entry = getpwnam (user)) != NULL) { file = svz_malloc (strlen (entry->pw_dir) + strlen (cfg->userdir) + strlen (p) + 2); sprintf (file, "%s/%s%s", entry->pw_dir, cfg->userdir, p); svz_free (user); return file; }#elif defined (__MINGW32__) if (GetUserInfo == NULL) { svz_free (user); return NULL; } status = GetUserInfo (NULL, /* server name */ svz_windoze_asc2uni (user), /* user name */ 1, /* type of info */ (LPBYTE *) &entry); /* info buffer */ if (status != NERR_Success) { char *error; switch (status) { case ERROR_ACCESS_DENIED: error = "The user does not have access to the requested " "information."; break; case NERR_InvalidComputer: error = "The computer name is invalid."; break; case NERR_UserNotFound: error = "The user name could not be found."; break; default: error = "Unknown error."; break; } svz_log (LOG_ERROR, "NetUserGetInfo: %s\n", error); } /* successfully got the user information ? */ else if (entry && entry->usri1_home_dir && entry->usri1_home_dir[0]) { file = svz_malloc (strlen (svz_windoze_uni2asc (entry->usri1_home_dir)) + strlen (cfg->userdir) + strlen (p) + 2); sprintf (file, "%s/%s%s", svz_windoze_uni2asc (entry->usri1_home_dir), cfg->userdir, p); FreeUserInfo (entry); svz_free (user); return file; }#if ENABLE_DEBUG else if (entry) { svz_log (LOG_DEBUG, "http: home directory for %s not set\n", svz_windoze_uni2asc (entry->usri1_name)); }#endif /* ENABLE_DEBUG */#endif /* not HAVE_GETPWNAM and not __MINGW32__ */ svz_free (user); } return NULL;}/* * When the http server has been configured to invoke identd request * this function is called for each client connection after successful * identification. */inthttp_identification (char *ident, int id, int version){ http_socket_t *http; svz_socket_t *sock = svz_sock_find (id, version); if (ident && sock) { http = sock->data; if (!http->ident) http->ident = svz_strdup (ident); } return 0;}/* * Each http client gets resolved by this callback. */inthttp_remotehost (char *host, int id, int version){ http_socket_t *http; svz_socket_t *sock = svz_sock_find (id, version); if (host && sock) { http = sock->data; if (!http->host) http->host = svz_strdup (host); } return 0;}/* * When the localhost has been resolved to a hostname this callback is * invoked by the main loop. Put the result into the http configuration. */inthttp_localhost (char *host, http_config_t *cfg){ if (host && !cfg->host) { cfg->host = svz_pstrdup (host); } return 0;}/* * Write a logging notification to the access logfile if possible * and necessary. */voidhttp_log (svz_socket_t *sock){ http_config_t *cfg = sock->cfg; http_socket_t *http = sock->data; static char line[1024]; char *referrer, *agent, *p, *start; if (cfg->log && http->request) { referrer = http_find_property (http, "Referer"); agent = http_find_property (http, "User-Agent"); /* access logging format given ? */ if (cfg->logformat && *cfg->logformat) start = cfg->logformat; else start = HTTP_CLF; memset (line, 0, sizeof (line)); while (*start) { /* parse until next format character */ p = start; while (*p && *p != '%') p++; strncat (line, start, p - start); if (!*p) break; p++; switch (*p) { /* %i - identity information */ case 'i': strcat (line, http->ident ? http->ident : "-"); p++; break; /* %u - user authentication */ case 'u': strcat (line, http->auth ? http->auth : "-"); p++; break; /* %l - delivered content length */ case 'l': strcat (line, svz_itoa (http->length)); p++; break; /* %c - http response code */ case 'c': strcat (line, svz_itoa (http->response)); p++; break; /* %h - host name */ case 'h': strcat (line, http->host ? http->host : svz_inet_ntoa (sock->remote_addr)); p++; break; /* %t - request time stamp */ case 't': strcat (line, http_clf_date (http->timestamp)); p++; break; /* end of string */ case '\0': break; /* %R - original http request uri */ case 'R': strcat (line, http->request ? http->request : "-"); p++; break; /* %r - referrer document */ case 'r': strcat (line, referrer ? referrer : "-"); p++; break; /* %a - user agent */ case 'a': strcat (line, agent ? agent : "-"); p++; break; default: p++; break; } start = p; } strcat (line, "\n"); if (!ferror (cfg->log) && !feof (cfg->log)) { fprintf (cfg->log, line); fflush (cfg->log); } else { svz_log (LOG_ERROR, "http: access logfile died\n"); svz_fclose (cfg->log); cfg->log = NULL; } }}/* * Reset the current http header structure. */voidhttp_reset_header (void){ http_header.code = 0; http_header.field[0] = '\0'; http_header.response = NULL;}/* * Add a response header field to the current header. */voidhttp_add_header (const char *fmt, ...){ va_list args; int len = strlen (http_header.field); char *p = http_header.field + len; if (len >= HTTP_HEADER_SIZE) return; va_start (args, fmt); svz_vsnprintf (p, HTTP_HEADER_SIZE - len, fmt, args); va_end (args);}/* * Send the http header. */inthttp_send_header (svz_socket_t *sock){ int ret = 0; /* send first part of header including response field and static texts */ ret = svz_sock_printf (sock, "%s" "Date: %s\r\n" "Server: %s/%s\r\n", http_header.response, http_asc_date (time (NULL)), svz_library, svz_version); if (ret) return ret; /* send header fields and trailing line break */ ret = svz_sock_printf (sock, "%s\r\n", http_header.field); if (ret) return ret; return 0;}/* * Set the current http header response. */voidhttp_set_header (char *response){ http_header.response = response;}/* * Create a http content range if the given line specifies a valid one. * Return zero on success and -1 on errors. */inthttp_get_range (char *line, http_range_t *range){ char *p = line; off_t n; /* parse until byte-range specifier */ if (!line || !range) return -1; while (*p && *p != ':') p++; if (!*p || *(p + 1) != ' ') return -1; p += 2; /* check identifier */ if (memcmp (p, "bytes", 5)) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "http: invalid byte-range specifier (%s)\n", p);#endif return -1; } p += 5; /* parse content range itself */ range->first = range->last = range->length = 0; if (*p != '=') return 0; p++; n = 0; while (*p >= '0' && *p <= '9') { n *= 10; n += (*p - '0'); p++; } range->first = n; if (*p != '-') return 0; p++; n = 0; while (*p >= '0' && *p <= '9') { n *= 10; n += (*p - '0'); p++; } range->last = n; if (*p != '/') return 0; p++; n = 0; while (*p >= '0' && *p <= '9') { n *= 10; n += (*p - '0'); p++; } range->length = n; return 0;}/* * Send an error message response body to the http client connection. * This is not actually necessary, because an appropriate response header * should work out fine. But most browsers indicate "document contained * not data." if this occurs. */inthttp_error_response (svz_socket_t *sock, int response){ http_config_t *cfg = sock->cfg; http_socket_t *http = sock->data; char *txt; /* Convert error code to text. */ switch (response) { case 400: txt = "Bad Request"; break; case 401: txt = "Unauthorized"; break; case 402: txt = "Payment Required";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -