AmendHub

Download:

jcs

/

detritus

/

amendments

/

19

finger: Add finger support


jcs made amendment 19 about 1 year ago
--- browser.c Sun Nov 3 11:23:08 2024 +++ browser.c Mon Nov 4 09:29:10 2024 @@ -36,10 +36,12 @@ static Pattern fill_pattern; extern struct uri_handler gemini_handler; extern struct uri_handler gopher_handler; +extern struct uri_handler finger_handler; struct uri_handler * uri_handlers[] = { &gemini_handler, &gopher_handler, + &finger_handler, }; bool browser_close(struct focusable *focusable); --- finger.c Mon Nov 4 10:47:04 2024 +++ finger.c Mon Nov 4 10:47:04 2024 @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2024 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "detritus.h" +#include "browser.h" + +#define FINGER_PORT 79 + +struct finger_request { + unsigned char tcp_buf[(4 * 1500) + 2048]; /* 4*MTU + tcp_input */ + + TCPiopb tcp_iopb; + StreamPtr tcp_stream; + wdsEntry tcp_wds[2]; + TCPStatusPB tcp_status_pb; + + char query[128]; + size_t query_len; +}; + +struct URI * finger_parse_uri(char *uristr); +bool finger_init_request(page_handle pageh); +bool finger_process(page_handle pageh); +void finger_cleanup(page_handle pageh); + +static bool parse_content(struct page *page); + +struct uri_handler finger_handler = { + finger_parse_uri, + finger_init_request, + finger_process, + finger_cleanup, +}; + +struct URI * +finger_parse_uri(char *uristr) +{ + return parse_uri(uristr, "finger"); +} + +bool +finger_init_request(page_handle pageh) +{ + struct page *page; + struct finger_request *req = NULL; + size_t len; + char ip_s[12 + 3 + 1]; + short err; + ip_addr ip, local_ip; + tcp_port port, local_port; + + HLock(pageh); + page = *pageh; + if (page->content_len) { + /* already fetched, nothing to do here but reset */ + page->content_pos = 0; + goto done; + } + + req = xmalloczero(sizeof(struct finger_request)); + if (req == NULL) { + warn("Out of memory"); + goto fail; + } + + if (page->uri->port == 0) + page->uri->port = FINGER_PORT; + + browser_statusf(page->browser, "Resolving \"%s\"...", + page->uri->hostname); + + err = DNSResolveName(page->uri->hostname, &ip, NULL); + if (err) { + browser_statusf(page->browser, "Error: Failed resolving: %d", err); + goto fail; + } + + long2ip(ip, (char *)&ip_s); + browser_statusf(page->browser, "Connecting to %s port %d...", ip_s, + page->uri->port); + + err = _TCPCreate(&req->tcp_iopb, &req->tcp_stream, (Ptr)req->tcp_buf, + sizeof(req->tcp_buf), NULL, NULL, NULL, false); + if (err) { + browser_statusf(page->browser, "Error: TCPCreate failed: %d", err); + goto fail; + } + + err = _TCPActiveOpen(&req->tcp_iopb, req->tcp_stream, ip, + page->uri->port, &local_ip, &local_port, NULL, NULL, false); + if (err) { + browser_statusf(page->browser, + "Error: Failed connecting to %s (%s) port %d: %d", + page->uri->hostname, ip_s, page->uri->port, err); + goto fail; + } + + err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb, + NULL, NULL, false); + if (err) { + browser_statusf(page->browser, + "Error: Failed TCPStatus on connection to %s (%s) port %d: %d", + page->uri->hostname, ip_s, page->uri->port, err); + goto fail; + } + + if (page->uri->path[0] == '\0' || + (page->uri->path[0] == '/' && page->uri->path[1] == '\0')) + req->query_len = snprintf(req->query, sizeof(req->query), + "\r\n", page->uri->hostname); + else + req->query_len = snprintf(req->query, sizeof(req->query), + "%s\r\n", page->uri->path + 1); + + browser_statusf(page->browser, "Connected to %s, sending request...", + page->uri->hostname); + + memset(&req->tcp_wds, 0, sizeof(req->tcp_wds)); + req->tcp_wds[0].ptr = (Ptr)req->query; + req->tcp_wds[0].length = req->query_len; + + err = _TCPSend(&req->tcp_iopb, req->tcp_stream, req->tcp_wds, NULL, + NULL, false); + if (err) { + browser_statusf(page->browser, "Error: TCPSend failed: %d", err); + goto fail; + } + + page->handler_cookie = req; + +done: + HUnlock(pageh); + browser_commit_to_page(page->browser, pageh); + + return true; + +fail: + if (req) { + if (req->tcp_stream) { + _TCPAbort(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false); + _TCPRelease(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false); + } + + xfree(&req); + page->handler_cookie = NULL; + } + + HUnlock(pageh); + return false; +} + +void +finger_cleanup(page_handle pageh) +{ + struct finger_request *req; + struct page *page; + + HLock(pageh); + page = *pageh; + req = (struct finger_request *)(page->handler_cookie); + + if (req) { + if (req->tcp_stream) { + _TCPAbort(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false); + _TCPRelease(&req->tcp_iopb, req->tcp_stream, NULL, NULL, false); + } + + xfree(&page->handler_cookie); + } + + HUnlock(pageh); +} + +bool +finger_process(page_handle pageh) +{ + struct finger_request *req; + struct page *page; + size_t size, len, n; + short err; + unsigned short slen; + bool ret; + + HLock(pageh); + page = *pageh; + + if (page->handler_cookie == NULL) + goto parse; + + req = (struct finger_request *)(page->handler_cookie); + + if (req->tcp_iopb.ioResult > 0 || CommandPeriodPressed()) { + BROWSER_DEBUGF((page->browser, "TCP I/O Result %d, disconnecting", + req->tcp_iopb.ioResult)); + goto finish_request; + } + + err = _TCPStatus(&req->tcp_iopb, req->tcp_stream, &req->tcp_status_pb, + NULL, NULL, false); + if (err) { + browser_statusf(page->browser, "Error: Bad TCPStatus: %d", err); + goto finish_request; + } + + if (req->tcp_status_pb.connectionState != + ConnectionStateEstablished) { + BROWSER_DEBUGF((page->browser, "TCP connection closed (state %d)", + req->tcp_status_pb.connectionState)); + goto finish_request; + } + if (req->tcp_status_pb.amtUnreadData == 0) + goto parse; + + if (page->content_len + req->tcp_status_pb.amtUnreadData >= + page->content_size) { + if (!browser_grow_page_content(pageh, + req->tcp_status_pb.amtUnreadData)) + return false; + HLock(pageh); + page = *pageh; + req = (struct finger_request *)(page->handler_cookie); + } + + slen = req->tcp_status_pb.amtUnreadData; + err = _TCPRcv(&req->tcp_iopb, req->tcp_stream, + (Ptr)(page->content + page->content_len), &slen, NULL, NULL, false); + if (err) { + browser_statusf(page->browser, "Error: Failed TCPRcv: %d", err); + goto finish_request; + } + page->content_len += slen; + browser_statusf(page->browser, "Read %ld bytes", page->content_len); + + goto parse; + +finish_request: + finger_cleanup(pageh); + +parse: + HLock(pageh); + page = *pageh; + + if (page->content_pos == page->content_len) { + if (!page->handler_cookie) { + /* nothing left to do */ + return false; + } + HUnlock(pageh); + return true; + } + + TVAutoCalc(page->browser->output_tv, false); + ret = parse_content(page); + TVAutoCalc(page->browser->output_tv, true); + TVCalcLines(page->browser->output_tv); + TVUpdate(page->browser->output_tv); + TVUpdateScrollbar(page->browser->output_tv, + page->browser->output_tv_scroller); + + if (ret) { + HUnlock(pageh); + return true; + } + + browser_statusf(page->browser, "Finished loading %ld bytes", + page->content_len); + + /* release some memory if we can */ + if (page->content_size > page->content_len) { + size = page->content_len; + page->content_size = size; + HUnlock(pageh); + SetHandleSize(pageh, sizeof(struct page) + size); + } + +request_fail: + HUnlock(pageh); + return false; +} + +static bool +parse_content(struct page *page) +{ + long n; + + page->browser->style = STYLE_PRE; + + /* text file, convert newlines and display */ + for (n = page->content_pos; n < page->content_len; n++) { + if (page->content[n] == '\r') { + if (n > page->content_pos) + browser_print(page->browser, + page->content + page->content_pos, + n - page->content_pos + 1); + page->content_pos = n + 1; + if (page->content[n + 1] == '\n') { + page->content_pos++; + n++; + } + } else if (page->content[n] == '\n') { + /* lone \n */ + if (n > page->content_pos) + browser_print(page->browser, + page->content + page->content_pos, + n - page->content_pos); + browser_print(page->browser, "\r", 1); + page->content_pos = n + 1; + } + } + + if (!page->handler_cookie && + page->content_pos < page->content_len) { + browser_print(page->browser, page->content + page->content_pos, + page->content_len - page->content_pos); + page->content_pos = page->content_len; + return false; + } + + if (page->handler_cookie) + return true; + + return false; +}