⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gopher.c

📁 一个很有名的浏览器
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Gopher access protocol (RFC 1436) *//* $Id: gopher.c,v 1.31.2.2 2005/05/01 21:04:46 jonas Exp $ *//* Based on version of HTGopher.c in the lynx tree. * * Author tags: *	TBL	Tim Berners-Lee * 	FM	Foteos Macrides * * A little history: *	26 Sep 90	Adapted from other accesses (News, HTTP) TBL *	29 Nov 91	Downgraded to C, for portable implementation. *	10 Mar 96	Foteos Macrides (macrides@sci.wfbr.edu).  Added a *			form-based CSO/PH gateway.  Can be invoked via a *			"cso://host[:port]/" or "gopher://host:105/2" *			URL.	If a gopher URL is used with a query token *			('?'), the old ISINDEX procedure will be used *			instead of the form-based gateway. *	15 Mar 96	Foteos Macrides (macrides@sci.wfbr.edu).  Pass *			port 79, gtype 0 gopher URLs to the finger *			gateway. */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <errno.h>#include <stdlib.h>#include <string.h>#include "elinks.h"#include "cache/cache.h"#include "intl/gettext/libintl.h"#include "lowlevel/connect.h"#include "modules/module.h"#include "protocol/gopher/gopher.h"#include "protocol/protocol.h"#include "protocol/uri.h"#include "sched/connection.h"#include "util/conv.h"#include "util/memory.h"#include "util/string.h"struct module gopher_protocol_module = struct_module(	/* name: */		N_("Gopher"),	/* options: */		NULL,	/* hooks: */		NULL,	/* submodules: */	NULL,	/* data: */		NULL,	/* init: */		NULL,	/* done: */		NULL);/* Gopher entity types */enum gopher_entity {	GOPHER_UNKNOWN		=  0 , /* Special fall-back entity */	GOPHER_FILE		= '0',	GOPHER_DIRECTORY	= '1',	GOPHER_CSO		= '2',	GOPHER_ERROR		= '3',	GOPHER_MACBINHEX	= '4',	GOPHER_PCBINARY		= '5',	GOPHER_UUENCODED	= '6',	GOPHER_INDEX		= '7',	GOPHER_TELNET		= '8',	GOPHER_BINARY		= '9',	GOPHER_GIF		= 'g',	GOPHER_HTML		= 'h', /* HTML */	GOPHER_CHTML		= 'H', /* HTML */	GOPHER_MIME		= 'm',	GOPHER_SOUND		= 's',	GOPHER_WWW		= 'w', /* W3 address */	GOPHER_IMAGE		= 'I',	GOPHER_TN3270		= 'T',	GOPHER_INFO		= 'i', /* Information or separator line */	GOPHER_DUPLICATE	= '+',	GOPHER_PLUS_IMAGE	= ':', /* Addition from Gopher Plus */	GOPHER_PLUS_MOVIE	= ';',	GOPHER_PLUS_SOUND	= '<',	GOPHER_PLUS_PDF		= 'P',};/* Default Gopher Node type is directory listing */#define DEFAULT_GOPHER_ENTITY	GOPHER_DIRECTORY#define entity_needs_gopher_access(entity)	\	((entity) != GOPHER_TELNET		\	 && (entity) != GOPHER_TN3270		\	 && (entity) != GOPHER_WWW)struct gopher_entity_info {	enum gopher_entity type;	unsigned char *description;	unsigned char *content_type;};/* This table provides some hard-coded associations between entity type * and MIME type. A NULL MIME type in this table indicates * that the MIME type should be deduced from the extension. * * - Lynx uses "text/plain" for GOPHER_FILE, but it can be anything. * - Lynx uses "image/gif" for GOPHER_IMAGE and GOPHER_PLUS_IMAGE, *   but they really can be anything. * - GOPHER_BINARY can be, for example, a tar ball, so using *   "application/octet-stream" is a bad idea. */static struct gopher_entity_info gopher_entity_info[] = {	{ GOPHER_BINARY,	"    (BINARY)",	NULL			   },	{ GOPHER_CHTML,		"     (CHTML)",	"text/html"		   },	{ GOPHER_CSO,		"       (CSO)",	"text/html"		   },	{ GOPHER_DIRECTORY,	" (DIRECTORY)",	"text/html"		   },	{ GOPHER_FILE,		"      (FILE)",	NULL /* "text/plain" */	   },	{ GOPHER_GIF,		" (GIF IMAGE)",	"image/gif"		   },	{ GOPHER_HTML,		"      (HTML)",	"text/html"		   },	{ GOPHER_IMAGE,		"     (IMAGE)",	NULL /* "image/gif" */	   },	{ GOPHER_INDEX,		"     (INDEX)",	"text/html"		   },	{ GOPHER_MACBINHEX,	"(BINARY HEX)",	"application/octet-stream" },	{ GOPHER_MIME,		"      (MIME)",	"application/octet-stream" },	{ GOPHER_PCBINARY,	"  (PCBINARY)",	"application/octet-stream" },	{ GOPHER_PLUS_IMAGE,	"    (IMAGE+)",	NULL /* "image/gif" */	   },	{ GOPHER_PLUS_MOVIE,	"     (MOVIE)",	"video/mpeg"		   },	{ GOPHER_PLUS_PDF,	"       (PDF)",	"application/pdf"	   },	{ GOPHER_PLUS_SOUND,	"    (SOUND+)",	"audio/basic"		   },	{ GOPHER_SOUND,		"     (SOUND)",	"audio/basic"		   },	{ GOPHER_TELNET,	"    (TELNET)",	NULL			   },	{ GOPHER_TN3270,	"    (TN3270)",	NULL			   },	{ GOPHER_UUENCODED,	" (UUENCODED)",	"application/octet-stream" },	{ GOPHER_WWW,		"(W3 ADDRESS)",	NULL			   },	{ GOPHER_INFO,		"            ",	NULL			   },	{ GOPHER_ERROR,		NULL,		NULL			   },	/* XXX: Keep GOPHER_UNKNOWN last so it is easy to access. */	{ GOPHER_UNKNOWN,	"            ",	"application/octet-stream" },};static struct gopher_entity_info *get_gopher_entity_info(enum gopher_entity type){	int entry;	for (entry = 0; entry < sizeof(gopher_entity_info) - 1; entry++)		if (gopher_entity_info[entry].type == type)			return &gopher_entity_info[entry];	assert(gopher_entity_info[entry].type == GOPHER_UNKNOWN);	return &gopher_entity_info[entry];}static unsigned char *get_gopher_entity_description(enum gopher_entity type){	struct gopher_entity_info *info = get_gopher_entity_info(type);	return info ? info->description : NULL;}struct gopher_connection_info {	struct gopher_entity_info *entity;	int commandlen;	unsigned char command[1];};/* De-escape a selector into a command. *//* The % hex escapes are converted. Otherwise, the string is copied. */static voidadd_uri_decoded(struct string *command, unsigned char *string, int length,		int replace_plus){	int oldlen = command->length;	assert(string);	if (!length) return;	if (replace_plus) {		/* Remove plus signs 921006 */		if (!add_string_replace(command, string, length, '+', ' '))			return;	} else if (!add_bytes_to_string(command, string, length)) {		return;	}	assert(command->length > oldlen);	/* FIXME: Decoding the whole command string should not be a problem,	 * and I don't remember why I didn't do that in the first place.	 * --jonas */	decode_uri(command->source + oldlen);	/* Evil decode_uri_string() modifies the string */	command->length = strlen(command->source);}static enum connection_state init_gopher_index_cache_entry(struct connection *conn);static enum connection_stateadd_gopher_command(struct connection *conn, struct string *command,		   enum gopher_entity entity,		   unsigned char *selector, int selectorlen){	unsigned char *query;	int querylen;	if (!init_string(command))		return S_OUT_OF_MEM;	/* Look for search string */	query = memchr(selector, '?', selectorlen);	/* Check if no search is required */	if (!query || !query[1]) {		/* Exclude '?' */		if (query) selectorlen -= 1;		query = NULL;		querylen = 0;	} else {		query += 1;		querylen = selector + selectorlen - query;		/* Exclude '?' */		selectorlen -= querylen + 1;		if (querylen >= 7 && !strncasecmp(query, "search=", 7)) {			query	 += 7;			querylen -= 7;		}	}	switch (entity) {	case GOPHER_INDEX:		/* No search required? */		if (!query) {			done_string(command);			return init_gopher_index_cache_entry(conn);		}		add_uri_decoded(command, selector, selectorlen, 0);		add_char_to_string(command, '\t');		add_uri_decoded(command, query, querylen, 1);		break;	case GOPHER_CSO:		/* No search required */		if (!query) {			done_string(command);			/* Display "cover page" */#if 0			return init_gopher_cso_cache_entry(conn);#endif			return S_GOPHER_CSO_ERROR;		}		add_uri_decoded(command, selector, selectorlen, 0);		add_to_string(command, "query ");		add_uri_decoded(command, query, querylen, 1);		break;	default:		/* Not index */		add_uri_decoded(command, selector, selectorlen, 0);	}	add_crlf_to_string(command);	return S_CONN;}static enum connection_stateinit_gopher_connection_info(struct connection *conn){	struct gopher_connection_info *gopher;	enum connection_state state;	struct string command;	enum gopher_entity entity = DEFAULT_GOPHER_ENTITY;	unsigned char *selector = conn->uri->data;	int selectorlen = conn->uri->datalen;	struct gopher_entity_info *entity_info;	size_t size;	/* Get entity type, and selector string. */	/* Pick up gopher_entity */	if (selectorlen > 0) {		entity = *selector++;		selectorlen--;	}	/* This is probably a hack. It serves as a work around when no entity is	 * available in the Gopher URI. Instead of segfaulting later the content	 * will be served as application/octet-stream. However, it could	 * possible break handling Gopher URIs with entities which are really	 * unknown because parts of the real Gopher entity character is added to	 * the selector. A possible work around is to always expect a '/'	 * _after_ the Gopher entity. If the <entity-char> '/' combo is not	 * found assume that the whole URI data part is the selector. */	entity_info = get_gopher_entity_info(entity);	if (entity_info->type == GOPHER_UNKNOWN) {		selector--;		selectorlen++;	}	state = add_gopher_command(conn, &command, entity, selector, selectorlen);	if (state != S_CONN)		return state;	/* Atleast the command should contain \r\n to ask the server	 * wazzup! */	assert(command.length >= 2);	size = sizeof(*gopher) + command.length;	gopher = mem_calloc(1, size);	if (!gopher) {		done_string(&command);		return S_OUT_OF_MEM;	}	gopher->entity = entity_info;	gopher->commandlen = command.length;	memcpy(gopher->command, command.source, command.length);	done_string(&command);	conn->info = gopher;	return S_CONN;}static voidend_gopher_connection(struct connection *conn, enum connection_state state){	if (state == S_OK && conn->cached) {#if 0		/* Seeing dots at all files .. trying to remove the end-marker		 * ".\r\n" */		struct gopher_connection_info *gopher = conn->info;		if (gopher		    && gopher->entity		    && gopher->entity->type != GOPHER_DIRECTORY		    && gopher->entity->type != GOPHER_INDEX		    && gopher->entity->type != GOPHER_CSO) {			conn->from -= 3;		}#endif		truncate_entry(conn->cached, conn->from, 1);		conn->cached->incomplete = 0;	}	abort_conn_with_state(conn, state);}/* Add a link. The title of the destination is set, as there is no way of * knowing what the title is when we arrive. * *	text	points to the text to be put into the file, 0 terminated. * 	addr	points to the hypertext reference address 0 terminated. */static voidadd_gopher_link(struct string *buffer, const unsigned char *text,		const unsigned char *addr){	add_format_to_string(buffer, "<a href=\"%s\">%s</a>",			     addr, text);}static voidadd_gopher_search_field(struct string *buffer, const unsigned char *text,		const unsigned char *addr){	add_format_to_string(buffer,		"<form action=\"%s\">"		"<table>"		"<td>            </td>"		"<td>%s:</td>"		"<td><input maxlength=\"256\" name=\"search\" value=\"\"></td>"		"<td><input type=submit value=\"Search\"></td>"		"</table>"		"</form>",		addr, text);}static voidadd_gopher_description(struct string *buffer, enum gopher_entity entity){	unsigned char *description = get_gopher_entity_description(entity);	if (!description)		return;	add_to_string(buffer, "<b>");	add_to_string(buffer, description);	add_to_string(buffer, "</b> ");}static voidencode_selector_string(struct string *buffer, unsigned char *selector){	unsigned char *slashes;	/* Rather hackishly only convert slashes if there are	 * two successive ones. */	while ((slashes = strstr(selector, "//"))) {		*slashes = 0;		encode_uri_string(buffer, selector, 0);		encode_uri_string(buffer, "//", 1);		*slashes = '/';		selector = slashes + 2;	}	encode_uri_string(buffer, selector, 0);}static voidadd_gopher_menu_line(struct string *buffer, unsigned char *line){	/* Gopher menu fields */	unsigned char *name = line;	unsigned char *selector = NULL;	unsigned char *host = NULL;	unsigned char *port = NULL;	enum gopher_entity entity = *name++;	if (!entity) {		add_char_to_string(buffer, '\n');

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -