mirror of
https://github.com/bobbimanners/emailler.git
synced 2024-10-01 21:56:43 +00:00
7ac8c55603
So far we were reading (much) more bytes from the W5100 than necessary when processing the HTTP header. The byte were memmove'd to the beginning of the buffer. However, when downloading a DOS 3.3 sector order disk image (*.dsk / *.do) then we need to place every DOS 3.3 sector at a certain point in the buffer to avoid additional memcpy's later. Therefore the HTTP header processing mustn't read (or rather commit) any body bytes from the W5100. So we now just check for the "\r\n\r\n" after each and every byte. This is of course less than optimal but small/simple - and the header isn't supposed to be that large anyway.
505 lines
9.1 KiB
C
505 lines
9.1 KiB
C
#include <cc65.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <conio.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <device.h>
|
|
|
|
#include "../inc/ip65.h"
|
|
#include "w5100.h"
|
|
#include "linenoise.h"
|
|
|
|
// Both pragmas are obligatory to have cc65 generate code
|
|
// suitable to access the W5100 auto-increment registers.
|
|
#pragma optimize (on)
|
|
#pragma static-locals (on)
|
|
|
|
char buffer[0x1000];
|
|
|
|
void ip65_error_exit(bool quit)
|
|
{
|
|
switch (ip65_error)
|
|
{
|
|
case IP65_ERROR_DEVICE_FAILURE:
|
|
printf("- No Uthernet II found\n");
|
|
break;
|
|
case IP65_ERROR_ABORTED_BY_USER:
|
|
printf("- User abort\n");
|
|
break;
|
|
case IP65_ERROR_TIMEOUT_ON_RECEIVE:
|
|
printf("- Timeout\n");
|
|
break;
|
|
case IP65_ERROR_MALFORMED_URL:
|
|
printf("- Malformed URL\n");
|
|
break;
|
|
case IP65_ERROR_DNS_LOOKUP_FAILED:
|
|
printf("- Lookup failed\n");
|
|
break;
|
|
default:
|
|
printf("- Error $%X\n", ip65_error);
|
|
}
|
|
if (quit)
|
|
{
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void file_error_exit(void)
|
|
{
|
|
printf("- ");
|
|
perror(NULL);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
void confirm_exit(void)
|
|
{
|
|
printf("\nPress any key ");
|
|
cgetc();
|
|
}
|
|
|
|
void reset_cwd(void)
|
|
{
|
|
chdir("");
|
|
}
|
|
|
|
char *self_path(const char *filename)
|
|
{
|
|
extern char **_argv[];
|
|
|
|
return strcat(strcpy(buffer, *_argv[0]), filename);
|
|
}
|
|
|
|
bool match(const char *filter, const char *string)
|
|
{
|
|
while (*filter)
|
|
{
|
|
if (!*string)
|
|
{
|
|
return false;
|
|
}
|
|
if (toupper(*filter++) != toupper(*string++))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void url_completion(const char *line, linenoiseCompletions *lc)
|
|
{
|
|
if (match(line, "http://"))
|
|
{
|
|
linenoiseAddCompletion(lc, "http://");
|
|
}
|
|
if (match(line, "http://www."))
|
|
{
|
|
linenoiseAddCompletion(lc, "http://www.");
|
|
}
|
|
}
|
|
|
|
void file_completion(const char *line, linenoiseCompletions *lc) {
|
|
char *lineptr = strrchr(line, '/');
|
|
|
|
// Add device names
|
|
if (lineptr == line)
|
|
{
|
|
unsigned char disk = getfirstdevice();
|
|
while (disk != INVALID_DEVICE)
|
|
{
|
|
if (getdevicedir(disk, buffer, sizeof(buffer)))
|
|
{
|
|
if (match(line, buffer))
|
|
{
|
|
linenoiseAddCompletion(lc, buffer);
|
|
}
|
|
}
|
|
disk = getnextdevice(disk);
|
|
}
|
|
}
|
|
|
|
// Add directory entries
|
|
else
|
|
{
|
|
DIR *dir;
|
|
struct dirent *ent;
|
|
char *bufferptr;
|
|
|
|
// Absolute or relative path
|
|
if (lineptr)
|
|
{
|
|
*lineptr = '\0';
|
|
dir = opendir(line);
|
|
*lineptr = '/';
|
|
++lineptr;
|
|
}
|
|
|
|
// Current directory
|
|
else
|
|
{
|
|
dir = opendir(".");
|
|
lineptr = (char*)line;
|
|
}
|
|
|
|
if (!dir)
|
|
{
|
|
return;
|
|
}
|
|
|
|
strcpy(buffer, line);
|
|
bufferptr = buffer + (lineptr - line);
|
|
|
|
while (ent = readdir(dir))
|
|
{
|
|
if (match(lineptr, ent->d_name))
|
|
{
|
|
strcpy(bufferptr, ent->d_name);
|
|
linenoiseAddCompletion(lc, buffer);
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
}
|
|
|
|
char *get_argument(char arg, const char *name, const char *history,
|
|
linenoiseCompletionCallback *completion)
|
|
{
|
|
extern int _argc;
|
|
extern char **_argv[];
|
|
char *val;
|
|
|
|
linenoiseHistoryReset();
|
|
linenoiseHistoryLoad(self_path(history));
|
|
|
|
if (_argc > arg)
|
|
{
|
|
val = *_argv[arg];
|
|
printf("%s: %s", name, val);
|
|
}
|
|
else
|
|
{
|
|
char prompt[10];
|
|
|
|
linenoiseSetCompletionCallback(completion);
|
|
|
|
snprintf(prompt, sizeof(prompt), "%s? ", name);
|
|
val = linenoise(prompt);
|
|
if (!val || !*val)
|
|
{
|
|
putchar('\n');
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
linenoiseHistoryAdd(val);
|
|
linenoiseHistorySave(self_path(history));
|
|
|
|
return val;
|
|
}
|
|
|
|
void exit_on_key(void)
|
|
{
|
|
if (input_check_for_abort_key())
|
|
{
|
|
w5100_disconnect();
|
|
printf("- User abort\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void exit_on_disconnect(void)
|
|
{
|
|
if (!w5100_connected())
|
|
{
|
|
printf("- Connection lost\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void receive_file(const char *name)
|
|
{
|
|
uint16_t i;
|
|
int file;
|
|
uint16_t rcv;
|
|
bool cont = true;
|
|
uint16_t len = 0;
|
|
uint32_t size = 0;
|
|
|
|
printf("- Ok\n\nOpening file ");
|
|
file = open(name, O_WRONLY | O_CREAT | O_TRUNC);
|
|
if (file == -1)
|
|
{
|
|
w5100_disconnect();
|
|
file_error_exit();
|
|
}
|
|
printf("- Ok\n\n");
|
|
|
|
while (cont)
|
|
{
|
|
exit_on_key();
|
|
|
|
rcv = w5100_receive_request();
|
|
if (!rcv)
|
|
{
|
|
cont = w5100_connected();
|
|
if (cont)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (rcv > sizeof(buffer) - len)
|
|
{
|
|
rcv = sizeof(buffer) - len;
|
|
}
|
|
|
|
{
|
|
// One less to allow for faster pre-increment below
|
|
char *dataptr = buffer + len - 1;
|
|
for (i = 0; i < rcv; ++i)
|
|
{
|
|
// The variable is necessary to have cc65 generate code
|
|
// suitable to access the W5100 auto-increment register.
|
|
char data = *w5100_data;
|
|
*++dataptr = data;
|
|
}
|
|
}
|
|
|
|
w5100_receive_commit(rcv);
|
|
len += rcv;
|
|
|
|
if (cont && len < sizeof(buffer))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
cprintf("\rWriting ");
|
|
if (write(file, buffer, len) != len)
|
|
{
|
|
w5100_disconnect();
|
|
file_error_exit();
|
|
}
|
|
size += len;
|
|
cprintf("%lu bytes ", size);
|
|
|
|
len = 0;
|
|
}
|
|
|
|
printf("- Ok\n\nClosing file ");
|
|
if (close(file))
|
|
{
|
|
w5100_disconnect();
|
|
file_error_exit();
|
|
}
|
|
}
|
|
|
|
int main(int, char *argv[])
|
|
{
|
|
uint16_t i;
|
|
char *arg;
|
|
uint8_t drv_init = DRV_INIT_DEFAULT;
|
|
|
|
if (doesclrscrafterexit())
|
|
{
|
|
atexit(confirm_exit);
|
|
}
|
|
|
|
if (!*getcwd(buffer, sizeof(buffer)))
|
|
{
|
|
// Set a defined working dir before potentially changing devices
|
|
chdir(getdevicedir(getcurrentdevice(), buffer, sizeof(buffer)));
|
|
atexit(reset_cwd);
|
|
}
|
|
|
|
// Trim program name from argv[0] to prepare usage in self_path()
|
|
arg = strrchr(argv[0], '/');
|
|
if (arg) {
|
|
*(arg + 1) = '\0';
|
|
}
|
|
else
|
|
{
|
|
*argv[0] = '\0';
|
|
}
|
|
|
|
{
|
|
int file;
|
|
|
|
printf("\nSetting slot ");
|
|
file = open(self_path("ethernet.slot"), O_RDONLY);
|
|
if (file != -1)
|
|
{
|
|
read(file, &drv_init, 1);
|
|
close(file);
|
|
drv_init &= ~'0';
|
|
}
|
|
}
|
|
|
|
printf("- %d\n\nInitializing ", drv_init);
|
|
if (ip65_init(drv_init))
|
|
{
|
|
ip65_error_exit(true);
|
|
}
|
|
|
|
// Abort on Ctrl-C to be consistent with Linenoise
|
|
abort_key = 0x83;
|
|
|
|
printf("- Ok\n\nObtaining IP address ");
|
|
if (dhcp_init())
|
|
{
|
|
ip65_error_exit(true);
|
|
}
|
|
printf("- Ok\n\n");
|
|
|
|
// Copy IP config from IP65 to W5100
|
|
w5100_config();
|
|
|
|
while (true)
|
|
{
|
|
arg = get_argument(1, "URL", "wget.urls", url_completion);
|
|
|
|
printf("\n\nProcessing URL ");
|
|
if (!url_parse(arg))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Do not actually exit
|
|
ip65_error_exit(false);
|
|
printf("\n");
|
|
}
|
|
printf("- Ok\n\n");
|
|
|
|
arg = get_argument(2, "File", "wget.files", file_completion);
|
|
|
|
printf("\n\nConnecting to %s:%d ", dotted_quad(url_ip), url_port);
|
|
|
|
if (!w5100_connect(url_ip, url_port))
|
|
{
|
|
printf("- Connect failed\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
printf("- Ok\n\nSending request ");
|
|
{
|
|
uint16_t snd;
|
|
uint16_t pos = 0;
|
|
uint16_t len = strlen(url_selector);
|
|
|
|
while (len)
|
|
{
|
|
exit_on_key();
|
|
|
|
snd = w5100_send_request();
|
|
if (!snd)
|
|
{
|
|
exit_on_disconnect();
|
|
continue;
|
|
}
|
|
|
|
if (len < snd)
|
|
{
|
|
snd = len;
|
|
}
|
|
|
|
{
|
|
// One less to allow for faster pre-increment below
|
|
char *dataptr = url_selector + pos - 1;
|
|
for (i = 0; i < snd; ++i)
|
|
{
|
|
// The variable is necessary to have cc65 generate code
|
|
// suitable to access the W5100 auto-increment register.
|
|
char data = *++dataptr;
|
|
*w5100_data = data;
|
|
}
|
|
}
|
|
|
|
w5100_send_commit(snd);
|
|
len -= snd;
|
|
pos += snd;
|
|
}
|
|
}
|
|
|
|
printf("- Ok\n\nReceiving response ");
|
|
{
|
|
uint16_t rcv;
|
|
bool body = false;
|
|
uint16_t len = 0;
|
|
|
|
while (!body)
|
|
{
|
|
exit_on_key();
|
|
|
|
rcv = w5100_receive_request();
|
|
if (!rcv)
|
|
{
|
|
exit_on_disconnect();
|
|
continue;
|
|
}
|
|
|
|
if (rcv > sizeof(buffer) - len)
|
|
{
|
|
rcv = sizeof(buffer) - len;
|
|
}
|
|
|
|
{
|
|
// One less to allow for faster pre-increment below
|
|
char *dataptr = buffer + len - 1;
|
|
for (i = 0; i < rcv; ++i)
|
|
{
|
|
// The variable is necessary to have cc65 generate code
|
|
// suitable to access the W5100 auto-increment register.
|
|
char data = *w5100_data;
|
|
*++dataptr = data;
|
|
|
|
if (!memcmp(dataptr - 3, "\r\n\r\n", 4))
|
|
{
|
|
rcv = i + 1;
|
|
body = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
w5100_receive_commit(rcv);
|
|
len += rcv;
|
|
|
|
// No body found in full buffer
|
|
if (len == sizeof(buffer))
|
|
{
|
|
printf("- Invalid response\n");
|
|
w5100_disconnect();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// Replace "HTTP/1.1" with "HTTP/1.0"
|
|
buffer[7] = '0';
|
|
|
|
if (!match("HTTP/1.0 200", buffer))
|
|
{
|
|
if (match("HTTP/1.0", buffer))
|
|
{
|
|
char *eol = strchr(buffer,'\r');
|
|
*eol = '\0';
|
|
printf("- Status%s\n", buffer + 8);
|
|
}
|
|
else
|
|
{
|
|
printf("- Unknown response\n");
|
|
}
|
|
w5100_disconnect();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
receive_file(arg);
|
|
|
|
printf("- Ok\n\nDisconnecting ");
|
|
w5100_disconnect();
|
|
|
|
printf("- Ok\n");
|
|
return EXIT_SUCCESS;
|
|
}
|