jcs
/detritus
/amendments
/8
browser: Switch to TextView for main output, fix a few TLS bugs
When the remote server disconnects, we may still have data in the TLS
engine to read and parse.
jcs made amendment 8 about 1 year ago
--- browser.c Tue Oct 1 20:15:34 2024
+++ browser.c Tue Oct 22 17:37:20 2024
@@ -42,8 +42,6 @@ void browser_mouse_down(struct focusable *focusable, E
bool browser_handle_menu(struct focusable *focusable, short menu,
short item);
void browser_atexit(struct focusable *focusable);
-bool browser_avoid_te_overflow(struct browser *browser, TEHandle te,
- short line_height);
void browser_cleanup(struct browser *browser);
void browser_connect(struct browser *browser);
@@ -66,12 +64,14 @@ browser_idle(struct focusable *focusable, EventRecord
struct browser *browser = (struct browser *)focusable->cookie;
size_t len;
- TEIdle(browser->active_te);
+ TEIdle(browser->uri_te);
if (!browser->req)
return;
switch (browser->req->state) {
+ case REQ_STATE_DISCONNECTED:
+ break;
case REQ_STATE_IDLE:
break;
case REQ_STATE_CONNECTED:
@@ -87,10 +87,8 @@ browser_init(void)
char title[64];
struct browser *browser;
struct focusable *focusable;
- Rect bounds = { 0 }, te_bounds = { 0 };
+ Rect bounds, te_bounds, padding;
Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */
- Point cell_size = { 0, 0 };
- Cell cell = { 0 };
short n, width, height;
browser = xmalloczero(sizeof(struct browser));
@@ -138,40 +136,35 @@ browser_init(void)
panic("Out of memory allocating TE");
TEAutoView(false, browser->uri_te);
TEActivate(browser->uri_te);
- browser->active_te = browser->uri_te;
bounds.left = bounds.right + PADDING;
bounds.right = browser->win->portRect.right - PADDING;
browser->go_button = NewControl(browser->win, &bounds, "\pGo", true,
1, 1, 1, pushButProc, 0L);
- /* output TE */
+ /* output TV */
bounds.left = PADDING;
bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH - PADDING;
bounds.top = bounds.bottom + PADDING;
bounds.bottom = browser->win->portRect.bottom - PADDING;
- te_bounds = bounds;
- InsetRect(&te_bounds, 2, 2);
- browser->output_te = TEStylNew(&te_bounds, &bounds);
- if (browser->output_te == NULL)
- panic("Out of memory allocating TE");
- TEAutoView(false, browser->output_te);
- (*(browser->output_te))->caretHook = NullCaretHook;
+ SetRect(&padding, 2, 2, 2, 2);
+ browser->output_tv = TVNew(&bounds, &padding);
+ if (browser->output_tv == NULL)
+ panic("Out of memory allocating TV");
/* scrollbar for output text */
bounds.right = browser->win->portRect.right - PADDING;
bounds.left = bounds.right - SCROLLBAR_WIDTH;
bounds.bottom++;
bounds.top--;
- browser->output_te_scroller = NewControl(browser->win, &bounds, "\p",
+ browser->output_tv_scroller = NewControl(browser->win, &bounds, "\p",
true, 1, 1, 1, scrollBarProc, 0L);
browser_update_menu(browser);
- UpdateScrollbarForTE(browser->win, browser->output_te_scroller,
- browser->output_te, true);
+ TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
- TESetText("geminiprotocol.net", strlen("geminiprotocol.net"),
- browser->uri_te);
+ TESetText("gemini://geminiprotocol.net/",
+ strlen("gemini://geminiprotocol.net/"), browser->uri_te);
focusable = xmalloczero(sizeof(struct focusable));
if (focusable == NULL)
@@ -196,7 +189,7 @@ browser_close(struct focusable *focusable)
struct browser *browser = (struct browser *)focusable->cookie;
TEDispose(browser->uri_te);
- TEDispose(browser->output_te);
+ TVDispose(browser->output_tv);
DisposeWindow(browser->win);
browser_cleanup(browser);
@@ -302,18 +295,16 @@ browser_update(struct focusable *focusable, EventRecor
InsetRect(&r, -1, -1);
FrameRect(&r);
- HLock(browser->output_te);
- r = (*(browser->output_te))->viewRect;
- HUnlock(browser->output_te);
- FillRect(&r, white);
- TEUpdate(&r, browser->output_te);
+ HLock(browser->output_tv);
+ r = (*(browser->output_tv))->view;
+ HUnlock(browser->output_tv);
+ TVUpdate(browser->output_tv);
InsetRect(&r, -1, -1);
FrameRect(&r);
UpdtControl(browser->win, browser->win->visRgn);
browser_reveal_shadow(browser);
-
browser_update_menu(browser);
break;
@@ -361,25 +352,20 @@ browser_mouse_down(struct focusable *focusable, EventR
r = (*(browser->uri_te))->viewRect;
HUnlock(browser->uri_te);
if (PtInRect(p, &r)) {
- if (browser->active_te != browser->uri_te) {
- TEDeactivate(browser->active_te);
- browser->active_te = browser->uri_te;
- TEActivate(browser->uri_te);
- }
TEClick(p, ((event->modifiers & shiftKey) != 0), browser->uri_te);
browser_update_menu(browser);
return;
}
- HLock(browser->output_te);
- r = (*(browser->output_te))->viewRect;
- HUnlock(browser->output_te);
+ HLock(browser->output_tv);
+ r = (*(browser->output_tv))->view;
+ HUnlock(browser->output_tv);
if (PtInRect(p, &r)) {
- TEClick(p, ((event->modifiers & shiftKey) != 0),
- browser->output_te);
+ TVClick(browser->output_tv, p,
+ ((event->modifiers & shiftKey) != 0));
if (browser->req && browser->req->links) {
- off = TEGetOffset(p, browser->output_te);
+ off = TVGetOffset(browser->output_tv, p);
for (n = 0; n < browser->req->links_count; n++) {
link = &browser->req->links[n];
if ((link->pos <= off) && (off < link->pos + link->len)) {
@@ -406,10 +392,10 @@ browser_mouse_down(struct focusable *focusable, EventR
case inDownButton:
case inPageUp:
case inPageDown:
- if (control != browser->output_te_scroller)
+ if (control != browser->output_tv_scroller)
break;
- SetTrackControlTE(browser->output_te);
- TrackControl(control, p, TrackMouseDownInControl);
+ TVSetTrackScrollControl(browser->output_tv);
+ TrackControl(control, p, TVTrackScrollControl);
break;
case inThumb:
val = GetCtlValue(control);
@@ -418,10 +404,8 @@ browser_mouse_down(struct focusable *focusable, EventR
adj = val - GetCtlValue(control);
if (adj != 0) {
val -= adj;
- if (control == browser->output_te_scroller) {
- TEScroll(0, adj * TEGetHeight(0, 0,
- browser->output_te), browser->output_te);
- }
+ if (control == browser->output_tv_scroller)
+ TVScroll(browser->output_tv, 0, adj);
SetCtlValue(control, val);
}
break;
@@ -454,10 +438,10 @@ browser_handle_menu(struct focusable *focusable, short
case EDIT_MENU_ID:
switch (item) {
case EDIT_MENU_COPY_ID:
- TECopy(browser->active_te);
+ TECopy(browser->uri_te);
return true;
case EDIT_MENU_SELECT_ALL_ID:
- TESetSelect(0, 1024 * 32, browser->active_te);
+ TESetSelect(0, 1024 * 32, browser->uri_te);
return true;
}
break;
@@ -502,6 +486,7 @@ browser_connect(struct browser *browser)
/* TODO: support URIs with port? */
browser->req->port = DEFAULT_GEMINI_PORT;
+ browser->req->uri_len = strlen(browser->req->uri);
if (count = 0, sscanf(browser->req->uri, "gemini://%255[^/]/%*s%n",
&browser->req->hostname, &count) == 1 && count > 10) {
@@ -537,20 +522,18 @@ browser_connect(struct browser *browser)
return;
}
- TESetText(browser->req->uri, browser->req->uri_len, browser->uri_te);
HLock(browser->uri_te);
r = (*(browser->uri_te))->viewRect;
HUnlock(browser->uri_te);
+ TESetText(browser->req->uri, browser->req->uri_len, browser->uri_te);
TESetSelect(SHRT_MAX, SHRT_MAX, browser->uri_te);
TEUpdate(&r, browser->uri_te);
- TEDeactivate(browser->uri_te);
- browser->active_te = browser->output_te;
browser->req->uri_len = strlen(browser->req->uri);
memcpy(browser->req->message, browser->req->uri, browser->req->uri_len);
browser->req->message[browser->req->uri_len] = '\r';
- browser->req->message[browser->req->uri_len] = '\n';
+ browser->req->message[browser->req->uri_len + 1] = '\n';
browser->req->message_len = browser->req->uri_len + 2;
if (browser->req->host_ip == 0) {
@@ -628,9 +611,11 @@ browser_shuffle_data(struct browser *browser)
short err, status;
short cipherspace, plainspace, error;
- if (browser->req->tcp_iopb.ioResult > 0) {
+ if (browser->req->tcp_iopb.ioResult > 0 || CommandPeriodPressed()) {
progress(NULL);
browser->req->state = REQ_STATE_DISCONNECTED;
+ browser_debugf(browser, "[TCP IO Result %d, disconnecting]\r",
+ browser->req->tcp_iopb.ioResult);
return;
}
@@ -641,8 +626,9 @@ browser_shuffle_data(struct browser *browser)
if (status & 0x1) {
/* closed */
progress(NULL);
- browser_printf(browser, "[TLS handshake failed: %d, 0x%x]\r",
- error, status);
+ if (error != 0)
+ browser_printf(browser, "[TLS handshake failed: %d, 0x%x]\r",
+ error, status);
browser->req->state = REQ_STATE_DISCONNECTED;
break;
}
@@ -696,6 +682,9 @@ shuffle_read_tls_ciphertext(struct browser *browser)
browser_debugf(browser, "[Read %lu bytes of ciphertext from TLS, "
"forwarding to TCP]\r", len);
+ if (browser->req->tcp_done_reading)
+ return;
+
memset(&browser->req->tcp_wds, 0, sizeof(browser->req->tcp_wds));
browser->req->tcp_wds[0].ptr = (Ptr)browser->scsi_buf;
browser->req->tcp_wds[0].length = len;
@@ -718,7 +707,8 @@ shuffle_read_tcp_ciphertext(struct browser *browser, s
unsigned short slen;
/* read ciphertext from TCP and send it to TLS */
- if (browser->req->tcp_input_len < sizeof(browser->req->tcp_input)) {
+ if (browser->req->tcp_input_len < sizeof(browser->req->tcp_input) &&
+ !browser->req->tcp_done_reading) {
err = _TCPStatus(&browser->req->tcp_iopb, browser->req->tcp_stream,
&browser->req->tcp_status_pb, NULL, NULL, false);
if (browser->req->tcp_status_pb.amtUnreadData > 0) {
@@ -743,11 +733,15 @@ shuffle_read_tcp_ciphertext(struct browser *browser, s
"tcp_input]\r");
}
}
+
if (err) {
- progress(NULL);
- browser_printf(browser, "[Failed TCPStatus: %d]\r", err);
- browser->req->state = REQ_STATE_DISCONNECTED;
- return;
+ browser->req->tcp_done_reading = true;
+ browser_printf(browser, "[Bad TCPStatus: %d]\r", err);
+ } else if (browser->req->tcp_status_pb.connectionState !=
+ ConnectionStateEstablished) {
+ browser_debugf(browser, "[TCP connection closed (state %d)]\r",
+ browser->req->tcp_status_pb.connectionState);
+ browser->req->tcp_done_reading = true;
}
}
@@ -765,12 +759,9 @@ shuffle_read_tcp_ciphertext(struct browser *browser, s
else {
size_t n;
- /* TODO: why does memmove fail? */
- //memmove(browser->req->readbuf, browser->req->readbuf + len,
- // browser->req->readbuflen - len);
- for (n = 0; n < browser->req->tcp_input_len - len; n++)
- browser->req->tcp_input[n] =
- browser->req->tcp_input[len + n];
+ memmove(browser->req->tcp_input,
+ browser->req->tcp_input + len,
+ browser->req->tcp_input_len - len);
browser->req->tcp_input_len -= len;
browser_debugf(browser, "[Wrote %ld bytes of "
@@ -893,64 +884,31 @@ browser_debugf(struct browser *browser, const char *fo
size_t
browser_print(struct browser *browser, const char *str, size_t len)
{
- StScrpRec *scrp_rec;
- ScrpSTElement *scrp_ele;
+ struct TVStyle style;
struct browser_link *link;
- short line_height;
size_t n;
- unsigned long style = STYLE_NONE;
- if (browser->req && browser->req->style)
- style = browser->req->style;
-
- line_height = BROWSER_FONT_SIZE + 3;
-
- browser_avoid_te_overflow(browser, browser->output_te, line_height);
+ style.font = BROWSER_FONT;
+ style.size = BROWSER_FONT_SIZE;
+ style.style = 0;
- if (browser->scrp_rec_h == NULL) {
- browser->scrp_rec_h = xNewHandle(sizeof(short) +
- (sizeof(ScrpSTElement) * BROWSER_SCRAP_ELEMENTS));
- HLock(browser->scrp_rec_h);
- memset(*(browser->scrp_rec_h), 0,
- GetHandleSize(browser->scrp_rec_h));
- } else {
- HLock(browser->scrp_rec_h);
- }
-
- scrp_rec = (StScrpRec *)(*(browser->scrp_rec_h));
- scrp_rec->scrpNStyles = 1;
- scrp_ele = &scrp_rec->scrpStyleTab[0];
- scrp_ele->scrpHeight = line_height;
- scrp_ele->scrpAscent = BROWSER_FONT_SIZE;
- scrp_ele->scrpFont = BROWSER_FONT;
- scrp_ele->scrpSize = BROWSER_FONT_SIZE;
- scrp_ele->scrpFace = 0;
-
- if (style & STYLE_BOLD)
- scrp_ele->scrpFace |= bold | condense;
- if (style & (STYLE_H1 | STYLE_H2 | STYLE_H3))
- scrp_ele->scrpFace |= bold;
- if (style & STYLE_ITALIC)
- scrp_ele->scrpFace |= italic;
- if (style & STYLE_LINK)
- scrp_ele->scrpFace |= underline;
- if (style & STYLE_H1) {
- scrp_ele->scrpSize += 8;
- scrp_ele->scrpHeight += 10;
- scrp_ele->scrpAscent += 8;
- } else if (style & STYLE_H2) {
- scrp_ele->scrpSize += 4;
- scrp_ele->scrpHeight += 6;
- scrp_ele->scrpAscent += 4;
- } else if (style & STYLE_H3) {
- scrp_ele->scrpSize += 2;
- scrp_ele->scrpHeight += 4;
- scrp_ele->scrpAscent += 2;
- }
-
- TESetSelect(SHRT_MAX, SHRT_MAX, browser->output_te);
+ if (browser->req->style & STYLE_BOLD)
+ style.style |= bold | condense;
+ if (browser->req->style & (STYLE_H1 | STYLE_H2 | STYLE_H3))
+ style.style |= bold;
+ if (browser->req->style & STYLE_ITALIC)
+ style.style |= italic;
+ if (browser->req->style & STYLE_LINK)
+ style.style |= underline;
- if (style & STYLE_LINK) {
+ if (browser->req->style & STYLE_H1)
+ style.size += 8;
+ else if (browser->req->style & STYLE_H2)
+ style.size += 4;
+ else if (browser->req->style & STYLE_H3)
+ style.size += 2;
+
+ if (browser->req->style & STYLE_LINK) {
if (browser->req->links_count == browser->req->links_size) {
browser->req->links_size += BROWSER_LINKS_CHUNK_SIZE;
browser->req->links = xreallocarray(browser->req->links,
@@ -962,13 +920,13 @@ browser_print(struct browser *browser, const char *str
memset(&browser->req->links[browser->req->links_count], 0,
sizeof(struct browser_link) * BROWSER_LINKS_CHUNK_SIZE);
}
-
+
link = &browser->req->links[browser->req->links_count++];
+
+ HLock(browser->output_tv);
+ link->pos = (*(browser->output_tv))->text_length;
+ HUnlock(browser->output_tv);
- HLock(browser->output_te);
- link->pos = (*(browser->output_te))->teLength;
- HUnlock(browser->output_te);
-
/* [<whitespace>]<URL>[<whitespace><title>] */
/* eat leading whitespace */
@@ -1018,94 +976,32 @@ browser_print(struct browser *browser, const char *str
link->title[len - 1] = '\0';
link->len = len - 1;
- TEStylInsert(link->title, len - 1, browser->scrp_rec_h,
- browser->output_te);
+ TVAppend(browser->output_tv, &style, link->title, len - 1);
str += len - 1;
len = 1;
} else
- TEStylInsert(link->link, len, browser->scrp_rec_h,
- browser->output_te);
+ TVAppend(browser->output_tv, &style, (char *)str, len);
- style &= ~(STYLE_LINK);
+ browser->req->style &= ~(STYLE_LINK);
}
if (str[len - 1] == '\r' &&
- (style & (STYLE_H1 | STYLE_H2 | STYLE_H3))) {
+ (browser->req->style & (STYLE_H1 | STYLE_H2 | STYLE_H3))) {
/* print newlines in a small size */
- TEStylInsert(str, len - 1, browser->scrp_rec_h, browser->output_te);
- scrp_ele->scrpHeight = line_height;
- scrp_ele->scrpAscent = BROWSER_FONT_SIZE;
- scrp_ele->scrpFont = BROWSER_FONT;
- scrp_ele->scrpSize = BROWSER_FONT_SIZE;
- TEStylInsert("\r", 1, browser->scrp_rec_h, browser->output_te);
+ TVAppend(browser->output_tv, &style, (char *)str, len - 1);
+ style.font = BROWSER_FONT;
+ style.size = BROWSER_FONT_SIZE;
+ style.style = 0;
+ TVAppend(browser->output_tv, &style, "\r", 1);
} else
- TEStylInsert(str, len, browser->scrp_rec_h, browser->output_te);
+ TVAppend(browser->output_tv, &style, (char *)str, len);
+
+ TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
- HUnlock(browser->scrp_rec_h);
-
-// if (was_len == 0) {
-// SetCtlValue(browser->output_te_scroller,
-// GetCtlMin(browser->output_te_scroller));
-// }
- UpdateScrollbarForTE(browser->win, browser->output_te_scroller,
- browser->output_te, false);
-
- HUnlock(browser->output_te);
-
return len;
}
-bool
-browser_avoid_te_overflow(struct browser *browser, TEHandle te,
- short line_height)
-{
- RgnHandle savergn;
- Rect zerorect = { 0, 0, 0, 0 };
-
- HLock(te);
-
- /* too many lines */
- if ((*te)->nLines >= (nitems((*te)->lineStarts) - 10))
- goto te_overflow;
-
- /* too many characters */
- if ((*te)->teLength >= (SHRT_MAX - 500))
- goto te_overflow;
-
- /* rect of all lines is too tall */
- if ((*te)->nLines * line_height >= (SHRT_MAX - 100))
- goto te_overflow;
-
- HUnlock(te);
-
- return false;
-
-te_overflow:
- savergn = NewRgn();
- GetClip(savergn);
- /* create an empty clip region so all TE updates are hidden */
- ClipRect(&zerorect);
-
- /* select some lines at the start, delete them */
- TESetSelect(0, (*te)->lineStarts[5], te);
- TEDelete(te);
-
- /* scroll up, causing a repaint */
- TEPinScroll(0, INT_MAX, te);
-
- /* then scroll back down to what it looked like before we did anything */
- TEPinScroll(0, -INT_MAX, te);
-
- /* resume normal drawing */
- SetClip(savergn);
- DisposeRgn(savergn);
-
- HUnlock(te);
-
- return true;
-}
-
void
browser_clear(struct browser *browser)
{
@@ -1114,12 +1010,9 @@ browser_clear(struct browser *browser)
GetPort(&win);
SetPort(browser->win);
- EraseRect(&(*(browser->output_te))->viewRect);
- TESetText("", 0, browser->output_te);
+ TVClear(browser->output_tv);
+ TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
- UpdateScrollbarForTE(browser->win, browser->output_te_scroller,
- browser->output_te, true);
-
SetPort(win);
}
@@ -1144,6 +1037,8 @@ handle_state:
browser->req->response[n - 1] = '\0';
if (!browser_parse_header(browser, browser->req->response)) {
+ browser_debugf(browser, "[Header parsing failed, "
+ "disconnecting]\r");
browser->req->state = REQ_STATE_DISCONNECTED;
return;
}
@@ -1176,13 +1071,14 @@ restart_parse:
*/
break;
}
-
+
if (!(n == browser->req->response_len ||
browser->req->response[n] == '\n'))
continue;
len = n + 1;
trail = 0;
+ skip = 0;
if (n < browser->req->response_len &&
browser->req->response[n] == '\n') {
@@ -1191,8 +1087,21 @@ restart_parse:
len--;
trail = 1;
}
+ } else if (browser->req->response_len <
+ sizeof(browser->req->response) &&
+ !browser->req->tcp_done_reading) {
+ /*
+ * No newline found at the end of the buffer, but the
+ * buffer isn't full so wait for more data.
+ */
+ break;
}
+ /*
+ * If we didn't find a newline but we're at the end of the
+ * buffer, shift it down first to see if we can fit the whole
+ * line in the buffer at once */
+
if (browser->req->response[0] == '=' &&
browser->req->response[1] == '>') {
/* link */
@@ -1315,12 +1224,15 @@ restart_parse:
void
browser_consume_response(struct browser *browser, size_t len)
{
- if (len == browser->req->response_len)
+ if (len == browser->req->response_len) {
browser->req->response_len = 0;
- else {
+ memset(browser->req->response, 0, sizeof(browser->req->response));
+ } else {
memmove(browser->req->response, browser->req->response + len,
sizeof(browser->req->response) - len);
browser->req->response_len -= len;
+ memset(browser->req->response + browser->req->response_len, 0,
+ sizeof(browser->req->response) - browser->req->response_len);
}
}
@@ -1329,7 +1241,7 @@ browser_parse_header(struct browser *browser, char *st
{
short status;
- browser_debugf(browser, "[Received header: %d]\r", str);
+ browser_debugf(browser, "[Received header: %s]\r", str);
if (!(str[0] >= '0' && str[0] <= '9' &&
str[1] >= '0' && str[1] <= '9' && str[2] == ' ')) {
browser_printf(browser, "[Malformed response %s]\r", str);
@@ -1408,12 +1320,12 @@ browser_click_link(struct browser *browser, struct bro
}
TESetText(new_uri, len, browser->uri_te);
+ TESetSelect(SHRT_MAX, SHRT_MAX, browser->uri_te);
HLock(browser->uri_te);
r = (*(browser->uri_te))->viewRect;
HUnlock(browser->uri_te);
- TESetSelect(SHRT_MAX, SHRT_MAX, browser->uri_te);
TEUpdate(&r, browser->uri_te);
- //browser_connect(browser);
+ browser_connect(browser);
}
\ No newline at end of file
--- browser.h Tue Oct 1 20:08:53 2024
+++ browser.h Tue Oct 22 15:29:10 2024
@@ -18,6 +18,7 @@
#define __BROWSER_H__
#include <stdlib.h>
+#include "textview.h"
#include "tcp.h"
#include "util.h"
@@ -69,6 +70,7 @@ struct tcp_request {
StreamPtr tcp_stream;
wdsEntry tcp_wds[2];
TCPStatusPB tcp_status_pb;
+ bool tcp_done_reading;
char uri[1024 + 1];
size_t uri_len;
@@ -94,11 +96,8 @@ struct browser {
BitMap shadow;
TEHandle uri_te;
ControlHandle go_button;
- TEHandle output_te;
- ControlHandle output_te_scroller;
- Handle scrp_rec_h;
-#define BROWSER_SCRAP_ELEMENTS 20
- TEHandle active_te;
+ TVHandle output_tv;
+ ControlHandle output_tv_scroller;
struct tcp_request *req;
unsigned char scsi_buf[2048];