From 2462ed38979e9ec1bb3a89d207cf5f9feb010ffc Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Sat, 18 Apr 2026 13:29:50 +0200 Subject: [PATCH 1/4] feat: HTTPS support via AmiSSL + redirect following MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire TLS into the test-program image hook so https:// URLs and http->https 302 redirects resolve end-to-end. - Makefile downloads the AmiSSL 5.27 SDK on demand (lha xq) and exposes the headers only; inline macros in dispatch through a task-local AmiSSLBase, so no stub lib link and no global symbols are required. Enabled for OS3 and OS4 (MorphOS stays plain HTTP). - test_image_hook.h: THL_State gains AmiSSL library bases, SSL context and connection, plus a sni_errno slot for AmiSSL_ErrNoPtr. THL_Recv/THL_Send dispatch via SSL_read/SSL_write when use_tls is set, with function-scope shadowed library bases for inline macros. - URL parsing generalised: THL_ParseUrl replaces THL_ParseHttp and accepts both http and https (default ports 80/443). - THL_Connect factors out TCP connect plus optional TLS wrap; THL_TlsWrap opens amisslmaster + amissl, InitAmiSSLMaster / OpenAmiSSL / InitAmiSSLA (A-suffixed because OS3 builds with -DNO_INLINE_STDARG), then SSL_CTX_new(TLS_client_method), SSL_new, SSL_set_fd, SSL_set_tlsext_host_name, SSL_connect. VERIFY_NONE for now — CA bundle wiring is a follow-up. - THL_DoRequest sends the GET and parses status/headers; returns 1 OK / 0 error / -1 redirect. THL_HttpOpen loops up to 5 hops so http://www.amigaworld.net/images/awn2.gif follows its 302 to HTTPS transparently. - SimpleTest.c / LibLoad_Test.c: HTML adds a direct-HTTPS test entry (https://aminet.net/pics/aminet.png) alongside the existing HTTP entries. - .gitignore: exclude the downloaded mcc/amissl_sdk/ tree. --- .gitignore | 1 + mcc/LibLoad_Test.c | 7 +- mcc/Makefile | 62 ++++- mcc/SimpleTest.c | 7 +- mcc/test_image_hook.h | 633 ++++++++++++++++++++++++++++-------------- 5 files changed, 497 insertions(+), 213 deletions(-) diff --git a/.gitignore b/.gitignore index 6a32990..76c1e67 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ bin_* mcp/locale.* MEMORY.md .DS_Store +mcc/amissl_sdk/ diff --git a/mcc/LibLoad_Test.c b/mcc/LibLoad_Test.c index aa0df24..3d875ca 100644 --- a/mcc/LibLoad_Test.c +++ b/mcc/LibLoad_Test.c @@ -52,7 +52,12 @@ static const char *test_html = "

Images - Network (HTTP)

" "

Network images require bsdsocket.library:

" "

\"Aminet

" - "

\"AmigaWorld

" + "

Exercises http->https redirect (requires AmiSSL):

" + "

\"AmigaWorld

" + + "

Images - Network (HTTPS)

" + "

Direct TLS fetch. Needs amisslmaster.library + amissl.library installed:

" + "

\"Aminet

" "

Text

" "

Bold, italic, under, strike, tt

" diff --git a/mcc/Makefile b/mcc/Makefile index f1ed190..b2b5f54 100644 --- a/mcc/Makefile +++ b/mcc/Makefile @@ -152,6 +152,11 @@ endif LDFLAGS = $(CPU) $(DEBUGSYM) LDLIBS = +# Per-program extras; populated by the AmiSSL block below. +TEST_CFLAGS = +TEST_LDLIBS = +TEST_DEPS = + # different options per target OS ifeq ($(OS), os4) @@ -241,6 +246,45 @@ endif endif endif +########################################################################### +# AmiSSL SDK — enables HTTPS in SimpleTest / LibLoad_Test. +# +# The SDK is downloaded from jens-maus/amissl on first build. Pass +# USE_AMISSL=0 to disable; the test programs then speak plain HTTP only. +# No SDK is distributed for MorphOS, so USE_AMISSL is forced off there. + +AMISSL_VERSION ?= 5.27 +AMISSL_SDK_DIR ?= amissl_sdk +AMISSL_SDK_READY = $(AMISSL_SDK_DIR)/.ready +AMISSL_INC = $(AMISSL_SDK_DIR)/AmiSSL/Developer/include +AMISSL_URL = https://github.com/jens-maus/amissl/releases/download/$(AMISSL_VERSION)/AmiSSL-$(AMISSL_VERSION)-SDK.lha + +ifeq ($(OS), os3) + AMISSL_LIBDIR = $(AMISSL_SDK_DIR)/AmiSSL/Developer/lib/AmigaOS3 + USE_AMISSL ?= 1 +else ifeq ($(OS), os4) + AMISSL_LIBDIR = $(AMISSL_SDK_DIR)/AmiSSL/Developer/lib/AmigaOS4/newlib + USE_AMISSL ?= 1 +else + USE_AMISSL = 0 +endif + +ifeq ($(USE_AMISSL), 1) + # Headers only: the inline macros in dispatch through + # a task-local library base, so no stub lib (libamisslstubs.a) is needed + # and there are no global AmiSSLBase / AmiSSLMasterBase symbols to + # satisfy at link time. + TEST_CFLAGS += -DHAVE_AMISSL -I$(AMISSL_INC) + TEST_DEPS += $(AMISSL_SDK_READY) +endif + +$(AMISSL_SDK_READY): + @echo " GET AmiSSL SDK $(AMISSL_VERSION)" + @mkdir -p $(AMISSL_SDK_DIR) + @cd $(AMISSL_SDK_DIR) && curl -fsSL -o sdk.lha "$(AMISSL_URL)" + @cd $(AMISSL_SDK_DIR) && lha xq sdk.lha && rm -f sdk.lha + @touch $@ + ########################################################################### # Here starts all stuff that is common for all target platforms and # hosts. @@ -447,6 +491,16 @@ $(OBJDIR)/mccclass_68k.o: ../include/mccclass_68k.c @echo " CC $<" @$(CC) $(CFLAGS) $< -o $@ +# Test programs get the AmiSSL include path (if enabled). The explicit +# rules below shadow the generic %.o: %.c rule so TEST_CFLAGS applies. +$(OBJDIR)/SimpleTest.o: SimpleTest.c test_image_hook.h $(TEST_DEPS) + @echo " CC $<" + @$(CC) $(CFLAGS) $(TEST_CFLAGS) $< -o $@ + +$(OBJDIR)/LibLoad_Test.o: LibLoad_Test.c test_image_hook.h $(TEST_DEPS) + @echo " CC $<" + @$(CC) $(CFLAGS) $(TEST_CFLAGS) $< -o $@ + # # Link against all MCC objects EXCEPT library.o (globals conflict) @@ -456,18 +510,18 @@ LIBOBJS = $(filter-out $(OBJDIR)/library.o $(OBJDIR)/crtclasses_begin.o $(OBJDIR $(SIMPLETARGET): $(OBJDIR)/SimpleTest.o @echo " LD $@" ifneq (,$(DEBUG)) - @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/SimpleTest.o $(LDLIBS) -ldebug -Wl,-Map,$@.map + @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/SimpleTest.o $(LDLIBS) $(TEST_LDLIBS) -ldebug -Wl,-Map,$@.map else - @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/SimpleTest.o $(LDLIBS) -Wl,-Map,$@.map + @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/SimpleTest.o $(LDLIBS) $(TEST_LDLIBS) -Wl,-Map,$@.map endif @$(STRIP) --preserve-dates -R.comment -R.sdata2 -S -o $@ $@.debug $(LIBLOADTARGET): $(OBJDIR)/LibLoad_Test.o @echo " LD $@" ifneq (,$(DEBUG)) - @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/LibLoad_Test.o $(LDLIBS) -ldebug -Wl,-Map,$@.map + @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/LibLoad_Test.o $(LDLIBS) $(TEST_LDLIBS) -ldebug -Wl,-Map,$@.map else - @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/LibLoad_Test.o $(LDLIBS) -Wl,-Map,$@.map + @$(CXX) $(filter-out -nostartfiles,$(LDFLAGS)) -o $@.debug $(OBJDIR)/LibLoad_Test.o $(LDLIBS) $(TEST_LDLIBS) -Wl,-Map,$@.map endif @$(STRIP) --preserve-dates -R.comment -R.sdata2 -S -o $@ $@.debug diff --git a/mcc/SimpleTest.c b/mcc/SimpleTest.c index ce4753c..679bf9a 100644 --- a/mcc/SimpleTest.c +++ b/mcc/SimpleTest.c @@ -57,7 +57,12 @@ static const char *test_html = "

