jcs
/detritus
/amendments
/17
gopher: Pass type in URIs, add plain-text file displaying
jcs made amendment 17 about 1 year ago
--- gopher.c Sat Oct 26 20:34:01 2024
+++ gopher.c Tue Oct 29 11:56:09 2024
@@ -25,6 +25,7 @@
struct gopher_request {
struct browser *browser;
struct URI *uri;
+ char type;
unsigned char tcp_buf[(4 * 1500) + 2048]; /* 4*MTU + tcp_input */
char response[2048];
@@ -47,7 +48,7 @@ bool gopher_process_request(void *cookie);
void gopher_free(void *cookie);
static bool parse_response(struct gopher_request *req);
static void consume_response(struct gopher_request *req, size_t len);
-static void gopher_print(struct gopher_request *req, size_t len);
+static void gopher_print_menu(struct gopher_request *req, size_t len);
struct request_handler gopher_handler = {
gopher_parse_uri,
@@ -119,10 +120,23 @@ gopher_init_request(struct browser *browser, struct UR
goto fail;
}
- /* send selector without leading slash */
- req->message_len = snprintf(req->message, sizeof(req->message),
- "%s\r\n", req->uri->path[1] == '\0' ? "" : req->uri->path + 1);
-
+ /*
+ * If we have a path other than /, assume the first character is the
+ * type and should not be sent, per RFC 4266.
+ */
+ if (req->uri->path[0] == '\0' ||
+ (req->uri->path[0] == '/' && req->uri->path[1] == '\0')) {
+ req->type = '1';
+ req->message_len = snprintf(req->message, sizeof(req->message),
+ "\r\n");
+ } else {
+ /* /0/blah -> blah */
+ req->type = req->uri->path[1];
+ req->message_len = snprintf(req->message, sizeof(req->message),
+ "%s\r\n", req->uri->path + 2);
+ }
+
+ browser_commit_to_uri(browser, req->uri);
browser_statusf(browser, "Connected to %s, sending request...",
req->uri->hostname);
@@ -136,7 +150,7 @@ gopher_init_request(struct browser *browser, struct UR
browser_statusf(req->browser, "Error: TCPSend failed: %d", err);
goto fail;
}
-
+
return req;
fail:
@@ -172,7 +186,8 @@ gopher_process_request(void *cookie)
size_t len, n;
short err;
unsigned short slen;
-
+ bool ret;
+
if (req->tcp_iopb.ioResult > 0 || CommandPeriodPressed()) {
BROWSER_DEBUGF((req->browser, "TCP I/O Result %d, disconnecting",
req->tcp_iopb.ioResult));
@@ -221,9 +236,20 @@ gopher_process_request(void *cookie)
}
parse:
- if (req->response_len)
- return parse_response(req);
-
+ if (req->response_len) {
+ TVAutoCalc(req->browser->output_tv, false);
+ ret = parse_response(req);
+ TVAutoCalc(req->browser->output_tv, true);
+ TVCalcLines(req->browser->output_tv);
+ TVUpdate(req->browser->output_tv);
+ TVUpdateScrollbar(req->browser->output_tv,
+ req->browser->output_tv_scroller);
+ if (ret == false)
+ browser_statusf(req->browser,
+ "Finished parsing %ld bytes", req->total_response_len);
+ return ret;
+ }
+
return true;
}
@@ -232,23 +258,69 @@ parse_response(struct gopher_request *req)
{
long n;
-restart_parse:
- for (n = 0; n < req->response_len; n++) {
- if (n + 3 <= req->response_len && req->response[0] == '.' &&
- req->response[1] == '\r' && req->response[2] == '\n') {
- /* end of transfer */
- browser_statusf(req->browser,
- "Finished reading %ld bytes", req->total_response_len);
- return false;
+ if (req->type == '1') {
+ /* gopher menu */
+parse_menu:
+ for (n = 0; n < req->response_len; n++) {
+ if (n > 0 && req->response[n - 1] == '\r' &&
+ req->response[n] == '\n') {
+ /* print line */
+ req->response[n - 1] = '\0';
+ if (req->response[0] == '.' && n == 2 &&
+ req->response_len - n == 1)
+ /* ".\r\n" is end of transfer */
+ req->tcp_done_reading = true;
+ else
+ gopher_print_menu(req, n + 1);
+ consume_response(req, n + 1);
+ goto parse_menu;
+ }
+
+ /* otherwise leave data in the buffer */
}
-
- if (n > 1 && req->response[n - 1] == '\r' &&
- req->response[n] == '\n') {
- req->response[n - 1] = '\0';
- gopher_print(req, n + 1);
- consume_response(req, n + 1);
- goto restart_parse;
+ if (req->response_len == sizeof(req->response)) {
+ /* just dump it so we can move on */
+ gopher_print_menu(req, req->response_len);
+ consume_response(req, req->response_len);
}
+ } else if (req->type == '0') {
+ /* text file, convert newlines and display */
+find_lines:
+ for (n = 0; n < req->response_len; n++) {
+ if (n > 0 && req->response[n - 1] == '\r' &&
+ req->response[n] == '\n') {
+ /* \r\n -> \r */
+ if (req->response[0] == '.' && n == 2 &&
+ req->response_len - n == 1)
+ /* ".\r\n" is end of transfer */
+ req->tcp_done_reading = true;
+ else
+ browser_print(req->browser, req->response, n);
+ consume_response(req, n + 1);
+ goto find_lines;
+ } else if (n > 0 && req->response[n - 1] == '\r') {
+ /* lone \r */
+ browser_print(req->browser, req->response, n);
+ consume_response(req, n);
+ goto find_lines;
+ } else if (n > 0 && req->response[n] == '\n') {
+ /* lone \n */
+ req->response[n] = '\r';
+ browser_print(req->browser, req->response, n);
+ consume_response(req, n);
+ goto find_lines;
+ }
+ /* otherwise leave data in the buffer */
+ }
+ if (req->response_len == sizeof(req->response)) {
+ /* gosh this is one long line */
+ browser_print(req->browser, req->response, req->response_len);
+ consume_response(req, req->response_len);
+ }
+ } else {
+ /* anything else, just download it */
+ browser_print(req->browser, req->response, req->response_len);
+ consume_response(req, req->response_len);
}
return true;
@@ -270,7 +342,7 @@ consume_response(struct gopher_request *req, size_t le
}
static void
-gopher_print(struct gopher_request *req, size_t len)
+gopher_print_menu(struct gopher_request *req, size_t len)
{
static char uri[member_size(struct URI, str)], prefix[5];
char type, *label, *selector = NULL, *hostname = NULL;
@@ -312,8 +384,8 @@ gopher_print(struct gopher_request *req, size_t len)
snprintf(prefix, sizeof(prefix), "[%c] ", type);
browser_print(req->browser, prefix, 4);
- len = snprintf(uri, sizeof(uri), "gopher://%s%s", hostname,
- selector);
+ len = snprintf(uri, sizeof(uri), "gopher://%s/%c%s", hostname,
+ type, selector);
browser_print_link(req->browser, uri, len, label, strlen(label));
browser_print(req->browser, "\r", 1);
break;