📄 gopher.c
字号:
return; } if (*name) { selector = strchr(name, ASCII_TAB); if (selector) { /* Terminate name */ *selector++ = '\0'; /* Gopher+ Type=0+ objects can be binary, and will have * 9 or 5 beginning their selector. Make sure we don't * trash the terminal by treating them as text. - FM */ if (entity == GOPHER_FILE && (*selector == GOPHER_BINARY || *selector == GOPHER_PCBINARY)) entity = *selector; } host = selector ? strchr(selector, ASCII_TAB) : NULL; if (host) { /* Terminate selector */ *host++ = '\0'; } port = host ? strchr(host, ASCII_TAB) : NULL; if (port) { unsigned char *end; int portno; errno = 0; portno = strtol(port + 1, (char **) &end, 10); if (errno || !uri_port_is_valid(portno)) { port = NULL; } else { /* Try to wipe out the default gopher port * number from being appended to links. */ if (portno == 70 && entity_needs_gopher_access(entity)) portno = 0; /* If the port number is 0 it means no port * number is needed in which case it can be * wiped out completely. Else append it to the * host string a la W3. */ if (portno == 0) { *port = '\0'; } else { *port = ':'; /* Chop port if there is junk after the * number */ *end = '\0'; } } } } /* Nameless files are separator lines */ if (entity == GOPHER_FILE) { int i = strlen(name) - 1; while (name[i] == ' ' && i >= 0) name[i--] = '\0'; if (i < 0) entity = GOPHER_INFO; } if (entity != GOPHER_INDEX) { add_gopher_description(buffer, entity); } switch (entity) { case GOPHER_WWW: /* Gopher pointer to W3 */ if (selector) { add_gopher_link(buffer, name, selector); break; } /* Fall through if no selector is defined so the * text is just displayed. */ case GOPHER_INFO: /* Information or separator line */ add_to_string(buffer, name); break; default: { struct string address; unsigned char *format = *selector ? "%s://%s@%s/" : "%s://%s%s/"; /* If port is defined it means that both @selector and @host * was correctly parsed. */ if (!port || !init_string(&address)) { /* Parse error: Bad menu item */ add_to_string(buffer, name); break; } assert(selector && host); if (entity == GOPHER_TELNET) { add_format_to_string(&address, format, "telnet", selector, host); } else if (entity == GOPHER_TN3270) { add_format_to_string(&address, format, "tn3270", selector, host); } else { add_format_to_string(&address, "gopher://%s/%c", host, entity); /* Encode selector string */ encode_selector_string(&address, selector); } /* Error response from Gopher doesn't deserve to * be a hyperlink. */ if (entity == GOPHER_INDEX) { add_gopher_search_field(buffer, name, address.source); } else if (address.length > 0 && strlcmp(address.source, address.length - 1, "gopher://error.host:1/", -1)) { add_gopher_link(buffer, name, address.source); } else { add_to_string(buffer, name); } done_string(&address); } } add_char_to_string(buffer, '\n');}/* Search for line ending \r\n pair */static unsigned char *get_gopher_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;}static inline unsigned char *check_gopher_last_line(unsigned char *line, unsigned char *end){ assert(line < end); /* Just to be safe NUL terminate the line */ end[-2] = 0; return line[0] == '.' && !line[1] ? NULL : line;}/* Parse a Gopher Menu document */static enum connection_stateread_gopher_directory_data(struct connection *conn, struct read_buffer *rb){ enum connection_state state = S_TRANS; struct string buffer; unsigned char *end; if (!init_string(&buffer)) return S_OUT_OF_MEM; if (conn->from == 0) { unsigned char *where = get_uri_string(conn->uri, URI_PUBLIC); if (where) decode_uri_for_display(where); add_format_to_string(&buffer, "<html>\n" "<head>\n" "<title>Gopher menu at %s</title>\n" "</head>\n" "<body>\n" "<h1>Gopher menu at %s</h1>\n" "<pre>", empty_string_or_(where), empty_string_or_(where)); mem_free_if(where); } while ((end = get_gopher_line_end(rb->data, rb->len))) { unsigned char *line = check_gopher_last_line(rb->data, end); /* Break on line with a dot by itself */ if (!line) { state = S_OK; break; } add_gopher_menu_line(&buffer, line); conn->received += end - rb->data; kill_buffer_data(rb, end - rb->data); } if (state != S_TRANS || rb->close == READ_BUFFER_END) add_to_string(&buffer, "</pre>\n" "</body>\n" "</html>\n"); add_fragment(conn->cached, conn->from, buffer.source, buffer.length); conn->from += buffer.length; done_string(&buffer); return state;}static struct cache_entry *init_gopher_cache_entry(struct connection *conn){ struct gopher_connection_info *gopher = conn->info; struct cache_entry *cached = get_cache_entry(conn->uri); if (!cached) return NULL; conn->cached = cached; if (!cached->content_type && gopher && gopher->entity && gopher->entity->content_type) { cached->content_type = stracpy(gopher->entity->content_type); if (!cached->content_type) return NULL; } return cached;}/* Display a Gopher Index document. */static enum connection_stateinit_gopher_index_cache_entry(struct connection *conn){ unsigned char *where; struct string buffer; if (!init_gopher_cache_entry(conn) || !init_string(&buffer)) return S_OUT_OF_MEM; where = get_uri_string(conn->uri, URI_PUBLIC); if (where) decode_uri_for_display(where); add_format_to_string(&buffer, "<html>\n" "<head>\n" "<title>Searchable gopher index at %s</title>\n" "</head>\n" "<body>\n" "<h1>Searchable gopher index at %s</h1>\n", empty_string_or_(where), empty_string_or_(where)); if (where) { add_gopher_search_field(&buffer, "Please enter search keywords", where); } mem_free_if(where); /* FIXME: I think this needs a form or something */ add_fragment(conn->cached, conn->from, buffer.source, buffer.length); conn->from += buffer.length; done_string(&buffer); conn->cached->content_type = stracpy("text/html"); return conn->cached->content_type ? S_OK : S_OUT_OF_MEM;}static voidread_gopher_response_data(struct connection *conn, struct read_buffer *rb){ struct gopher_connection_info *gopher = conn->info; enum connection_state state = S_TRANS; assert(gopher && gopher->entity); set_connection_timeout(conn); if (!conn->cached && !init_gopher_cache_entry(conn)) { end_gopher_connection(conn, S_OUT_OF_MEM); return; } /* Now read the data from the socket */ switch (gopher->entity->type) { case GOPHER_DIRECTORY: case GOPHER_INDEX: state = read_gopher_directory_data(conn, rb); break; case GOPHER_CSO:#if 0 /* FIXME: Merge CSO support */ state = read_gopher_cso_data(conn, rb);#endif state = S_GOPHER_CSO_ERROR; break; case GOPHER_SOUND: case GOPHER_PLUS_SOUND: case GOPHER_PLUS_MOVIE: case GOPHER_PLUS_PDF: case GOPHER_MACBINHEX: case GOPHER_PCBINARY: case GOPHER_UUENCODED: case GOPHER_BINARY: case GOPHER_FILE: case GOPHER_HTML: case GOPHER_CHTML: case GOPHER_GIF: case GOPHER_IMAGE: case GOPHER_PLUS_IMAGE: default: /* Add the received data as a new cache entry fragment and do * the connection data accounting. */ add_fragment(conn->cached, conn->from, rb->data, rb->len); conn->received += rb->len; conn->from += rb->len; kill_buffer_data(rb, rb->len); } /* Has the transport layer forced a shut down? */ if (rb->close == READ_BUFFER_END) { state = S_OK; } if (state != S_TRANS) { end_gopher_connection(conn, state); return; } read_from_socket(conn, &conn->socket, rb, read_gopher_response_data); set_connection_state(conn, S_TRANS);}static voidreceive_gopher_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, read_gopher_response_data);}static voidsend_gopher_command(struct connection *conn){ struct gopher_connection_info *gopher = conn->info; write_to_socket(conn, &conn->socket, gopher->command, gopher->commandlen, receive_gopher_response); set_connection_state(conn, S_SENT);}/* FIXME: No decoding of strange data types as yet. */voidgopher_protocol_handler(struct connection *conn){ struct uri *uri = conn->uri; enum connection_state state = S_CONN; switch (get_uri_port(uri)) { case 105: /* If it's a port 105 GOPHER_CSO gopher_entity with no ISINDEX * token ('?'), use the form-based CSO gateway (otherwise, * return an ISINDEX cover page or do the ISINDEX search). * - FM */ if (uri->datalen == 1 && *uri->data == GOPHER_CSO) { /* FIXME: redirect_cache() */ end_gopher_connection(conn, S_GOPHER_CSO_ERROR); } break; case 79:#if 0 /* This is outcommented because it apparently means that the * finger protocol handler needs to be extended for handling * this the way Lynx does. --jonas */ /* If it's a port 79/0[/...] URL, use the finger gateway. * - FM */ if (uri->datalen >= 1 && *uri->data == GOPHER_FILE) { /* FIXME: redirect_cache() */ end_gopher_connection(conn, S_OK); } break;#endif default: break; } state = init_gopher_connection_info(conn); if (state != S_CONN) { /* FIXME: Handle bad selector ... */ end_gopher_connection(conn, state); return; } /* Set up a socket to the server for the data */ set_connection_timeout(conn); conn->from = 0; make_connection(conn, &conn->socket, send_gopher_command);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -