From a07e889d7bdfde88e85c6f4a0d47903187b2e803 Mon Sep 17 00:00:00 2001 From: Oliver Schmidt Date: Tue, 20 Feb 2018 00:18:48 +0100 Subject: [PATCH] Added HttpFileServer65. HttpFileServer65 is inspired by "HFS ~ Http File Server" (http://www.rejetto.com/hfs/). It features an intuitive mapping of local disks and directories to HTTP paths. This allows to not only navigate to some file but to directly enter some file URL. This is especially usefull for downloads via wget, curl or alike. However, in contrast to HFS it doesn't allow file uploads, only file downloads. On targets with complete DIO support it additionally allows disk image downloads. Note: The Atari build currently fails as there's so far no getdevicedir() in the Atari C library. --- apps/Makefile | 3 + apps/hfs65.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 331 insertions(+) create mode 100644 apps/hfs65.c diff --git a/apps/Makefile b/apps/Makefile index c0a33bb..d82c450 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -26,6 +26,7 @@ UDP =\ date TCP =\ + hfs65 \ telnet65 all: $(UDP) $(TCP) @@ -97,11 +98,13 @@ telnet65.com: ATARI_CFG = atrtelnet.cfg ip65.d64: prg $(C1541) -format ip65,00 d64 $@ $(C1541) -attach $@ -write date.prg date,p + $(C1541) -attach $@ -write hfs65.prg hfs65,p $(C1541) -attach $@ -write telnet65.prg telnet65,p ip65.dsk: bin cp prodos.dsk $@ java -jar $(AC) -cc65 $@ date bin < date.bin + java -jar $(AC) -cc65 $@ hfs65 bin < hfs65.bin java -jar $(AC) -cc65 $@ telnet65 bin < telnet65.bin ip65.atr: com diff --git a/apps/hfs65.c b/apps/hfs65.c new file mode 100644 index 0000000..4881d18 --- /dev/null +++ b/apps/hfs65.c @@ -0,0 +1,328 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../inc/ip65.h" + +#ifdef __CBM__ +//#define IMAGE ".D64" +#endif +#ifdef __APPLE2__ +#define IMAGE ".PO" +#endif +#ifdef __ATARI__ +//#define IMAGE ".ATR" +#endif + +#define SEND_FIRST 1 +#define SEND_LAST 2 + +unsigned char send_buffer[1024]; +unsigned int send_size; +unsigned char send_type; + +void send(unsigned char flags, const char* str, ...) +{ + va_list args; + + if (flags & SEND_FIRST) + { + send_size = 0; + send_type = HTTPD_RESPONSE_200_HTML; + } + + va_start(args, str); + send_size += vsnprintf(send_buffer + send_size, sizeof(send_buffer) - send_size, str, args); + va_end(args); + + if (flags & SEND_LAST || sizeof(send_buffer) - send_size < 1024 / 4) + { +#ifdef __CBM__ + { + unsigned char* ptr = send_buffer; + unsigned char* end = send_buffer + send_size; + + for (; ptr != end; ++ptr) + { + *ptr = toascii(*ptr); + } + } +#endif + + httpd_send_response(send_type, send_buffer, send_size); + send_size = 0; + send_type = HTTPD_RESPONSE_NOHEADER; + } +} + +void error(void) +{ + httpd_send_response(HTTPD_RESPONSE_500, 0, 0); + printf("500\n"); +} + +void root(void) +{ + char diskdir[FILENAME_MAX]; + char* rootdir = diskdir; + unsigned char disk = getfirstdevice(); + +#ifdef __APPLE2__ + // skip '/' + ++rootdir; +#endif + + send(SEND_FIRST, "

/

"); + + while (disk != INVALID_DEVICE) + { + if (getdevicedir(disk, diskdir, sizeof(diskdir))) + { + send(0, "", + rootdir, rootdir); + +#ifdef IMAGE + { + dhandle_t dio = dio_open(disk); + if (dio) + { + send(0, "", + rootdir, rootdir, dio_query_sectcount(dio)); + dio_close(dio); + } + } +#endif + } + disk = getnextdevice(disk); + } + send(SEND_LAST, "
Disk%s
Image%s"IMAGE"%u
"); + + printf("200\n"); +} + +void directory(const char* path) +{ + char* delimiter; + struct dirent *ent; + DIR *dir = opendir("."); + + if (!dir) + { + error(); + return; + } + + send(SEND_FIRST, "

%s

", path); + + delimiter = strrchr(path, '/'); + if (delimiter && delimiter != path) + { + *delimiter = '\0'; + send(0, "", path); + *delimiter = '/'; + } + else + { + send(0, ""); + } + + while (ent = readdir(dir)) + { + if (_DE_ISREG(ent->d_type)) + { +#ifdef __ATARI__ + send(0, "", + path, ent->d_name, ent->d_name); +#else + send(0, "", + path, ent->d_name, ent->d_name, ent->d_blocks); +#endif + continue; + } + if (_DE_ISDIR(ent->d_type)) + { + send(0, "", + path, ent->d_name, ent->d_name); + } + } + send(SEND_LAST, "
Directory..
Disk..
File%s
File%s%u
Directory%s
"); + + closedir(dir); + printf("200\n"); +} + +unsigned char file(const char* name) +{ + int size; + int file = open(name, O_RDONLY); + + if (file < 0) + { + return 0; + } + + send_type = HTTPD_RESPONSE_200_DATA; + while ((size = read(file, send_buffer, sizeof(send_buffer))) > 0) + { + httpd_send_response(send_type, send_buffer, size); + send_type = HTTPD_RESPONSE_NOHEADER; + } + + close(file); + printf("200\n"); + return 1; +} + +#ifdef IMAGE +void image() +{ + unsigned int sector = 0; + dhandle_t dio = dio_open(getcurrentdevice()); + + if (!dio) + { + error(); + return; + } + + send_size = dio_query_sectsize(dio); + send_type = HTTPD_RESPONSE_200_DATA; + +#ifdef __ATARI__ + #error "TODO: Send ATR header." + dio_query_sectcount(dio); +#endif + + while (!dio_read(dio, sector++, send_buffer)) + { + httpd_send_response(send_type, send_buffer, send_size); + send_type = HTTPD_RESPONSE_NOHEADER; + } + + dio_close(dio); + printf("200\n"); +} +#endif + +void http_server(unsigned long client, const char* method, const char* path) +{ + char* delimiter; + + printf("%s \"%s %s\" ", dotted_quad(client), method, path); + + if (stricmp(method, "get") || path[0] != '/') + { + error(); + return; + } + + if (path[1] == '\0') + { + root(); + return; + } + +#ifndef __APPLE2__ + // skip '/' + ++path; +#endif + + if (!chdir(path)) + { + directory(path); + return; + } + + delimiter = strrchr(path, '/'); + if (delimiter && delimiter != path) + { + *delimiter++ = '\0'; + if (!chdir(path) && file(delimiter)) + { + return; + } + } +#ifdef IMAGE + else + { + delimiter = strrchr(path, '.'); + if (delimiter) + { + *delimiter = '\0'; + if (!chdir(path)) + { + image(); + return; + } + } + } +#endif + + httpd_send_response(HTTPD_RESPONSE_404, 0, 0); + printf("404\n"); +} + +void error_exit(void) +{ + switch (ip65_error) + { + case IP65_ERROR_DEVICE_FAILURE: + printf("- No device found\n"); + break; + case IP65_ERROR_ABORTED_BY_USER: + printf("- User abort\n"); + break; + case IP65_ERROR_TIMEOUT_ON_RECEIVE: + printf("- Timeout\n"); + break; + default: + printf("- Error $%X\n", ip65_error); + } + + if (doesclrscrafterexit()) + { + printf("\nPress any key "); + cgetc(); + } + + exit(1); +} + +void main(void) +{ + char cwd[FILENAME_MAX]; + + getcwd(cwd, sizeof(cwd)); + +#ifdef __APPLE2__ + videomode(VIDEOMODE_80COL); +#endif + + printf("\nHttpFileServer65 v1.0" + "\n=====================" + "\n\nInitializing "); + if (ip65_init()) + { + error_exit(); + } + + printf("- Ok\n\nObtaining IP address "); + if (dhcp_init()) + { + error_exit(); + } + + printf("- Ok\n\nStarting server on %s\n\n", dotted_quad(cfg_ip)); + httpd_start(80, http_server); + + chdir(cwd); +}