Images - Network (HTTP)

" "

These need bsdsocket.library and a working network stack:

" "

\"Aminet

" - "

\"AmigaWorld

" + "

Exercises http->https redirect (requires AmiSSL):

" + "

\"AmigaWorld

" + + "

Images - Network (HTTPS)

" + "

Direct TLS fetch. Needs amisslmaster.library + amissl.library installed:

" + "

\"Aminet

" "

Text Formatting

" "

Bold text, italic text, " diff --git a/mcc/test_image_hook.h b/mcc/test_image_hook.h index 52e1cfb..2dad8cc 100644 --- a/mcc/test_image_hook.h +++ b/mcc/test_image_hook.h @@ -2,23 +2,26 @@ * test_image_hook.h -- Shared image-load hook used by SimpleTest and * LibLoad_Test to feed picture data to HTMLview.mcc. * - * The hook resolves three kinds of URLs: - * - PROGDIR:, DH0:, etc. (direct dos Open) - * - file:// (stripped and dos Open) - * - http://host[:port]/p (bsdsocket.library GET, chunked aware) + * URL schemes resolved by the hook: + * - PROGDIR:, DH0:, etc. -- direct dos Open + * - file:// -- stripped and dos Open + * - http://host[:port]/p -- bsdsocket GET, chunked-aware, redirect-aware + * - https://host[:port]/p -- AmiSSL-wrapped GET (only if HAVE_AMISSL) * - * https:// URLs return failure (no TLS in the stock Amiga bsdsocket). + * The hook follows up to 5 consecutive 3xx redirects (absolute URLs only; + * relative Location: headers are NOT supported yet) and upgrades http->https + * transparently when a server redirects to TLS. * - * The header is intentionally single-include, single-translation-unit. - * Include it from exactly one .c file in a test program; the program owns - * the SocketBase / ISocket globals (so it can close them on exit). + * HAVE_AMISSL is defined by the Makefile when the AmiSSL SDK is present. + * Without it, https:// URLs and http->https redirects fail cleanly. * - * Threading notes: the hook runs in the HTMLview decoder thread, not the - * main task. On m68k every task that uses bsdsocket must OpenLibrary the - * library itself -- the library base opened by main() isn't valid for our - * thread. We therefore open a task-local SocketBase in the hook and shadow - * the file-scope globals so the proto/socket.h inline macros dispatch - * through the local base. + * Threading: runs in the HTMLview decoder task. On m68k every task that uses + * bsdsocket must OpenLibrary the library itself, so this file opens a task- + * local SocketBase (and AmiSSLMasterBase / AmiSSLBase) in the hook and + * shadows the file-scope globals so the proto/socket.h inline macros + * dispatch through the local base. + * + * Include this header from exactly one translation unit per test program. */ #ifndef HTMLVIEW_TEST_IMAGE_HOOK_H @@ -33,6 +36,14 @@ #include #include "HTMLview_mcc.h" +#ifdef HAVE_AMISSL +#include +#include +#include +#include +#include +#endif + #ifndef TRUE #define TRUE 1 #endif @@ -40,8 +51,9 @@ #define FALSE 0 #endif -/* The test programs carry their own globals for this; only used by the - main task. The hook uses its own task-local bases instead (see below). */ + +/* Owned by the test program's main task. The hook uses its own task-local + library bases and never touches these from the decoder thread. */ #if defined(__amigaos4__) extern struct Library *SocketBase; extern struct SocketIFace *ISocket; @@ -59,20 +71,34 @@ struct THL_State ULONG buflen; int chunked; /* non-zero => Transfer-Encoding: chunked */ ULONG chunk_left; + int use_tls; /* non-zero => route I/O through SSL_* */ - /* Task-local bsdsocket bases -- opened by the decoder thread so it can + /* Task-local bsdsocket bases. Opened by the decoder thread so it can actually call socket/recv/etc.; closed on hook-Close. */ struct Library *SBase; #if defined(__amigaos4__) struct SocketIFace *SIFace; #endif + +#ifdef HAVE_AMISSL + struct Library *AMSBase; /* amisslmaster.library */ + struct Library *ASBase; /* amissl.library */ +#if defined(__amigaos4__) + struct AmiSSLMasterIFace *IAMSMaster; + struct AmiSSLIFace *IAS; +#endif + SSL_CTX *ssl_ctx; + SSL *ssl; + int ssl_initialized; /* CleanupAmiSSLA required on teardown */ + int sni_errno; /* storage for AmiSSL_ErrNoPtr */ +#endif }; /* --- logging ----------------------------------------------------------- */ /* Accumulating-buffer + rewrite pattern (same as ImageManager DTLog) so the log file stays coherent across OS3 and OS4 without relying on Seek. */ -static char THL_LogBuf[8192]; +static char THL_LogBuf[16384]; static ULONG THL_LogLen = 0; static void THL_Log(const char *line) @@ -87,23 +113,63 @@ static void THL_Log(const char *line) if (f) { Write(f, THL_LogBuf, THL_LogLen); Close(f); } } -/* --- low-level helpers ------------------------------------------------- */ +/* --- low-level I/O (plain TCP or TLS depending on st->use_tls) --------- */ -static LONG THL_ReadChunked(struct THL_State *st, UBYTE *out, LONG want); +static LONG THL_Recv(struct THL_State *st, APTR buf, LONG len) +{ +#ifdef HAVE_AMISSL + if (st->use_tls && st->ssl) + { + /* Inline macros in resolve AMISSL_BASE_NAME + (default: AmiSSLBase) by normal C name lookup -- shadow the + file-scope extern with our task-local base. */ + struct Library *AmiSSLBase = st->ASBase; +#if defined(__amigaos4__) + struct AmiSSLIFace *IAmiSSL = st->IAS; +#endif + int n = SSL_read(st->ssl, buf, (int)len); + return n > 0 ? n : 0; + } +#endif + { + struct Library *SocketBase = st->SBase; +#if defined(__amigaos4__) + struct SocketIFace *ISocket = st->SIFace; +#endif + LONG got = recv(st->socket, buf, len, 0); + return got > 0 ? got : 0; + } +} -static LONG THL_RecvLine(struct THL_State *st, char *buf, LONG maxlen) +static LONG THL_Send(struct THL_State *st, const APTR buf, LONG len) { - /* Shadow file-scope bases with our task-local ones for proto/socket.h. */ - struct Library *SocketBase = st->SBase; +#ifdef HAVE_AMISSL + if (st->use_tls && st->ssl) + { + struct Library *AmiSSLBase = st->ASBase; +#if defined(__amigaos4__) + struct AmiSSLIFace *IAmiSSL = st->IAS; +#endif + int n = SSL_write(st->ssl, buf, (int)len); + return n > 0 ? n : -1; + } +#endif + { + struct Library *SocketBase = st->SBase; #if defined(__amigaos4__) - struct SocketIFace *ISocket = st->SIFace; + struct SocketIFace *ISocket = st->SIFace; #endif + return send(st->socket, buf, len, 0); + } +} +static LONG THL_RecvLine(struct THL_State *st, char *buf, LONG maxlen) +{ LONG i = 0; while (i < maxlen - 1) { UBYTE c; - LONG got = recv(st->socket, (APTR)&c, 1, 0); + LONG got = THL_Recv(st, (APTR)&c, 1); if (got <= 0) return -1; if (c == '\n') { buf[i] = 0; return i; } if (c != '\r') buf[i++] = c; @@ -115,11 +181,6 @@ static LONG THL_RecvLine(struct THL_State *st, char *buf, LONG maxlen) /* Reads up to `want` bytes, unwrapping chunked transfer encoding. */ static LONG THL_ReadChunked(struct THL_State *st, UBYTE *out, LONG want) { - struct Library *SocketBase = st->SBase; -#if defined(__amigaos4__) - struct SocketIFace *ISocket = st->SIFace; -#endif - LONG produced = 0; while (produced < want) { @@ -141,7 +202,7 @@ static LONG THL_ReadChunked(struct THL_State *st, UBYTE *out, LONG want) LONG take = want - produced; if ((ULONG)take > st->chunk_left) take = (LONG)st->chunk_left; - LONG got = recv(st->socket, (APTR)(out + produced), take, 0); + LONG got = THL_Recv(st, (APTR)(out + produced), take); if (got <= 0) return produced; produced += got; st->chunk_left -= got; @@ -149,13 +210,17 @@ static LONG THL_ReadChunked(struct THL_State *st, UBYTE *out, LONG want) return produced; } -/* Parses "http://host[:port]/path" into components (buffers owned by caller). */ -static int THL_ParseHttp(CONST_STRPTR url, char *host, ULONG hlen, - char *path, ULONG plen, ULONG *portp) +/* Parses http:// or https:// URLs. Sets *is_https from scheme. */ +static int THL_ParseUrl(CONST_STRPTR url, char *host, ULONG hlen, + char *path, ULONG plen, ULONG *portp, int *is_https) { const char *p = url; - if (strncmp(p, "http://", 7) != 0) return 0; - p += 7; + ULONG default_port = 80; + *is_https = 0; + + if (strncmp(p, "https://", 8) == 0) { p += 8; default_port = 443; *is_https = 1; } + else if (strncmp(p, "http://", 7) == 0) { p += 7; } + else return 0; const char *hs = p; while (*p && *p != '/' && *p != ':') p++; @@ -165,7 +230,7 @@ static int THL_ParseHttp(CONST_STRPTR url, char *host, ULONG hlen, memcpy(host, hs, n); host[n] = 0; - ULONG port = 80; + ULONG port = default_port; if (*p == ':') { p++; @@ -182,26 +247,186 @@ static int THL_ParseHttp(CONST_STRPTR url, char *host, ULONG hlen, return 1; } -/* Opens an HTTP connection, reads and parses response headers, stashes any - body bytes that came with the header recv. Returns 1 on success. */ -static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) +/* --- connection teardown (shared by error paths and redirect loop) ----- */ + +static void THL_Disconnect(struct THL_State *st) { - char logbuf[320]; +#ifdef HAVE_AMISSL + if (st->use_tls) + { + struct Library *AmiSSLBase = st->ASBase; + struct Library *AmiSSLMasterBase = st->AMSBase; +#if defined(__amigaos4__) + struct AmiSSLIFace *IAmiSSL = st->IAS; + struct AmiSSLMasterIFace *IAmiSSLMaster = st->IAMSMaster; +#endif + if (st->ssl) { SSL_shutdown(st->ssl); SSL_free(st->ssl); st->ssl = NULL; } + if (st->ssl_ctx) { SSL_CTX_free(st->ssl_ctx); st->ssl_ctx = NULL; } + if (st->ssl_initialized) { CleanupAmiSSLA(NULL); st->ssl_initialized = 0; } +#if defined(__amigaos4__) + if (st->IAS) { DropInterface((struct Interface *)st->IAS); st->IAS = NULL; } +#endif + if (st->ASBase) { CloseAmiSSL(); st->ASBase = NULL; AmiSSLBase = NULL; } +#if defined(__amigaos4__) + if (st->IAMSMaster) { DropInterface((struct Interface *)st->IAMSMaster); st->IAMSMaster = NULL; } +#endif + if (st->AMSBase) { CloseLibrary(st->AMSBase); st->AMSBase = NULL; } + (void)AmiSSLBase; (void)AmiSSLMasterBase; + } +#endif - char host[256], path[1024]; - ULONG port = 80; - if (!THL_ParseHttp(url, host, sizeof(host), path, sizeof(path), &port)) + if (st->socket >= 0 && st->SBase) { - THL_Log("http: parse URL failed"); + struct Library *SocketBase = st->SBase; +#if defined(__amigaos4__) + struct SocketIFace *ISocket = st->SIFace; +#endif + CloseSocket(st->socket); + } + st->socket = -1; + +#if defined(__amigaos4__) + if (st->SIFace) { DropInterface((struct Interface *)st->SIFace); st->SIFace = NULL; } +#endif + if (st->SBase) { CloseLibrary(st->SBase); st->SBase = NULL; } + + st->use_tls = 0; + st->chunked = 0; + st->chunk_left = 0; + if (st->buffer) { free(st->buffer); st->buffer = NULL; } + st->buflen = st->bufpos = 0; +} + +/* --- AmiSSL-wrapped connect (only linked when HAVE_AMISSL) ------------- */ + +#ifdef HAVE_AMISSL +static int THL_TlsWrap(struct THL_State *st, const char *host) +{ + char logbuf[256]; + + /* All inline SSL calls resolve their library base via normal C name + lookup. We declare these early and reassign as bases come up, so + every call below sees the right local. */ + struct Library *AmiSSLMasterBase = NULL; + struct Library *AmiSSLBase = NULL; +#if defined(__amigaos4__) + struct AmiSSLMasterIFace *IAmiSSLMaster = NULL; + struct AmiSSLIFace *IAmiSSL = NULL; + struct SocketIFace *ISocket = st->SIFace; +#endif + + st->AMSBase = OpenLibrary((STRPTR)"amisslmaster.library", AMISSLMASTER_MIN_VERSION); + if (!st->AMSBase) + { + THL_Log("https: OpenLibrary amisslmaster.library failed"); + return 0; + } + AmiSSLMasterBase = st->AMSBase; +#if defined(__amigaos4__) + st->IAMSMaster = (struct AmiSSLMasterIFace *) + GetInterface(st->AMSBase, "main", 1, NULL); + if (!st->IAMSMaster) { THL_Log("https: GetInterface IAmiSSLMaster failed"); return 0; } + IAmiSSLMaster = st->IAMSMaster; +#endif + + if (!InitAmiSSLMaster(AMISSL_CURRENT_VERSION, TRUE)) + { + THL_Log("https: InitAmiSSLMaster failed (library too old)"); + return 0; + } + + st->ASBase = OpenAmiSSL(); + if (!st->ASBase) + { + THL_Log("https: OpenAmiSSL failed"); + return 0; + } + AmiSSLBase = st->ASBase; +#if defined(__amigaos4__) + st->IAS = (struct AmiSSLIFace *)GetInterface(st->ASBase, "main", 1, NULL); + if (!st->IAS) { THL_Log("https: GetInterface IAmiSSL failed"); return 0; } + IAmiSSL = st->IAS; +#endif + + /* Use the A-suffixed variant: OS3 builds pass -DNO_INLINE_STDARG, + which disables the varargs InitAmiSSL() macro. */ + { + struct TagItem init_tags[] = { + { AmiSSL_ErrNoPtr, (ULONG)&st->sni_errno }, +#if defined(__amigaos4__) + { AmiSSL_ISocket, (ULONG)st->SIFace }, +#else + { AmiSSL_SocketBase, (ULONG)st->SBase }, +#endif + { TAG_DONE, 0 } + }; + if (InitAmiSSLA(init_tags) != 0) + { + THL_Log("https: InitAmiSSL failed"); + return 0; + } + } + st->ssl_initialized = 1; + + OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT + | OPENSSL_INIT_ADD_ALL_CIPHERS + | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); + + /* Minimal entropy seeding -- enough for client handshake; see the + AmiSSL sample for a proper implementation if needed. */ + { + unsigned char seed[32]; + ULONG t = (ULONG)FindTask(NULL); + for (ULONG i = 0; i < sizeof(seed); i++) + seed[i] = (unsigned char)(t >> ((i & 3) * 8)) ^ (unsigned char)i; + RAND_seed(seed, sizeof(seed)); + } + + st->ssl_ctx = SSL_CTX_new(TLS_client_method()); + if (!st->ssl_ctx) { THL_Log("https: SSL_CTX_new failed"); return 0; } + + /* Disable peer verification for the test programs. Strict verification + needs ENVARC:AmiSSL/cacert.pem installed; relaxing lets the tests run + on a barebones setup without silently masking real TLS errors -- any + real protocol failure still shows up in SSL_connect below. */ + SSL_CTX_set_verify(st->ssl_ctx, SSL_VERIFY_NONE, NULL); + + st->ssl = SSL_new(st->ssl_ctx); + if (!st->ssl) { THL_Log("https: SSL_new failed"); return 0; } + + SSL_set_fd(st->ssl, (int)st->socket); + SSL_set_tlsext_host_name(st->ssl, host); + + int h = SSL_connect(st->ssl); + if (h <= 0) + { + int err = SSL_get_error(st->ssl, h); + sprintf(logbuf, "https: SSL_connect failed rc=%d err=%d", h, err); + THL_Log(logbuf); return 0; } - sprintf(logbuf, "http: host=%s port=%lu path=%s", host, port, path); + sprintf(logbuf, "https: handshake OK cipher=%s", SSL_get_cipher(st->ssl)); THL_Log(logbuf); + return 1; +} +#endif /* HAVE_AMISSL */ + +/* --- per-hop connect (TCP + optional TLS). Does NOT send the request. - */ + +static int THL_Connect(struct THL_State *st, const char *host, ULONG port, int use_tls) +{ + char logbuf[256]; + st->use_tls = use_tls; + +#ifndef HAVE_AMISSL + if (use_tls) + { + THL_Log("https: no AmiSSL support compiled in"); + return 0; + } +#endif - /* Open bsdsocket fresh for THIS task -- the decoder thread. On m68k the - library is opened per-task; the main task's SocketBase is not usable - from us. */ struct Library *SocketBase = OpenLibrary((STRPTR)"bsdsocket.library", 4); #if defined(__amigaos4__) struct SocketIFace *ISocket = SocketBase @@ -213,12 +438,10 @@ static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) THL_Log("http: OpenLibrary bsdsocket.library failed"); return 0; } + st->SBase = SocketBase; st->SIFace = ISocket; #else - if (!SocketBase) - { - THL_Log("http: OpenLibrary bsdsocket.library failed"); - return 0; - } + if (!SocketBase) { THL_Log("http: OpenLibrary bsdsocket.library failed"); return 0; } + st->SBase = SocketBase; #endif struct hostent *he; @@ -231,23 +454,11 @@ static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) { sprintf(logbuf, "http: gethostbyname(%s) failed", host); THL_Log(logbuf); -#if defined(__amigaos4__) - if (ISocket) DropInterface((struct Interface *)ISocket); -#endif - CloseLibrary(SocketBase); return 0; } - LONG s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) - { - THL_Log("http: socket() failed"); -#if defined(__amigaos4__) - if (ISocket) DropInterface((struct Interface *)ISocket); -#endif - CloseLibrary(SocketBase); - return 0; - } + st->socket = socket(AF_INET, SOCK_STREAM, 0); + if (st->socket < 0) { THL_Log("http: socket() failed"); return 0; } struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); @@ -255,56 +466,54 @@ static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) sin.sin_port = htons((UWORD)port); sin.sin_addr.s_addr = *((ULONG *)he->h_addr_list[0]); - if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) + if (connect(st->socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) { THL_Log("http: connect() failed"); - CloseSocket(s); -#if defined(__amigaos4__) - if (ISocket) DropInterface((struct Interface *)ISocket); -#endif - CloseLibrary(SocketBase); return 0; } +#ifdef HAVE_AMISSL + if (use_tls) + { + if (!THL_TlsWrap(st, host)) return 0; + } +#endif + return 1; +} + +/* --- build + send GET; read headers; stash any body bytes in st->buffer -- + Returns: 1 = headers OK, body follows + 0 = error / non-200 + -1 = redirect; next URL copied into redirect_out (size plen). */ +static int THL_DoRequest(struct THL_State *st, const char *host, + const char *path, char *redirect_out, ULONG rlen) +{ + char logbuf[320]; + char request[2048]; - int rlen = sprintf(request, + int rlen_s = sprintf(request, "GET %s HTTP/1.1\r\n" "Host: %s\r\n" - "User-Agent: HTMLview-TestHook/1.1\r\n" + "User-Agent: HTMLview-TestHook/1.2\r\n" "Accept: */*\r\n" "Connection: close\r\n" "\r\n", path, host); - if (send(s, request, rlen, 0) < 0) + if (THL_Send(st, request, rlen_s) < 0) { THL_Log("http: send() failed"); - CloseSocket(s); -#if defined(__amigaos4__) - if (ISocket) DropInterface((struct Interface *)ISocket); -#endif - CloseLibrary(SocketBase); return 0; } - /* Read a moderate chunk so we're very likely to have all headers plus - some body in a single recv. */ UBYTE *hdr = (UBYTE *)malloc(16384); - if (!hdr) - { - CloseSocket(s); -#if defined(__amigaos4__) - if (ISocket) DropInterface((struct Interface *)ISocket); -#endif - CloseLibrary(SocketBase); - return 0; - } + if (!hdr) return 0; LONG have = 0; char *hdrend = NULL; while (have < 16384 - 1) { - LONG got = recv(s, (APTR)(hdr + have), 16384 - 1 - have, 0); + LONG got = THL_Recv(st, (APTR)(hdr + have), 16384 - 1 - have); if (got <= 0) break; have += got; hdr[have] = 0; @@ -316,88 +525,81 @@ static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) { THL_Log("http: no header terminator in reply"); free(hdr); - CloseSocket(s); -#if defined(__amigaos4__) - if (ISocket) DropInterface((struct Interface *)ISocket); -#endif - CloseLibrary(SocketBase); return 0; } - /* hdrend points at the first byte of the header/body separator. - Separator is either "\r\n\r\n" (4 bytes) or "\n\n" (2 bytes). - Compute body offset BEFORE null-terminating. */ ULONG header_len = (*hdrend == '\r') ? (ULONG)(hdrend - (char *)hdr) + 4 : (ULONG)(hdrend - (char *)hdr) + 2; - - /* Null-terminate the header block so strstr on header values is safe. - Writing at hdrend clobbers only the first byte of the separator, - which we no longer need -- NOT the first body byte. */ *hdrend = 0; + /* Parse status line. */ + char status[160]; + ULONG n = 0; + while (n < sizeof(status) - 1 && n < header_len && + hdr[n] != '\r' && hdr[n] != '\n') { - /* Log the HTTP status line so we can spot 301/302 redirects, 404s, - etc. The header block also tells us whether the server returned - HTML instead of an image. */ - char status[120]; - ULONG n = 0; - while (n < sizeof(status) - 1 && n < header_len && - hdr[n] != '\r' && hdr[n] != '\n') - { - status[n] = (char)hdr[n]; - n++; - } - status[n] = 0; - sprintf(logbuf, "http: status=%s", status); - THL_Log(logbuf); + status[n] = (char)hdr[n]; + n++; + } + status[n] = 0; + sprintf(logbuf, "http: status=%s", status); + THL_Log(logbuf); - /* Bail on non-200 responses -- redirects, 404s and 5xx must not - reach the decoder as "image body". Parse the numeric code. */ - int code = 0; - const char *sp = strchr(status, ' '); - if (sp) - { - while (*sp == ' ') sp++; - while (*sp >= '0' && *sp <= '9') { code = code*10 + (*sp - '0'); sp++; } - } - if (code != 200) - { - sprintf(logbuf, "http: aborting (status=%d)", code); - THL_Log(logbuf); - free(hdr); - CloseSocket(s); -#if defined(__amigaos4__) - if (ISocket) DropInterface((struct Interface *)ISocket); -#endif - CloseLibrary(SocketBase); - return 0; - } + int code = 0; + const char *sp = strchr(status, ' '); + if (sp) + { + while (*sp == ' ') sp++; + while (*sp >= '0' && *sp <= '9') { code = code*10 + (*sp - '0'); sp++; } + } - /* Helper: dig out a header value by name, log as "http: =". */ - const char *hdrs[] = { "Location:", "location:", - "Content-Type:", "content-type:", - "Content-Length:", "content-length:", - "Content-Encoding:", "content-encoding:" }; - const char *tags[] = { "location", "location", - "content-type", "content-type", - "content-length", "content-length", - "content-encoding", "content-encoding" }; - for (ULONG i = 0; i < sizeof(hdrs)/sizeof(hdrs[0]); i++) - { - const char *v = strstr((char *)hdr, hdrs[i]); - if (!v) continue; - v += strlen(hdrs[i]); - while (*v == ' ' || *v == '\t') v++; - const char *e = v; - while (*e && *e != '\r' && *e != '\n') e++; - ULONG L = (ULONG)(e - v); - if (L > sizeof(status) - 1) L = sizeof(status) - 1; - memcpy(status, v, L); - status[L] = 0; - sprintf(logbuf, "http: %s=%s", tags[i], status); - THL_Log(logbuf); - } + /* Helper: extract a single named header into `out`. Case-aware: accepts + either capitalised or lowercase names (lazy match of the common + Amiga-side server responses). */ + #define GRAB_HDR(upper, lower, out, outsz) do { \ + const char *v = strstr((char *)hdr, upper); \ + if (!v) v = strstr((char *)hdr, lower); \ + if (v) { \ + v += strlen(upper); \ + while (*v == ' ' || *v == '\t') v++; \ + const char *e = v; \ + while (*e && *e != '\r' && *e != '\n') e++; \ + ULONG L = (ULONG)(e - v); \ + if (L > (ULONG)(outsz) - 1) L = (ULONG)(outsz) - 1; \ + memcpy(out, v, L); out[L] = 0; \ + } else out[0] = 0; \ + } while (0) + + char v_loc[512], v_type[128], v_len[32], v_enc[32]; + GRAB_HDR("Location:", "location:", v_loc, sizeof(v_loc)); + GRAB_HDR("Content-Type:", "content-type:", v_type, sizeof(v_type)); + GRAB_HDR("Content-Length:", "content-length:", v_len, sizeof(v_len)); + GRAB_HDR("Content-Encoding:","content-encoding:", v_enc, sizeof(v_enc)); + if (v_loc[0]) { sprintf(logbuf, "http: location=%s", v_loc); THL_Log(logbuf); } + if (v_type[0]) { sprintf(logbuf, "http: content-type=%s", v_type); THL_Log(logbuf); } + if (v_len[0]) { sprintf(logbuf, "http: content-length=%s", v_len); THL_Log(logbuf); } + if (v_enc[0]) { sprintf(logbuf, "http: content-encoding=%s", v_enc); THL_Log(logbuf); } + #undef GRAB_HDR + + /* 3xx + Location => signal caller to reconnect. We don't handle relative + redirects yet; the request will fail on next parse if Location isn't + a full URL. */ + if ((code == 301 || code == 302 || code == 303 || + code == 307 || code == 308) && v_loc[0]) + { + strncpy(redirect_out, v_loc, rlen - 1); + redirect_out[rlen - 1] = 0; + free(hdr); + return -1; + } + + if (code != 200) + { + sprintf(logbuf, "http: aborting (status=%d)", code); + THL_Log(logbuf); + free(hdr); + return 0; } if (strstr((char *)hdr, "Transfer-Encoding: chunked") || @@ -416,33 +618,73 @@ static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) } } - sprintf(logbuf, "http: OK header_len=%lu body_have=%lu chunked=%d", - header_len, body_have, st->chunked); + sprintf(logbuf, "http: OK header_len=%lu body_have=%lu chunked=%d tls=%d", + header_len, body_have, st->chunked, st->use_tls); THL_Log(logbuf); - /* Log the first 16 body bytes in hex -- lets us see the magic number - of the actual payload so we can tell PNG / GIF / HTML / gzip apart - at a glance. */ if (body_have > 0) { UBYTE *bp = hdr + header_len; ULONG show = body_have < 16 ? body_have : 16; - char hex[80]; + char hex[96]; int off = sprintf(hex, "http: body[0..%lu]=", show); for (ULONG i = 0; i < show; i++) off += sprintf(hex + off, "%02x ", bp[i]); THL_Log(hex); } - st->socket = s; - st->SBase = SocketBase; -#if defined(__amigaos4__) - st->SIFace = ISocket; -#endif free(hdr); return 1; } +/* Entry point: opens a connection and reads headers, following up to 5 + redirects across http / https. Returns 1 on success, 0 on any error. */ +static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) +{ + char logbuf[384]; + char current[1024]; + strncpy(current, url, sizeof(current) - 1); + current[sizeof(current) - 1] = 0; + + for (int hop = 0; hop < 5; hop++) + { + char host[256], path[1024]; + ULONG port; + int is_https; + if (!THL_ParseUrl(current, host, sizeof(host), + path, sizeof(path), &port, &is_https)) + { + sprintf(logbuf, "http: unparseable URL '%s'", current); + THL_Log(logbuf); + return 0; + } + + sprintf(logbuf, "http: [hop %d] %s://%s:%lu%s", + hop, is_https ? "https" : "http", host, port, path); + THL_Log(logbuf); + + if (!THL_Connect(st, host, port, is_https)) + { + THL_Disconnect(st); + return 0; + } + + char next[1024]; + int r = THL_DoRequest(st, host, path, next, sizeof(next)); + if (r == 1) return 1; + if (r == 0) { THL_Disconnect(st); return 0; } + + /* 3xx -- tear down and try the new URL. */ + THL_Disconnect(st); + strncpy(current, next, sizeof(current) - 1); + current[sizeof(current) - 1] = 0; + } + + THL_Log("http: too many redirects"); + THL_Disconnect(st); + return 0; +} + /* --- the hook function ------------------------------------------------- */ static ULONG TestImageHookFunc(struct Hook *hook, APTR obj, @@ -464,13 +706,8 @@ static ULONG TestImageHookFunc(struct Hook *hook, APTR obj, st->socket = -1; msg->lm_Userdata = st; - if (strncmp(url, "https://", 8) == 0) - { - THL_Log("hook: https:// unsupported"); - free(st); msg->lm_Userdata = NULL; return 0; - } - - if (strncmp(url, "http://", 7) == 0) + if (strncmp(url, "http://", 7) == 0 || + strncmp(url, "https://", 8) == 0) { if (!THL_HttpOpen(url, st)) { @@ -520,17 +757,15 @@ static ULONG TestImageHookFunc(struct Hook *hook, APTR obj, if (st->socket >= 0) { - struct Library *SocketBase = st->SBase; -#if defined(__amigaos4__) - struct SocketIFace *ISocket = st->SIFace; -#endif LONG rd = st->chunked ? THL_ReadChunked(st, (UBYTE *)out, len) - : recv(st->socket, out, len, 0); + : THL_Recv(st, out, len); { char logbuf[96]; - sprintf(logbuf, "read: recv(sock=%ld want=%ld) => %ld%s", - st->socket, len, rd, st->chunked ? " (chunked)" : ""); + sprintf(logbuf, "read: recv(sock=%ld want=%ld) => %ld%s%s", + st->socket, len, rd, + st->chunked ? " (chunked)" : "", + st->use_tls ? " (tls)" : ""); THL_Log(logbuf); } return rd > 0 ? rd : 0; @@ -548,23 +783,7 @@ static ULONG TestImageHookFunc(struct Hook *hook, APTR obj, if (st) { if (st->file) Close(st->file); - - if (st->socket >= 0 && st->SBase) - { - struct Library *SocketBase = st->SBase; -#if defined(__amigaos4__) - struct SocketIFace *ISocket = st->SIFace; -#endif - CloseSocket(st->socket); - } - -#if defined(__amigaos4__) - if (st->SIFace) - DropInterface((struct Interface *)st->SIFace); -#endif - if (st->SBase) CloseLibrary(st->SBase); - - if (st->buffer) free(st->buffer); + THL_Disconnect(st); free(st); msg->lm_Userdata = NULL; } From 98e8f927d0df60346afb28a41050a2c1b9da4409 Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Sat, 18 Apr 2026 13:47:20 +0200 Subject: [PATCH 2/4] test: swap amigaworld redirect image to a URL that isn't 404 on HTTPS awn2.gif redirects to HTTPS correctly, but the HTTPS endpoint returns 404 -- the server only hosts the image on plain HTTP. logo-top.gif redirects to HTTPS and returns the image, which is the scenario we actually want to exercise. --- mcc/LibLoad_Test.c | 2 +- mcc/SimpleTest.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mcc/LibLoad_Test.c b/mcc/LibLoad_Test.c index 3d875ca..3682a0c 100644 --- a/mcc/LibLoad_Test.c +++ b/mcc/LibLoad_Test.c @@ -53,7 +53,7 @@ static const char *test_html = "

