📄 response.c
字号:
/* Parses and converts NNTP responses to enum values and cache entry HTML *//* $Id: response.c,v 1.3 2004/11/10 21:08:10 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 "mime/backend/common.h"#include "modules/module.h"#include "protocol/header.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/conv.h"#include "util/memory.h"#include "util/string.h"/* Search for line ending \r\n pair */static unsigned char *get_nntp_line_end(unsigned char *data, int datalen){ for (; datalen > 1; data++, datalen--) if (data[0] == ASCII_CR && data[1] == ASCII_LF) return data + 2; return NULL;}/* RFC 977 - Section 2.4.1. Text Responses: * * A single line containing only a period (.) is sent to indicate the end of * the text (i.e., the server will send a CR-LF pair at the end of the last * line of text, a period, and another CR-LF pair). * * If the text contained a period as the first character of the text line in * the original, that first period is doubled. Therefore, the client must * examine the first character of each line received, and for those beginning * with a period, determine either that this is the end of the text or whether * to collapse the doubled period to a single one. *//* Returns NULL if end-of-text is found else start of collapsed line */static inline unsigned char *check_nntp_line(unsigned char *line, unsigned char *end){ assert(line < end); /* Just to be safe NUL terminate the line */ end[-2] = 0; if (line[0] != '.') return line; if (!line[1]) return NULL; if (line[1] == '.') line++; return line;}static inline unsigned char *get_nntp_message_header_end(unsigned char *data, int datalen){ unsigned char *end, *prev_end = data; while ((end = get_nntp_line_end(data, datalen))) { datalen -= end - data; data = end; /* If only \r\n is there */ if (prev_end + 2 == end) { /* NUL terminate the header so that it ends with just * one \r\n usefull for appending to cached->head. */ end[-2] = 0; return end; } prev_end = end; } return NULL;}static enum connection_stateinit_nntp_header(struct connection *conn, struct read_buffer *rb){ struct nntp_connection_info *nntp = conn->info; if (!conn->cached) { conn->cached = get_cache_entry(conn->uri); if (!conn->cached) return S_OUT_OF_MEM; } else if (conn->cached->head || conn->cached->content_type) { /* If the head is set wipe out the content to be sure */ delete_entry_content(conn->cached); mem_free_set(&conn->cached->head, NULL); } /* XXX: Override any Content-Type line in the header */ mem_free_set(&conn->cached->content_type, stracpy("text/html")); if (!conn->cached->content_type) return S_OUT_OF_MEM; switch (nntp->target) { case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: { unsigned char *end; end = get_nntp_message_header_end(rb->data, rb->len); if (!end) { /* Redo the whole cache entry thing next time */ return S_TRANS; } /* FIXME: Add the NNTP response code line */ conn->cached->head = stracpy("FIXME NNTP response code\r\n"); if (!conn->cached->head) return S_OUT_OF_MEM; add_to_strn(&conn->cached->head, rb->data); /* ... and remove it */ conn->received += end - rb->data; kill_buffer_data(rb, end - rb->data); break; } case NNTP_TARGET_ARTICLE_RANGE: case NNTP_TARGET_GROUP: case NNTP_TARGET_GROUPS: case NNTP_TARGET_QUIT: break; } return S_OK;}static unsigned char *get_nntp_title(struct connection *conn){ struct nntp_connection_info *nntp = conn->info; struct string title; if (!init_string(&title)) return NULL; switch (nntp->target) { case NNTP_TARGET_ARTICLE_RANGE: add_format_to_string(&title, "Articles in the range %d to %d", nntp->current_article, nntp->end_article); break; case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: { unsigned char *subject; subject = parse_header(conn->cached->head, "Subject", NULL); if (subject) { add_to_string(&title, subject); mem_free(subject); break; } add_format_to_string(&title, "Article "), add_string_to_string(&title, &nntp->message); if (nntp->target == NNTP_TARGET_MESSAGE_ID) break; add_format_to_string(&title, " in "); add_string_to_string(&title, &nntp->group); break; } case NNTP_TARGET_GROUP: add_format_to_string(&title, "Articles in "), add_string_to_string(&title, &nntp->group); break; case NNTP_TARGET_GROUPS: add_format_to_string(&title, "Newsgroups on "), add_uri_to_string(&title, conn->uri, URI_PUBLIC); break; case NNTP_TARGET_QUIT: break; } return title.source;}static voidadd_nntp_html_start(struct string *html, struct connection *conn){ struct nntp_connection_info *nntp = conn->info; unsigned char *title = get_nntp_title(conn); add_format_to_string(html, "<html>\n" "<head><title>%s</title></head>\n" "<body>\n", empty_string_or_(title)); switch (nntp->target) { case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: { unsigned char *header_entries; header_entries = get_nntp_header_entries(); if (!*header_entries) break; add_to_string(html, "<pre>"); while (*header_entries) { unsigned char *entry, *value; entry = get_next_path_filename(&header_entries, ','); if (!entry) continue; value = parse_header(conn->cached->head, entry, NULL); if (!value) { mem_free(entry); continue; } add_format_to_string(html, "<b>%s</b>: %s\n", entry, value); mem_free(value); mem_free(entry); } add_to_string(html, "<hr />"); break; } case NNTP_TARGET_ARTICLE_RANGE: case NNTP_TARGET_GROUP: case NNTP_TARGET_GROUPS: add_format_to_string(html, "<h2>%s</h2>\n" "<hr />\n" "<dl>", empty_string_or_(title)); break; case NNTP_TARGET_QUIT: break; } mem_free_if(title);}static voidadd_nntp_html_end(struct string *html, struct connection *conn){ struct nntp_connection_info *nntp = conn->info; switch (nntp->target) { case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: add_to_string(html, "</pre>"); break; case NNTP_TARGET_ARTICLE_RANGE: case NNTP_TARGET_GROUP: case NNTP_TARGET_GROUPS: add_to_string(html, "</dl>"); break; case NNTP_TARGET_QUIT: break; } add_to_string(html, "\n<hr />\n</body>\n</html>");}static voidadd_nntp_html_line(struct string *html, struct connection *conn, unsigned char *line){ struct nntp_connection_info *nntp = conn->info; switch (nntp->target) { case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: add_html_to_string(html, line, strlen(line)); break; case NNTP_TARGET_ARTICLE_RANGE: case NNTP_TARGET_GROUP: case NNTP_TARGET_GROUPS: { unsigned char *desc = strchr(line, '\t'); if (desc) { *desc++ = 0; } else { desc = ""; } add_format_to_string(html, "<dt><a href=\"%s/%s\">%s</a></dt><dd>%s</dd>\n", struri(conn->uri), line, line, desc); break; } case NNTP_TARGET_QUIT: break; } add_char_to_string(html, '\n');}enum connection_stateread_nntp_response_data(struct connection *conn, struct read_buffer *rb){ struct string html; unsigned char *end; enum connection_state state = S_TRANS; if (conn->from == 0) { switch (init_nntp_header(conn, rb)) { case S_OK: break; case S_OUT_OF_MEM: return S_OUT_OF_MEM; case S_TRANS: return S_TRANS; default: return S_NNTP_ERROR; } } if (!init_string(&html)) return S_OUT_OF_MEM; if (conn->from == 0) add_nntp_html_start(&html, conn); while ((end = get_nntp_line_end(rb->data, rb->len))) { unsigned char *line = check_nntp_line(rb->data, end); if (!line) { state = S_OK; break; } add_nntp_html_line(&html, conn, line); conn->received += end - rb->data; kill_buffer_data(rb, end - rb->data); } if (state != S_TRANS) add_nntp_html_end(&html, conn); add_fragment(conn->cached, conn->from, html.source, html.length); conn->from += html.length; done_string(&html); return state;}/* Interpret response code parameters for code 211 - after GROUP command *//* The syntax is: 211 <articles> <first-article> <last-article> <name> *//* Returns 1 on success and 0 on failure */static intparse_nntp_group_parameters(struct nntp_connection_info *nntp, unsigned char *pos, unsigned char *end){ errno = 0; /* Get <articles> */ while (pos < end && !isdigit(*pos)) pos++; nntp->articles = strtol(pos, (char **) &pos, 10); if (errno || pos >= end || nntp->articles < 0) return 0; if (nntp->target == NNTP_TARGET_ARTICLE_RANGE) return 1; /* Get <first-article> */ while (pos < end && !isdigit(*pos)) pos++; nntp->current_article = strtol(pos, (char **) &pos, 10); if (errno || pos >= end || nntp->current_article < 0) return 0; /* Get <last-article> */ while (pos < end && !isdigit(*pos)) pos++; nntp->end_article = strtol(pos, (char **) &pos, 10); if (errno || pos >= end || nntp->end_article < 0) return 0; return 1;}enum nntp_codeget_nntp_response_code(struct connection *conn, struct read_buffer *rb){ struct nntp_connection_info *nntp = conn->info; unsigned char *line = rb->data; unsigned char *end = get_nntp_line_end(rb->data, rb->len); enum nntp_code code; int linelen; if (!end) return NNTP_CODE_NONE; /* Just to be safe NUL terminate the line */ end[-1] = 0; linelen = end - line; if (linelen < sizeof("xxx\r\n") - 1 || !isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]) || isdigit(line[3])) return NNTP_CODE_INVALID; code = atoi(line); if (!check_nntp_code_valid(code)) return NNTP_CODE_INVALID; /* Only when listing all articles in group the parameters is needed */ if (code == NNTP_CODE_211_GROUP_SELECTED && nntp->target == NNTP_TARGET_GROUP && !parse_nntp_group_parameters(nntp, line + 4, end)) return NNTP_CODE_INVALID; /* Remove the response line */ kill_buffer_data(rb, linelen); conn->received += linelen; return code;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -