connection.c
来自「一个很有名的浏览器」· C语言 代码 · 共 599 行
C
599 行
/* Connection and data transport handling *//* $Id: connection.c,v 1.3.4.4 2005/05/01 22:09:22 jonas Exp $ */#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/nntp/codes.h"#include "protocol/nntp/connection.h"#include "protocol/nntp/nntp.h"#include "protocol/nntp/response.h"#include "protocol/protocol.h"#include "protocol/uri.h"#include "sched/connection.h"#include "util/memory.h"#include "util/string.h"/* The official color for this planet is green, * which grows in pockets of them people willing to scheme. --delasoul */static void nntp_send_command(struct connection *conn);/* Connection setup: *//* Resolves the target by looking at the part of the URI _after_ last '/' */static enum nntp_targetget_nntp_target(unsigned char *data, int datalen){ enum nntp_target target = NNTP_TARGET_ARTICLE_NUMBER; int pos; /* List groups on the server if there is nothing specified */ if (!datalen) return NNTP_TARGET_GROUPS; /* Check for <message-id> */ if (memchr(data, '@', datalen)) return NNTP_TARGET_MESSAGE_ID; /* Check for <article-number> or <start-number>-<end-number> */ for (pos = 0; pos < datalen; pos++) { if (!isdigit(data[pos])) { /* Assume <group> if it is not a range dash * or a range dash was already detected */ if (data[pos] != '-' || target == NNTP_TARGET_ARTICLE_RANGE) return NNTP_TARGET_GROUP; target = NNTP_TARGET_ARTICLE_RANGE; } } /* If the scanning saw only numberical char and possibly one '-' it * defaults to assume the URI part describes article number or range. * This rules out numerical only group names. */ return target;}/* Init article range and check that both <start-number> and <end-number> are * non empty and valid numbers and that the range is valid. */static intinit_nntp_article_range(struct nntp_connection_info *nntp, unsigned char *data, int datalen){ long start_number, end_number; unsigned char *end; errno = 0; start_number = strtol(data, (char **) &end, 10); if (errno || !end || *end != '-' || start_number < 0) return 0; end_number = strtol(end + 1, (char **) &end, 10); if (errno || end != data + datalen || end_number < 0) return 0; nntp->current_article = start_number; nntp->end_article = end_number; nntp->message.source = data; nntp->message.length = datalen; return start_number <= end_number ? 1 : 0;}static struct nntp_connection_info *init_nntp_connection_info(struct connection *conn){ struct uri *uri = conn->uri; struct nntp_connection_info *nntp; unsigned char *groupend; unsigned char *data; int datalen; nntp = mem_calloc(1, sizeof(*nntp)); if (!nntp) { abort_conn_with_state(conn, S_OUT_OF_MEM); return NULL; } conn->info = nntp; set_string_magic(&nntp->group); set_string_magic(&nntp->message); data = uri->data; datalen = uri->datalen; /* Check for <group>/ */ groupend = memchr(data, '/', datalen); if (groupend) { int grouplen = groupend - data; nntp->group.source = data; nntp->group.length = grouplen; datalen -= grouplen + 1; data += grouplen + 1; /* Nothing after last '/'? */ if (!datalen) { nntp->target = NNTP_TARGET_GROUP; return nntp; } } nntp->target = get_nntp_target(data, datalen); switch (nntp->target) { case NNTP_TARGET_ARTICLE_RANGE: if (init_nntp_article_range(nntp, data, datalen)) break; /* FIXME: Special S_NNTP_BAD_RANGE */ abort_conn_with_state(conn, S_BAD_URL); return NULL; case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: nntp->message.source = data; nntp->message.length = datalen; break; case NNTP_TARGET_GROUP: /* Map nntp://<server>/<group> to nntp://<server>/<group>/ so * we get only one cache entry with content. */ if (!groupend) { enum connection_state state = S_OK; conn->cached = get_cache_entry(conn->uri); if (!conn->cached || !redirect_cache(conn->cached, "/", 0, 0)) state = S_OUT_OF_MEM; abort_conn_with_state(conn, state); return NULL; } /* Reject nntp://<server>/<group>/<group> */ if (nntp->group.source) { abort_conn_with_state(conn, S_BAD_URL); return NULL; } nntp->group.source = data; nntp->group.length = datalen; break; case NNTP_TARGET_GROUPS: case NNTP_TARGET_QUIT: break; } return nntp;}/* Connection teardown: */static voidnntp_quit(struct connection *conn){ struct nntp_connection_info *info; info = mem_calloc(1, sizeof(*info)); if (!info) { abort_conn_with_state(conn, S_OUT_OF_MEM); return; } info->target = NNTP_TARGET_QUIT; conn->info = info; nntp_send_command(conn);}static voidnntp_end_request(struct connection *conn, enum connection_state state){ struct nntp_connection_info *nntp = conn->info; if (nntp->target == NNTP_TARGET_QUIT) { abort_conn_with_state(conn, state); return; } set_connection_state(conn, state); if (conn->state == S_OK) { if (conn->cached) { truncate_entry(conn->cached, conn->from, 1); conn->cached->incomplete = 0; } } else if (conn->state == S_OUT_OF_MEM) { /* FIXME: Clear the socket buffers before ending so the one * grabing the keepalive connection will be able to go on. */ } add_keepalive_connection(conn, NNTP_KEEPALIVE_TIMEOUT, nntp_quit);}/* Reponse receiving: */static voidread_nntp_data(struct connection *conn, struct read_buffer *rb){ set_connection_timeout(conn); if (rb->close == READ_BUFFER_END) { nntp_end_request(conn, S_OK); return; } switch (read_nntp_response_data(conn, rb)) { case S_OK: nntp_send_command(conn); break; case S_OUT_OF_MEM: nntp_end_request(conn, S_OUT_OF_MEM); break; case S_TRANS: default: read_from_socket(conn, &conn->socket, rb, read_nntp_data); set_connection_state(conn, S_TRANS); }}/* Translate NNTP code to the internal connection state. */static enum connection_stateget_nntp_connection_state(enum nntp_code code){ switch (code) { case NNTP_CODE_400_GOODBYE: return S_NNTP_SERVER_HANG_UP; case NNTP_CODE_411_GROUP_UNKNOWN: return S_NNTP_GROUP_UNKNOWN; case NNTP_CODE_423_ARTICLE_NONUMBER: return S_NNTP_ARTICLE_UNKNOWN; case NNTP_CODE_430_ARTICLE_NOID: return S_NNTP_ARTICLE_UNKNOWN; case NNTP_CODE_436_ARTICLE_TRANSFER: return S_NNTP_TRANSFER_ERROR; case NNTP_CODE_480_AUTH_REQUIRED: return S_NNTP_AUTH_REQUIRED; case NNTP_CODE_502_ACCESS_DENIED: return S_NNTP_ACCESS_DENIED; case NNTP_CODE_503_PROGRAM_FAULT: return S_NNTP_SERVER_ERROR; case NNTP_CODE_412_GROUP_UNSET: case NNTP_CODE_420_ARTICLE_UNSET: case NNTP_CODE_421_ARTICLE_NONEXT: case NNTP_CODE_422_ARTICLE_NOPREV: case NNTP_CODE_435_ARTICLE_NOSEND: case NNTP_CODE_437_ARTICLE_REJECTED: case NNTP_CODE_440_POSTING_DENIED: case NNTP_CODE_441_POSTING_FAILED: case NNTP_CODE_482_AUTH_REJECTED: case NNTP_CODE_580_AUTH_FAILED: case NNTP_CODE_500_COMMAND_UNKNOWN: case NNTP_CODE_501_COMMAND_SYNTAX: default: /* Notice and error codes for stuff which is either not * supported or which is not supposed to happen. */ return S_NNTP_ERROR; };}static voidnntp_got_response(struct connection *conn, struct read_buffer *rb){ struct nntp_connection_info *nntp = conn->info; set_connection_timeout(conn); if (rb->close == READ_BUFFER_END) { nntp_end_request(conn, S_OK); return; } nntp->code = get_nntp_response_code(conn, rb); switch (nntp->code) { case NNTP_CODE_NONE: read_from_socket(conn, &conn->socket, rb, nntp_got_response); set_connection_state(conn, S_TRANS); break; case NNTP_CODE_INVALID: nntp_end_request(conn, S_NNTP_ERROR); break; case NNTP_CODE_200_HELLO: case NNTP_CODE_201_HELLO_NOPOST: case NNTP_CODE_211_GROUP_SELECTED: nntp_send_command(conn); break; case NNTP_CODE_205_GOODBYE: nntp_end_request(conn, S_OK); break; case NNTP_CODE_215_FOLLOW_GROUPS: case NNTP_CODE_220_FOLLOW_ARTICLE: case NNTP_CODE_221_FOLLOW_HEAD: case NNTP_CODE_222_FOLLOW_BODY: case NNTP_CODE_223_FOLLOW_NOTEXT: case NNTP_CODE_224_FOLLOW_XOVER: case NNTP_CODE_230_FOLLOW_NEWNEWS: case NNTP_CODE_231_FOLLOW_NEWGROUPS: read_nntp_data(conn, rb); break; case NNTP_CODE_500_COMMAND_UNKNOWN: if (nntp->command == NNTP_COMMAND_LIST_ARTICLES && !nntp->xover_unsupported) { nntp->xover_unsupported = 1; nntp_send_command(conn); break; } default: /* FIXME: Handle (error) response codes */ nntp_end_request(conn, get_nntp_connection_state(nntp->code)); break; }}static voidnntp_get_response(struct connection *conn){ struct read_buffer *rb = alloc_read_buffer(conn); if (!rb) return; set_connection_timeout(conn); rb->close = READ_BUFFER_END_ONCLOSE; read_from_socket(conn, &conn->socket, rb, nntp_got_response);}/* Command sending: *//* Figure out the next command by looking at the target and the previous * command. */static enum nntp_commandget_nntp_command(struct nntp_connection_info *nntp){ /* The more logical approach of making the switch noodle use the target * actually will make it bigger altho' assertions of invalid states * would be easier. I chose to make it simpler for now. --jonas */ /* All valid states should return from within the outer switch. Anything * else should break out to the function end and cause an internal * error message. */ switch (nntp->command) { case NNTP_COMMAND_NONE: switch (nntp->target) { case NNTP_TARGET_GROUPS: return NNTP_COMMAND_LIST_NEWSGROUPS; case NNTP_TARGET_GROUP: case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_ARTICLE_RANGE: case NNTP_TARGET_GROUP_MESSAGE_ID: return NNTP_COMMAND_GROUP; case NNTP_TARGET_MESSAGE_ID: return NNTP_COMMAND_ARTICLE_MESSAGE_ID; case NNTP_TARGET_QUIT: return NNTP_COMMAND_QUIT; } break; case NNTP_COMMAND_GROUP: switch (nntp->target) { case NNTP_TARGET_GROUP: case NNTP_TARGET_ARTICLE_RANGE: return NNTP_COMMAND_LIST_ARTICLES; case NNTP_TARGET_ARTICLE_NUMBER: return NNTP_COMMAND_ARTICLE_NUMBER; case NNTP_TARGET_GROUP_MESSAGE_ID: return NNTP_COMMAND_ARTICLE_MESSAGE_ID; case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_QUIT: case NNTP_TARGET_GROUPS: break; } break; case NNTP_COMMAND_LIST_ARTICLES: if (nntp->target != NNTP_TARGET_GROUP && nntp->target != NNTP_TARGET_ARTICLE_RANGE) break; /* Are there more articles to list? */ if (nntp->xover_unsupported && nntp->current_article <= nntp->end_article) return NNTP_COMMAND_LIST_ARTICLES; return NNTP_COMMAND_NONE; case NNTP_COMMAND_ARTICLE_NUMBER: if (nntp->target == NNTP_TARGET_ARTICLE_NUMBER) return NNTP_COMMAND_NONE; return NNTP_COMMAND_NONE; case NNTP_COMMAND_ARTICLE_MESSAGE_ID: case NNTP_COMMAND_LIST_NEWSGROUPS: case NNTP_COMMAND_QUIT: /* FIXME: assert()? --jonas */ return NNTP_COMMAND_NONE; } INTERNAL("Bad command %d handling %d", nntp->target, nntp->command); return NNTP_COMMAND_NONE;}/* Command lines shall not exceed 512 characters in length, counting all * characters including spaces, separators, punctuation, and the trailing CR-LF * (Carriage Return - Line Feed) pair. */#define NNTP_MAX_COMMAND_LENGTH 512/* FIXME: For the beauty maybe %.*s could be used. --jonas */static voidadd_nntp_command_to_string(struct string *req, struct nntp_connection_info *nntp){ switch (nntp->command) { case NNTP_COMMAND_GROUP: add_to_string(req, "GROUP "); add_string_to_string(req, &nntp->group); break; case NNTP_COMMAND_ARTICLE_NUMBER: add_to_string(req, "ARTICLE "); add_string_to_string(req, &nntp->message); break; case NNTP_COMMAND_ARTICLE_MESSAGE_ID: add_to_string(req, "ARTICLE <"); add_string_to_string(req, &nntp->message); add_char_to_string(req, '>'); break; case NNTP_COMMAND_LIST_ARTICLES: if (!nntp->xover_unsupported) { int first = nntp->current_article++; int last = nntp->end_article; add_format_to_string(req, "XOVER %d-%d", first, last); break; } assert(nntp->current_article <= nntp->end_article); add_format_to_string(req, "HEAD %d", nntp->current_article++); break; case NNTP_COMMAND_QUIT: add_to_string(req, "QUIT"); break; case NNTP_COMMAND_LIST_NEWSGROUPS: add_to_string(req, "LIST NEWSGROUPS"); break; case NNTP_COMMAND_NONE: INTERNAL("Trying to add 'no' command."); return; } add_crlf_to_string(req);}static voidnntp_send_command(struct connection *conn){ struct nntp_connection_info *nntp = conn->info; struct string req; nntp->command = get_nntp_command(nntp); if (nntp->command == NNTP_COMMAND_NONE) { nntp_end_request(conn, S_OK); return; } if (!init_string(&req)) { nntp_end_request(conn, S_OUT_OF_MEM); return; } /* FIXME: Check non empty and < NNTP_MAX_COMMAND_LENGTH */ add_nntp_command_to_string(&req, nntp); write_to_socket(conn, &conn->socket, req.source, req.length, nntp_get_response); done_string(&req); set_connection_state(conn, S_SENT);}/* The ``nntp://'' and ``news:'' protocol handlers: */voidnntp_protocol_handler(struct connection *conn){ if (!init_nntp_connection_info(conn)) return; set_connection_timeout(conn); if (!has_keepalive_connection(conn)) { make_connection(conn, &conn->socket, nntp_get_response); } else { nntp_send_command(conn); }}/* Note that while nntp:// URIs specify a unique location for the article * resource, most NNTP servers currently on the Internet today are configured * only to allow access from local clients, and thus nntp:// URIs do not * designate globally accessible resources. Thus, the news: form of URI is * preferred as a way of identifying news articles. * * Whenever dealing with news: URIs the <server> part is retrieved from either * protocol.nntp.server or the NNTPSERVER environment variable. The news: URI * is then redirected to a nntp:// URI using the <server> so that: * * news:<message-id> -> nntp://<server>/<message-id> * news:<group> -> nntp://<server>/<group> * news:<group>/<...> -> nntp://<server>/<group>/<...> */voidnews_protocol_handler(struct connection *conn){ unsigned char *protocol; unsigned char *server = get_nntp_server(); struct string location; if (!*server) server = getenv("NNTPSERVER"); if (!server || !*server) { abort_conn_with_state(conn, S_NNTP_NEWS_SERVER); return; } conn->cached = get_cache_entry(conn->uri); if (!conn->cached || !init_string(&location)) { abort_conn_with_state(conn, S_OUT_OF_MEM); return; } protocol = conn->uri->protocol == PROTOCOL_NEWS ? "nntp" : "nntps"; add_format_to_string(&location, "%s://%s/", protocol, server); add_uri_to_string(&location, conn->uri, URI_DATA); redirect_cache(conn->cached, location.source, 0, 0); done_string(&location); abort_conn_with_state(conn, S_OK);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?