Network images require bsdsocket.library:

" "

\"Aminet

" "

Exercises http->https redirect (requires AmiSSL):

" - "

\"AmigaWorld

" + "

\"AmigaWorld

" "

Images - Network (HTTPS)

" "

Direct TLS fetch. Needs amisslmaster.library + amissl.library installed:

" diff --git a/mcc/SimpleTest.c b/mcc/SimpleTest.c index 679bf9a..64f136c 100644 --- a/mcc/SimpleTest.c +++ b/mcc/SimpleTest.c @@ -58,7 +58,7 @@ static const char *test_html = "

These need bsdsocket.library and a working network stack:

" "

\"Aminet

" "

Exercises http->https redirect (requires AmiSSL):

" - "

\"AmigaWorld

" + "

\"AmigaWorld

" "

Images - Network (HTTPS)

" "

Direct TLS fetch. Needs amisslmaster.library + amissl.library installed:

" From c47ddd77e48e7c027bf8dfb306bf571d6b317ee0 Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Sat, 18 Apr 2026 13:49:58 +0200 Subject: [PATCH 3/4] fix: replace sprintf with snprintf in test image hook Codex review on PR #2 flagged two stack overflows where a user-controllable value (Location: header up to 512 bytes, or a redirected URL up to 1024 bytes) was formatted into a smaller logbuf via unbounded sprintf. Converted every sprintf(logbuf, ...) and the GET-request sprintf to snprintf(...sizeof(buf)...). The hex-dump sprintfs are left as sprintf since they're bounded at 16 bytes and can't overflow. Fixes the P1 Location overflow and P2 unparseable-URL overflow flagged by Codex. --- mcc/test_image_hook.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/mcc/test_image_hook.h b/mcc/test_image_hook.h index 2dad8cc..0c253cd 100644 --- a/mcc/test_image_hook.h +++ b/mcc/test_image_hook.h @@ -401,12 +401,12 @@ static int THL_TlsWrap(struct THL_State *st, const char *host) if (h <= 0) { int err = SSL_get_error(st->ssl, h); - sprintf(logbuf, "https: SSL_connect failed rc=%d err=%d", h, err); + snprintf(logbuf, sizeof(logbuf), "https: SSL_connect failed rc=%d err=%d", h, err); THL_Log(logbuf); return 0; } - sprintf(logbuf, "https: handshake OK cipher=%s", SSL_get_cipher(st->ssl)); + snprintf(logbuf, sizeof(logbuf), "https: handshake OK cipher=%s", SSL_get_cipher(st->ssl)); THL_Log(logbuf); return 1; } @@ -452,7 +452,7 @@ static int THL_Connect(struct THL_State *st, const char *host, ULONG port, int u #endif if (!he) { - sprintf(logbuf, "http: gethostbyname(%s) failed", host); + snprintf(logbuf, sizeof(logbuf), "http: gethostbyname(%s) failed", host); THL_Log(logbuf); return 0; } @@ -491,7 +491,7 @@ static int THL_DoRequest(struct THL_State *st, const char *host, char logbuf[320]; char request[2048]; - int rlen_s = sprintf(request, + int rlen_s = snprintf(request, sizeof(request), "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: HTMLview-TestHook/1.2\r\n" @@ -543,7 +543,7 @@ static int THL_DoRequest(struct THL_State *st, const char *host, n++; } status[n] = 0; - sprintf(logbuf, "http: status=%s", status); + snprintf(logbuf, sizeof(logbuf), "http: status=%s", status); THL_Log(logbuf); int code = 0; @@ -576,10 +576,10 @@ static int THL_DoRequest(struct THL_State *st, const char *host, GRAB_HDR("Content-Type:", "content-type:", v_type, sizeof(v_type)); GRAB_HDR("Content-Length:", "content-length:", v_len, sizeof(v_len)); GRAB_HDR("Content-Encoding:","content-encoding:", v_enc, sizeof(v_enc)); - if (v_loc[0]) { sprintf(logbuf, "http: location=%s", v_loc); THL_Log(logbuf); } - if (v_type[0]) { sprintf(logbuf, "http: content-type=%s", v_type); THL_Log(logbuf); } - if (v_len[0]) { sprintf(logbuf, "http: content-length=%s", v_len); THL_Log(logbuf); } - if (v_enc[0]) { sprintf(logbuf, "http: content-encoding=%s", v_enc); THL_Log(logbuf); } + if (v_loc[0]) { snprintf(logbuf, sizeof(logbuf), "http: location=%s", v_loc); THL_Log(logbuf); } + if (v_type[0]) { snprintf(logbuf, sizeof(logbuf), "http: content-type=%s", v_type); THL_Log(logbuf); } + if (v_len[0]) { snprintf(logbuf, sizeof(logbuf), "http: content-length=%s", v_len); THL_Log(logbuf); } + if (v_enc[0]) { snprintf(logbuf, sizeof(logbuf), "http: content-encoding=%s", v_enc); THL_Log(logbuf); } #undef GRAB_HDR /* 3xx + Location => signal caller to reconnect. We don't handle relative @@ -596,7 +596,7 @@ static int THL_DoRequest(struct THL_State *st, const char *host, if (code != 200) { - sprintf(logbuf, "http: aborting (status=%d)", code); + snprintf(logbuf, sizeof(logbuf), "http: aborting (status=%d)", code); THL_Log(logbuf); free(hdr); return 0; @@ -618,7 +618,7 @@ static int THL_DoRequest(struct THL_State *st, const char *host, } } - sprintf(logbuf, "http: OK header_len=%lu body_have=%lu chunked=%d tls=%d", + snprintf(logbuf, sizeof(logbuf), "http: OK header_len=%lu body_have=%lu chunked=%d tls=%d", header_len, body_have, st->chunked, st->use_tls); THL_Log(logbuf); @@ -654,12 +654,12 @@ static LONG THL_HttpOpen(CONST_STRPTR url, struct THL_State *st) if (!THL_ParseUrl(current, host, sizeof(host), path, sizeof(path), &port, &is_https)) { - sprintf(logbuf, "http: unparseable URL '%s'", current); + snprintf(logbuf, sizeof(logbuf), "http: unparseable URL '%s'", current); THL_Log(logbuf); return 0; } - sprintf(logbuf, "http: [hop %d] %s://%s:%lu%s", + snprintf(logbuf, sizeof(logbuf), "http: [hop %d] %s://%s:%lu%s", hop, is_https ? "https" : "http", host, port, path); THL_Log(logbuf); @@ -762,7 +762,7 @@ static ULONG TestImageHookFunc(struct Hook *hook, APTR obj, : THL_Recv(st, out, len); { char logbuf[96]; - sprintf(logbuf, "read: recv(sock=%ld want=%ld) => %ld%s%s", + snprintf(logbuf, sizeof(logbuf), "read: recv(sock=%ld want=%ld) => %ld%s%s", st->socket, len, rd, st->chunked ? " (chunked)" : "", st->use_tls ? " (tls)" : ""); From 19645269fbd96e86ea1be7770307de6447d6f8fb Mon Sep 17 00:00:00 2001 From: Dimitris Panokostas Date: Sat, 18 Apr 2026 13:59:30 +0200 Subject: [PATCH 4/4] docs: update authors, copyright, changelog, and hook autodocs - AUTHORS: add Dimitris Panokostas; bump Open Source Team copyright to 2005-2026. - README, TODO, doc/MCC_HTMLview.readme: bump copyright year. - ChangeLog: log the HTTPS/AmiSSL work and the prior OS3/OS4/MorphOS build fix-ups under feat/https-support. - doc/MCC_HTMLview.doc, mcc/HTMLview_mcc.h: clarify that HTMLview.mcc itself never touches the network or filesystem -- URL-scheme support lives entirely in the application's ImageLoadHook / LoadHook. Point readers at SimpleTest / LibLoad_Test as a reference hook that handles PROGDIR:, file://, http:// and (with AmiSSL) https:// with redirect following. --- AUTHORS | 3 ++- ChangeLog | 30 ++++++++++++++++++++++++++++++ README | 2 +- TODO | 2 +- doc/MCC_HTMLview.doc | 12 ++++++++++++ doc/MCC_HTMLview.readme | 2 +- mcc/HTMLview_mcc.h | 11 +++++++++++ 7 files changed, 58 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index ee21782..0ef5350 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2,7 +2,7 @@ HTMLview.mcc - HTMLview MUI Custom Class Copyright (C) 1997-2000 Allan Odgaard - Copyright (C) 2005-2010 by HTMLview.mcc Open Source Team + Copyright (C) 2005-2026 by HTMLview.mcc Open Source Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -25,6 +25,7 @@ design provided by the following people: Alfonso Ranieri Allan Odgaard +Dimitris Panokostas Dwight Meese Ilkka Lehtoranta Jens Langner diff --git a/ChangeLog b/ChangeLog index f5fc9ea..a6f4824 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,36 @@ MUI HTMLview MCC class - ChangeLog $Id$ $URL$ +2026-04-18 Dimitris Panokostas + + * mcc/test_image_hook.h: HTTPS support via AmiSSL. The shared test-program + image hook now handles https:// URLs and follows http->https redirects + (up to five hops). TLS is wrapped through amisslmaster.library + + amissl.library (jens-maus AmiSSL 5.27 SDK), using the inline macros + against task-local library bases -- no stub lib link and no global + AmiSSLBase symbol required. Certificate verification is currently + VERIFY_NONE; CA bundle wiring is a follow-up. + * mcc/test_image_hook.h: Replaced unbounded sprintf into log buffers with + snprintf; a long Location: header or redirect target could otherwise + overflow the stack log buffer. + * mcc/Makefile: On-demand AmiSSL SDK download (lha xq). HTTPS is enabled + for OS3 and OS4 builds; MorphOS stays on plain HTTP. + * mcc/SimpleTest.c, mcc/LibLoad_Test.c: Added network-image test entries + (plain HTTP from aminet, http->https redirect on amigaworld, direct + HTTPS from aminet). + * mcc/LibLoad_Test.c: New test program that exercises the same hook as + SimpleTest but through dlopen-style library loading, making it easier + to verify the shipped HTMLview.mcc on each platform. + +2026-04-17 Dimitris Panokostas + + * mcc: Fix MorphOS MCP and OS4 MCC build issues; OS4 build now makes + -ldebug conditional and stubs kprintf when not building with DEBUG=1; + enabled all three platform builds (OS3, OS4, MorphOS) in CI. + * mcc/ImageManager.cpp, mcc/IM_Render.cpp, mcc/Dispatcher.cpp: Local + image rendering fixes uncovered by the new test programs (decoder + handshake, frame cleanup, hook dispatcher wiring on OS4). + 2016-07-29 Thore Böckelmann * mcc: lots of changes to get this beast buildable with our Linux based cross diff --git a/README b/README index a9e752b..0acd400 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ HTMLview.mcc - HTMLview MUI Custom Class Copyright (C) 1997-2000 Allan Odgaard - Copyright (C) 2005-2007 by HTMLview.mcc Open Source Team + Copyright (C) 2005-2026 by HTMLview.mcc Open Source Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/TODO b/TODO index e58ea4e..3fa4961 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,7 @@ HTMLview.mcc - HTMLview MUI Custom Class Copyright (C) 1997-2000 Allan Odgaard - Copyright (C) 2005-2007 by HTMLview.mcc Open Source Team + Copyright (C) 2005-2026 by HTMLview.mcc Open Source Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/doc/MCC_HTMLview.doc b/doc/MCC_HTMLview.doc index 2ce2999..7e34adc 100644 --- a/doc/MCC_HTMLview.doc +++ b/doc/MCC_HTMLview.doc @@ -141,6 +141,14 @@ ToDo: FUNCTION Setup a hook used for image-loading. + Only the hook performs I/O; HTMLview.mcc itself never + touches the network or the filesystem. The hook therefore + decides which URL schemes are supported. The bundled + SimpleTest / LibLoad_Test programs in mcc/ ship a reference + hook that handles PROGDIR:, file://, http:// and (when + compiled against the AmiSSL SDK) https:// with http->https + redirect following. + SEE ALSO MUIA_HTMLview_LoadHook @@ -160,6 +168,10 @@ ToDo: FUNCTION Setup a hook used for (page)-loading. + See MUIA_HTMLview_ImageLoadHook for a note on what the + hook is responsible for and where to find a reference + implementation that also covers HTTPS via AmiSSL. + The hook is called with a pointer to itself in a0, a pointer to a struct HTMLview_LoadMsg in a1 and a pointer to the calling object in a2. diff --git a/doc/MCC_HTMLview.readme b/doc/MCC_HTMLview.readme index 910dcc7..0f149a3 100644 --- a/doc/MCC_HTMLview.readme +++ b/doc/MCC_HTMLview.readme @@ -51,7 +51,7 @@ LEGALESE HTMLview.mcc was originally written in 1995 and is Copyright (C) 1995-2000 by Allan Odgaard. As of version 13.4, released in December 2007, the gadget is -maintained and Copyright (C) 2005-2007 by the HTMLview.mcc Open Source Team. +maintained and Copyright (C) 2005-2026 by the HTMLview.mcc Open Source Team. HTMLview.mcc is distributed under the GNU Lesser General Public License (LGPL) and the development is hosted at SourceForge.net: diff --git a/mcc/HTMLview_mcc.h b/mcc/HTMLview_mcc.h index 133a5f0..c591751 100644 --- a/mcc/HTMLview_mcc.h +++ b/mcc/HTMLview_mcc.h @@ -165,6 +165,10 @@ * to itself in a0, a pointer to a struct HTMLview_LoadMsg in a1 and a * pointer to the calling object in a2. * + * See MUIA_HTMLview_ImageLoadHook for a note on what the hook is + * responsible for and where to find a reference implementation that + * also covers HTTPS via AmiSSL. + * * This hook will be called from a separate task, so the only MUI method * that you can use is MUIM_Application_PushMethod. The hook may very well * be called by sevaral tasks at the same time, so your code needs to be @@ -619,6 +623,13 @@ * * Setup a hook used for image-loading. * + * Only the hook performs I/O; HTMLview.mcc itself never touches the + * network or the filesystem. The hook therefore decides which URL + * schemes are supported. The bundled SimpleTest / LibLoad_Test + * programs in mcc/ ship a reference hook that handles PROGDIR:, + * file://, http:// and (when compiled against the AmiSSL SDK) + * https:// with http->https redirect following. + * * SEE ALSO * * MUIA_HTMLview_LoadHook