mirror of
https://github.com/bobbimanners/emailler.git
synced 2025-04-07 21:37:06 +00:00
In progress checkin of SMTP65. Not working yet.
This commit is contained in:
parent
260e9dde20
commit
b616df27f7
@ -45,7 +45,7 @@ TCP =\
|
||||
tweet65 \
|
||||
pop65-slow
|
||||
|
||||
bin: wget65.bin pop65.bin email.bin
|
||||
bin: wget65.bin pop65.bin smtp65.bin email.bin
|
||||
|
||||
wget65.bin: w5100.c w5100_http.c linenoise.c
|
||||
wget65.bin: IP65LIB = ../ip65/ip65.lib
|
||||
@ -55,6 +55,10 @@ pop65.bin: w5100.c
|
||||
pop65.bin: IP65LIB = ../ip65/ip65.lib
|
||||
pop65.bin: A2_DRIVERLIB = ../drivers/ip65_apple2_uther2.lib
|
||||
|
||||
smtp65.bin: w5100.c
|
||||
smtp65.bin: IP65LIB = ../ip65/ip65.lib
|
||||
smtp65.bin: A2_DRIVERLIB = ../drivers/ip65_apple2_uther2.lib
|
||||
|
||||
date65.bin hfs65.bin tweet65.bin: CL65FLAGS = --start-addr 0x0C00 apple2enh-iobuf-0800.o
|
||||
|
||||
telnet65.com: ATARI_CFG = atrtelnet.cfg
|
||||
@ -139,8 +143,10 @@ ip65.dsk: bin
|
||||
# java -jar $(AC) -p $@ tweet65.system sys < $(CC65)/apple2enh/util/loader.system
|
||||
java -jar $(AC) -as $@ pop65 < pop65.bin
|
||||
java -jar $(AC) -p $@ pop65.system sys < $(CC65)/apple2enh/util/loader.system
|
||||
java -jar $(AC) -as $@ wget65 < wget65.bin
|
||||
java -jar $(AC) -p $@ wget65.system sys < $(CC65)/apple2enh/util/loader.system
|
||||
java -jar $(AC) -as $@ smtp65 < smtp65.bin
|
||||
java -jar $(AC) -p $@ smtp65.system sys < $(CC65)/apple2enh/util/loader.system
|
||||
# java -jar $(AC) -as $@ wget65 < wget65.bin
|
||||
# java -jar $(AC) -p $@ wget65.system sys < $(CC65)/apple2enh/util/loader.system
|
||||
java -jar $(AC) -as $@ telnet65 < telnet65.bin
|
||||
java -jar $(AC) -as $@ email < email.bin
|
||||
java -jar $(AC) -p $@ email.system sys < $(CC65)/apple2enh/util/loader.system
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
// TODO:
|
||||
// - Automatically insert date
|
||||
// - Parse email addresses in < angle brackets >
|
||||
// - Fix terrible scrollback algorithm!!
|
||||
// - Editor for email composition functions
|
||||
|
||||
@ -96,6 +97,8 @@ void readconfigfile(void) {
|
||||
fscanf(fp, "%s", cfg_server);
|
||||
fscanf(fp, "%s", cfg_user);
|
||||
fscanf(fp, "%s", cfg_pass);
|
||||
fscanf(fp, "%s", cfg_smtp_server);
|
||||
fscanf(fp, "%s", cfg_smtp_domain);
|
||||
fscanf(fp, "%s", cfg_emaildir);
|
||||
fscanf(fp, "%s", cfg_emailaddr);
|
||||
fclose(fp);
|
||||
|
@ -5,9 +5,11 @@
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
// Configuration params from POP65.CFG
|
||||
char cfg_server[80]; // IP of POP3 server
|
||||
char cfg_user[80]; // Username
|
||||
char cfg_pass[80]; // Password
|
||||
char cfg_server[40]; // IP of POP3 server
|
||||
char cfg_user[40]; // Username
|
||||
char cfg_pass[40]; // Password
|
||||
char cfg_smtp_server[40]; // IP of SMTP server
|
||||
char cfg_smtp_domain[40]; // Our domain
|
||||
char cfg_emaildir[80]; // ProDOS directory at root of email tree
|
||||
char cfg_emailaddr[80]; // Our email address
|
||||
|
||||
|
@ -297,6 +297,8 @@ void readconfigfile(void) {
|
||||
fscanf(fp, "%s", cfg_server);
|
||||
fscanf(fp, "%s", cfg_user);
|
||||
fscanf(fp, "%s", cfg_pass);
|
||||
fscanf(fp, "%s", cfg_smtp_server);
|
||||
fscanf(fp, "%s", cfg_smtp_domain);
|
||||
fscanf(fp, "%s", cfg_emaildir);
|
||||
fclose(fp);
|
||||
}
|
||||
@ -324,7 +326,7 @@ int16_t get_line(FILE *fp) {
|
||||
if (found) {
|
||||
rd = i + 1;
|
||||
linebuf[j - 1] = '\0'; // Remove LF from end
|
||||
return j - 2;
|
||||
return j - 1;
|
||||
}
|
||||
buflen = fread(buf, 1, READSZ, fp);
|
||||
if (buflen == 0) {
|
||||
@ -421,7 +423,7 @@ void update_inbox(uint16_t nummsgs) {
|
||||
hdrs.tag = ' ';
|
||||
while ((chars = get_line(fp)) != -1) {
|
||||
if (headers) {
|
||||
headerchars += chars + 1; // Don't forget the LF we deleted
|
||||
headerchars += chars;
|
||||
if (!strncmp(linebuf, "Date: ", 6)) {
|
||||
copyheader(hdrs.date, linebuf + 6, 39);
|
||||
hdrs.date[39] = '\0';
|
||||
@ -464,6 +466,8 @@ void main(void) {
|
||||
uint32_t bytes;
|
||||
|
||||
videomode(VIDEOMODE_80COL);
|
||||
printf("%cemai//er POP3%c\n", 0x0f, 0x0e);
|
||||
|
||||
printf("\nReading POP65.CFG -");
|
||||
readconfigfile();
|
||||
printf(" Ok");
|
||||
|
491
apps/smtp65.c
Normal file
491
apps/smtp65.c
Normal file
@ -0,0 +1,491 @@
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// SMTP65
|
||||
// Simple Mail Transport Protocol (SMTO) Client for IP65
|
||||
// https://www.ietf.org/rfc/rfc821
|
||||
// (Based on IP65's wget65.c)
|
||||
// Bobbi June 2020
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cc65.h>
|
||||
#include <errno.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 "../inc/ip65.h"
|
||||
#include "w5100.h"
|
||||
#include "w5100_http.h"
|
||||
#include "linenoise.h"
|
||||
|
||||
#include "email_common.h"
|
||||
|
||||
#define BACKSPACE 8
|
||||
|
||||
// Both pragmas are obligatory to have cc65 generate code
|
||||
// suitable to access the W5100 auto-increment registers.
|
||||
#pragma optimize (on)
|
||||
#pragma static-locals (on)
|
||||
|
||||
#define NETBUFSZ 1500
|
||||
#define LINEBUFSZ 1000 // According to RFC2822 Section 2.1.1 (998+CRLF)
|
||||
#define READSZ 1024 // Must be less than NETBUFSZ to fit in buf[]
|
||||
|
||||
static unsigned char buf[NETBUFSZ+1]; // One extra byte for null terminator
|
||||
static char linebuf[LINEBUFSZ];
|
||||
|
||||
char filename[80];
|
||||
int len;
|
||||
FILE *fp;
|
||||
uint32_t filesize;
|
||||
|
||||
/*
|
||||
* Keypress before quit
|
||||
*/
|
||||
void confirm_exit(void) {
|
||||
printf("\nPress any key ");
|
||||
cgetc();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for all non IP65 errors
|
||||
*/
|
||||
void error_exit() {
|
||||
confirm_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called if IP65 call fails
|
||||
*/
|
||||
void ip65_error_exit(void) {
|
||||
printf("%s\n", ip65_strerror(ip65_error));
|
||||
confirm_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Print message to the console, stripping extraneous CRLF stuff
|
||||
* from the end.
|
||||
*/
|
||||
void print_strip_crlf(char *s) {
|
||||
uint8_t i = 0;
|
||||
while ((s[i] != '\0') && (s[i] != '\r') && (s[i] != '\n'))
|
||||
putchar(s[i++]);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/*
|
||||
* Spinner while uploading files
|
||||
*/
|
||||
void spinner(uint32_t sz, uint8_t final) {
|
||||
static char chars[] = "|/-\\";
|
||||
static char buf[10] = "";
|
||||
static uint8_t i = 0;
|
||||
uint8_t j;
|
||||
for (j = 0; j < strlen(buf); ++j)
|
||||
putchar(BACKSPACE);
|
||||
if (final) {
|
||||
sprintf(buf, " [%lu]\n", sz);
|
||||
printf("%s", buf);
|
||||
strcpy(buf, "");
|
||||
}
|
||||
else {
|
||||
sprintf(buf, "%c %lu", chars[(i++) % 4], sz);
|
||||
printf("%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
#define DO_SEND 1 // For do_send param
|
||||
#define DONT_SEND 0 // For do_send param
|
||||
#define CMD_MODE 0 // For mode param
|
||||
#define DATA_MODE 1 // For mode param
|
||||
|
||||
// Modified verson of w5100_http_open from w5100_http.c
|
||||
// Sends a TCP message and receives a the first packet of the response.
|
||||
// sendbuf is the buffer to send (null terminated)
|
||||
// recvbuf is the buffer into which the received message will be written
|
||||
// length is the length of recvbuf[]
|
||||
// do_send Do the sending if true, otherwise skip
|
||||
// mode Binary mode for received message, maybe first block of long message
|
||||
bool w5100_tcp_send_recv(char* sendbuf, char* recvbuf, size_t length,
|
||||
uint8_t do_send, uint8_t mode) {
|
||||
|
||||
if (do_send == DO_SEND) {
|
||||
uint16_t snd;
|
||||
uint16_t pos = 0;
|
||||
uint16_t len = strlen(sendbuf); // TODO
|
||||
|
||||
if (strncmp(sendbuf, "PASS", 4) == 0)
|
||||
printf(">PASS ****\n");
|
||||
else {
|
||||
putchar('>');
|
||||
print_strip_crlf(sendbuf);
|
||||
}
|
||||
|
||||
while (len) {
|
||||
if (input_check_for_abort_key())
|
||||
{
|
||||
printf("User abort\n");
|
||||
w5100_disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
snd = w5100_send_request();
|
||||
if (!snd) {
|
||||
if (!w5100_connected()) {
|
||||
printf("Connection lost\n");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (len < snd)
|
||||
snd = len;
|
||||
|
||||
{
|
||||
// One less to allow for faster pre-increment below
|
||||
const char *dataptr = sendbuf + pos - 1;
|
||||
uint16_t i;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (mode == DATA_MODE) {
|
||||
//
|
||||
// Handle email body
|
||||
//
|
||||
uint16_t rcv, written;
|
||||
uint16_t len = 0;
|
||||
uint8_t cont = 1;
|
||||
|
||||
// Backspace to put spinner on same line as RETR
|
||||
for (rcv = 0; rcv < 15; ++rcv)
|
||||
putchar(BACKSPACE);
|
||||
|
||||
filesize = 0;
|
||||
|
||||
while(cont) {
|
||||
if (input_check_for_abort_key()) {
|
||||
printf("User abort\n");
|
||||
w5100_disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
rcv = w5100_receive_request();
|
||||
if (!rcv) {
|
||||
cont = w5100_connected();
|
||||
if (cont)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rcv > length - len)
|
||||
rcv = length - len;
|
||||
|
||||
{
|
||||
// One less to allow for faster pre-increment below
|
||||
char *dataptr = recvbuf + len - 1;
|
||||
uint16_t i;
|
||||
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;
|
||||
|
||||
// TODO -- check we are not looking before start of recvbuf here!!
|
||||
// TODO -- this doesn't handle the case where the sequence is split across
|
||||
// packets!!!
|
||||
if (!memcmp(dataptr - 4, "\r\n.\r\n", 5))
|
||||
cont = 0;
|
||||
}
|
||||
}
|
||||
w5100_receive_commit(rcv);
|
||||
len += rcv;
|
||||
|
||||
written = fwrite(recvbuf, 1, len, fp);
|
||||
if (written != len) {
|
||||
printf("Write error");
|
||||
fclose(fp);
|
||||
error_exit();
|
||||
}
|
||||
|
||||
filesize += len;
|
||||
spinner(filesize, 0);
|
||||
len = 0;
|
||||
}
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
//
|
||||
// Handle short single packet ASCII text responses
|
||||
//
|
||||
uint16_t rcv;
|
||||
uint16_t len = 0;
|
||||
|
||||
while(1) {
|
||||
if (input_check_for_abort_key()) {
|
||||
printf("User abort\n");
|
||||
w5100_disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
rcv = w5100_receive_request();
|
||||
if (rcv)
|
||||
break;
|
||||
if (!w5100_connected()) {
|
||||
printf("Connection lost\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (rcv > length - len)
|
||||
rcv = length - len;
|
||||
|
||||
{
|
||||
// One less to allow for faster pre-increment below
|
||||
char *dataptr = recvbuf + len - 1;
|
||||
uint16_t i;
|
||||
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;
|
||||
}
|
||||
putchar('<');
|
||||
print_strip_crlf(recvbuf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check expected string from server
|
||||
*/
|
||||
void expect(char *buf, char *s) {
|
||||
if (strncmp(buf, s, strlen(s)) != 0) {
|
||||
printf("\nExpected '%s' got '%s\n", s, buf);
|
||||
error_exit();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read parms from POP65.CFG
|
||||
*/
|
||||
void readconfigfile(void) {
|
||||
fp = fopen("POP65.CFG", "r");
|
||||
if (!fp) {
|
||||
puts("Can't open config file POP65.CFG");
|
||||
error_exit();
|
||||
}
|
||||
fscanf(fp, "%s", cfg_server);
|
||||
fscanf(fp, "%s", cfg_user);
|
||||
fscanf(fp, "%s", cfg_pass);
|
||||
fscanf(fp, "%s", cfg_smtp_server);
|
||||
fscanf(fp, "%s", cfg_smtp_domain);
|
||||
fscanf(fp, "%s", cfg_emaildir);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a text file a line at a time leaving the line in linebuf[]
|
||||
* Returns number of chars in the line, or -1 if EOF.
|
||||
* Expects Apple ][ style line endings (CR) and does no conversion
|
||||
*/
|
||||
int16_t get_line(FILE *fp) {
|
||||
static uint16_t rd = 0;
|
||||
static uint16_t buflen = 0;
|
||||
uint8_t found = 0;
|
||||
uint16_t j = 0;
|
||||
uint16_t i;
|
||||
while (1) {
|
||||
for (i = rd; i < buflen; ++i) {
|
||||
linebuf[j++] = buf[i];
|
||||
if (linebuf[j - 1] == '\r') {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
rd = i + 1;
|
||||
linebuf[j] = '\0';
|
||||
return j;
|
||||
}
|
||||
buflen = fread(buf, 1, READSZ, fp);
|
||||
if (buflen == 0) {
|
||||
rd = 0;
|
||||
return -1; // Hit EOF before we found EOL
|
||||
}
|
||||
rd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
static char sendbuf[80], sender[80], recipient[80];
|
||||
uint8_t eth_init = ETH_INIT_DEFAULT;
|
||||
uint8_t have_recipient, have_sender, linecount;
|
||||
DIR *dp;
|
||||
struct dirent *d;
|
||||
|
||||
videomode(VIDEOMODE_80COL);
|
||||
printf("%cemai//er SMTP%c\n", 0x0f, 0x0e);
|
||||
|
||||
printf("\nReading POP65.CFG -");
|
||||
readconfigfile();
|
||||
printf(" Ok");
|
||||
|
||||
{
|
||||
int file;
|
||||
|
||||
printf("\nSetting slot - ");
|
||||
file = open("ethernet.slot", O_RDONLY);
|
||||
if (file != -1) {
|
||||
read(file, ð_init, 1);
|
||||
close(file);
|
||||
eth_init &= ~'0';
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d\nInitializing %s - ", eth_init, eth_name);
|
||||
if (ip65_init(eth_init)) {
|
||||
ip65_error_exit();
|
||||
}
|
||||
|
||||
// Abort on Ctrl-C to be consistent with Linenoise
|
||||
abort_key = 0x83;
|
||||
|
||||
printf("Ok\nObtaining IP address - ");
|
||||
if (dhcp_init()) {
|
||||
ip65_error_exit();
|
||||
}
|
||||
|
||||
// Copy IP config from IP65 to W5100
|
||||
w5100_config(eth_init);
|
||||
|
||||
printf("Ok\nConnecting to %s - ", cfg_smtp_server);
|
||||
|
||||
if (!w5100_connect(parse_dotted_quad(cfg_smtp_server), 25)) {
|
||||
printf("Fail\n");
|
||||
error_exit();
|
||||
}
|
||||
|
||||
printf("Ok\n\n");
|
||||
|
||||
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DONT_SEND, CMD_MODE)) {
|
||||
error_exit();
|
||||
}
|
||||
expect(buf, "220 ");
|
||||
|
||||
sprintf(sendbuf, "HELO %s\r\n", cfg_smtp_domain);
|
||||
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
|
||||
error_exit();
|
||||
}
|
||||
expect(buf, "250 ");
|
||||
|
||||
sprintf(filename, "%s/OUTBOX", cfg_emaildir);
|
||||
dp = opendir(filename);
|
||||
if (!dp) {
|
||||
printf("Can't open dir %s\n", filename);
|
||||
error_exit();
|
||||
}
|
||||
|
||||
while (d = readdir(dp)) {
|
||||
|
||||
sprintf(filename, "%s/OUTBOX/%s", cfg_emaildir, d->d_name);
|
||||
fp = fopen(filename, "rb");
|
||||
if (!fp) {
|
||||
printf("Can't open %s\n", d->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip special files
|
||||
if (!strncmp(d->d_name, "EMAIL.DB", 8))
|
||||
continue;
|
||||
if (!strncmp(d->d_name, "NEXT.EMAIL", 10))
|
||||
continue;
|
||||
|
||||
puts(d->d_name);
|
||||
|
||||
have_sender = have_recipient = linecount = 0;
|
||||
|
||||
while (1) {
|
||||
if ((get_line(fp) == -1) || (linecount == 20)) {
|
||||
printf("Didn't find to/from headers in %s\n", d->d_name);
|
||||
goto skiptonext;
|
||||
}
|
||||
++linecount;
|
||||
if (!strncmp(linebuf, "From: ", 6)) {
|
||||
strcpy(recipient, linebuf + 6);
|
||||
have_recipient = 1;
|
||||
if (have_sender)
|
||||
break;
|
||||
} else if (!strncmp(linebuf, "To: ", 4)) {
|
||||
strcpy(sender, linebuf + 4);
|
||||
have_sender = 1;
|
||||
if (have_recipient)
|
||||
break;
|
||||
}
|
||||
// TODO Handle optional cc
|
||||
}
|
||||
|
||||
// TODO Handle multiple comma-separated recipients
|
||||
|
||||
sprintf(sendbuf, "MAIL FROM:<%s>\r\n", sender);
|
||||
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
|
||||
error_exit();
|
||||
}
|
||||
expect(buf, "250 ");
|
||||
|
||||
sprintf(sendbuf, "RCPT TO:<%s>\r\n", recipient);
|
||||
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
|
||||
error_exit();
|
||||
}
|
||||
expect(buf, "250 ");
|
||||
|
||||
sprintf(sendbuf, "DATA");
|
||||
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
|
||||
error_exit();
|
||||
}
|
||||
expect(buf, "354 ");
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
// TODO NOW SEND ENTIRE BODY OF FILE
|
||||
|
||||
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DONT_SEND, CMD_MODE)) {
|
||||
error_exit();
|
||||
}
|
||||
expect(buf, "250 OK");
|
||||
|
||||
skiptonext:
|
||||
fclose(fp);
|
||||
|
||||
//TODO: Copy file to SENT & remove from here
|
||||
|
||||
}
|
||||
closedir(dp);
|
||||
|
||||
// Ignore any error - can be a race condition where other side
|
||||
// disconnects too fast and we get an error
|
||||
w5100_tcp_send_recv("QUIT\r\n", buf, NETBUFSZ, DO_SEND, CMD_MODE);
|
||||
|
||||
printf("Disconnecting\n");
|
||||
w5100_disconnect();
|
||||
|
||||
confirm_exit();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user