Compare commits

...

44 Commits

Author SHA1 Message Date
Bobbi Webber-Manners
9fdedd46bb Fix copy/paste typo. 2022-09-28 23:45:16 -04:00
Bobbi Webber-Manners
9fa090f34d Release 2.1.14. Migrated to latest IP65 version. 2022-09-14 16:23:13 -04:00
Bobbi Webber-Manners
95615458aa Rebased on latest IP65. Needs latest CC65 to build. 2022-09-14 13:02:41 -04:00
Bobbi Webber-Manners
d8d532fd72 Bump up version number to 2.1.14 2022-09-13 20:13:21 -04:00
Bobbi Webber-Manners
7c402facc7 attacher.c: Turn off optimizer for inline asm. 2022-09-13 20:08:50 -04:00
Bobbi Webber-Manners
7bb47d4b85 email.c: Turn off optimizer for inline asm 2022-09-13 20:05:12 -04:00
Bobbi Webber-Manners
d015ca310c
README-gmail-gateway.md: add line to force IPv4 2022-09-04 20:19:21 -04:00
Bobbi Webber-Manners
e102d732b0
Update README-gmail-gateway.md to add libsasl2-modules package 2022-09-04 20:17:02 -04:00
Bobbi Webber-Manners
6106701fa8 Added 2.1.13 disk image. 2021-11-16 15:15:11 -05:00
Bobbi Webber-Manners
077bddc9f1 Bump upversion to 2.1.13. 2021-11-16 15:03:32 -05:00
Bobbi Webber-Manners
e123dc2dbc NNTP65: Change to way filename to delete is computed. 2021-11-16 15:03:11 -05:00
Bobbi Webber-Manners
675073a6c9 SMTP65/NNTP65.UP: Stop looking for headers on finding blank line 2021-11-16 15:02:03 -05:00
Bobbi Webber-Manners
4e482b14cb Fixed memory exhaustion bug in EMAIL.SYSTEM. 2021-11-14 20:13:18 -05:00
Bobbi Webber-Manners
ef07cc8b88 2.1.11 release. 2021-11-13 13:12:12 -05:00
Bobbi Webber-Manners
c2b4773e1c Improved tagged message prompt. Allow OA-/ for help. 2021-11-13 13:11:08 -05:00
Bobbi Webber-Manners
305c7bcf34 Fix to REBUILD.SYSTEM to handle empty email dirs. 2021-11-12 22:26:45 -05:00
Bobbi Webber-Manners
6e9c751f53 Fixed 2.1.9 release, which was generated incorrectly! 2021-11-12 17:10:02 -05:00
Bobbi Webber-Manners
6613ec218c Added missing releases to 'releases' dir. 2021-11-12 17:04:24 -05:00
Bobbi Webber-Manners
44c46943b0 Rebuild: Initialize max/min email number. 2021-11-12 16:56:22 -05:00
Bobbi Webber-Manners
52f6b664e4 Fixed #65 - Quoted-Printable ? char bug 2021-08-28 23:19:48 -04:00
Bobbi Webber-Manners
9c4baa170d EDIT: Only disconnect/reconnect s3d2 (not s3d1) 2021-07-05 21:26:20 -04:00
Bobbi Webber-Manners
e943e0cca1 EMAIL: Improved parsing of MIME boundaries 2021-07-02 17:21:33 -04:00
Bobbi Webber-Manners
80bf13eb69 NNTP65: Added NNTP65.LOG file, and summary at end 2021-07-01 19:47:28 -04:00
Bobbi Webber-Manners
004c2cb1dc
Merge pull request #57 from GregWildman/master
Allow skipping of authentication for NNTP server
2021-07-01 14:19:23 -04:00
Bobbi Webber-Manners
85f6fab6a9 EMAIL: Support multiline Content-Type in email body 2021-06-29 23:53:13 -04:00
Bobbi Webber-Manners
6919adb9be EMAIL: Allow boundary= on new line when parsing hdrs 2021-06-29 19:47:49 -04:00
Bobbi Webber-Manners
6b1cde52c1 EDIT: Fixed get_email_body() MIME handling 2021-06-27 22:16:18 -04:00
Bobbi Webber-Manners
d7ec996cc8 EMAIL: Improvements to MIME boundary handling 2021-06-27 20:46:29 -04:00
Bobbi Webber-Manners
ba19bc4f57 EMAIL: Fixed whoopsie in last commit 2021-06-27 17:58:30 -04:00
Bobbi Webber-Manners
19ffeb1c8a EMAIL: Support for QP 'From:' headers 2021-06-27 17:57:18 -04:00
Bobbi Webber-Manners
f63fe7829f EDIT/ATTACHER: Fix to corner case drawing bug in file_ui() 2021-06-20 19:43:16 -04:00
Greg Wildman
677137e851 Update documentation and default config files 2021-06-18 08:17:09 +02:00
Greg Wildman
332a5bb7e1 Fix default NNTP port and allow skipping of client authentication to NNTP server 2021-06-17 16:51:49 +02:00
Greg Wildman
07bc9b40d8 Update documentation 2021-06-17 16:15:27 +02:00
Greg Wildman
432de5ad21 Fix default NNTP port and allow skipping of client authentication to NNTP server 2021-06-17 15:58:28 +02:00
Bobbi Webber-Manners
82835c508d Updated docs to include PRINT65. 2021-06-13 18:22:18 -04:00
Bobbi Webber-Manners
733edc18c5 Bumped up version to 2.1.6 2021-06-13 18:02:03 -04:00
Bobbi Webber-Manners
3f9ad451e2 PRINT65 - print to Jetdirect printer over net 2021-06-13 17:59:38 -04:00
Bobbi Webber-Manners
84bcdc2043 EDIT: Blank one more char for 'Saving ...' msg. 2021-06-11 20:22:37 -04:00
Bobbi Webber-Manners
e8c726f2fb EDIT: More status line fixup 2021-06-11 19:26:45 -04:00
Bobbi Webber-Manners
89e5de0374 EDIT: Do not highlight on find. Beep on not found. 2021-06-11 19:13:04 -04:00
Bobbi Webber-Manners
2ca9e4769e EDIT: Improved status bar. Bumped to v1.58. 2021-06-11 18:53:20 -04:00
Bobbi Webber-Manners
080d952737 Updated 'releases' directory 2021-06-05 13:12:12 -04:00
Bobbi Webber-Manners
1655c60f93 Bumped up version to 2.1.5. 2021-06-05 12:41:44 -04:00
61 changed files with 963 additions and 415 deletions

View File

@ -82,7 +82,7 @@ Install the packages with root privs on the Pi:
```
sudo apt update
sudo apt upgrade
sudo apt install postfix postfix-pcre
sudo apt install postfix postfix-pcre libsasl2-modules
sudo apt install dovecot-common dovecot-pop3d
sudo apt install fetchmail
```
@ -134,7 +134,7 @@ We will modify a number of configuration files:
- `/etc/postfix/sasl/sasl_passwd`
- `/etc/postfix/sasl/sasl_passwd.db`
Once Dovecot has been configured, the service may be controlled as follows:
Once Postfix has been configured, the service may be controlled as follows:
- `systemctl start postfix` - start service.
- `systemctl stop postfix` - stop service.
- `systemctl status postfix` - status of service.
@ -179,6 +179,10 @@ My home network is 192.168.10.0/24, so I added it here:
`mynetworks = 192.168.10.0/24 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128`.
You should adjust this line to match your own LAN subnet.
I had an issue where the Pi was unable to connect to Google's SMTP server.
It turned out it was trying to use IPv6, so I forced IPv4 as follows:
`inet_protocols = ipv4`
Finally I added the following block of settings to enabled SASL authentication
when talking to Gmail:
@ -245,7 +249,7 @@ mynetworks = 192.168.10.0/24 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
inet_protocols = ipv4
# Enable SASL authentication
smtp_sasl_auth_enable = yes

43
README-print65.md Normal file
View File

@ -0,0 +1,43 @@
# Apple II Email and Usenet News Suite
<p align="center"><img src="img/emailler-logo.png" alt="emai//er-logo" height="200px"></p>
[Back to Main emai//er Docs](README.md#detailed-documentation-for-email-functions)
## `PRINT65.SYSTEM`
<!-- <p align="center"><img src="img/SMTP65.jpg" alt="SMTP65" height="400px"></p> -->
`PRINT65.SYSTEM` is a utility for printing to a network-connected printer using the Hewlett Packard Jetdirect protocol. It requires an Uthernet-II ethernet card and will not work with other interfaces without modification, because it uses the W5100 hardware TCP/IP stack.
Before running `PRINT65.SYSTEM` for the first time, use `EDIT.SYSTEM` to create a configuration file called `PRINT.CFG`. This file consists of a single line specifying the IP address of the network printer to use, optionally followed by a colon and a port number. If the port number is omitted it defaults to 9100. For example:
```
192.168.10.4:9100
```
`PRINT65.SYSTEM` performs the following tasks:
- If no filename was provided on the command line, prompt for the filename to print
- Detect Uthernet-II
- Obtain IP address using DHCP
- Connect to Jetdirect printer
- Open file
- Send file contents to printer over TCP/IP
- Close file
- Disconnect
### Using Command Line Argument to Specify the File to Print
`PRINT65.SYSTEM` supports command line arguments in a way that is compatible with the Davex shell (and possibily other environments.) In Davex you can print a file as follows:
```
print65.system /path/to/my/file
```
### HP Jetdirect
Most HP printers support Jetdirect. I am using an HP Photosmart 7520 which supports the Jetdirect protocol on port 9100 over it's wifi connection. Jetdirect defaults to a simple plain text mode, which we exploit here to print in 80 column text mode.
[Back to Main emai//er Docs](README.md#detailed-documentation-for-email-functions)

View File

@ -27,14 +27,14 @@ Here is an example config file (with passwords replaced with `****` for obvious
Bobbi
****
/H1/IP65
/DATA/EMAIL
/H1/DOCUMENTS/EMAIL
bobbi.8bit@gmail.com
```
The lines are as follows, in order:
1) IP address of the NNTP server, optionally followed by a colon and then the TCP port number. If the colon and port number are omitted, port 119 is the default.
2) Username to use when connecting to NNTP.
2) Username to use when connecting to NNTP. If your NNTP server does not need authentication then use '-' for the username and password.
3) Password to use when connecting to NNTP.
4) ProDOS path of the directory where the email executables are installed.
5) ProDOS path to the root of the email folder tree. Mailboxes will be created and managed under this root path.

View File

@ -31,6 +31,7 @@ Emai//er is implemented as a number of ProDOS executables, each of which perform
- `ATTACHER.SYSTEM` is used for creating multi-part MIME messages with attached files.
- `REBUILD.SYSTEM` is a utility for rebuilding mailbox databases, should they become corrupted. This can also be used for bulk import of messages.
- `DATE65.SYSTEM` is a Network Time Protocol (NTP) client which can be used for setting the system time and date if you do not have a real time clock.
- `PRINT65.SYSTEM` allows text file to be printed to a network-attached printer that supports the Hewlett Packard Jetdirect protocol.
The following diagram shows the various executables that form the emai//er suite and how they execute one another. Note how `EMAIL.SYSTEM` serves as the hub from which all the other programs may be invoked.
@ -68,6 +69,10 @@ Recommended optional hardware:
Emai//er has been extensively tested using ProDOS 2.4.2. However, it should not be a problem to run it under other versions of ProDOS.
## Uthernet-II Slot
The default slot is 5. If you have your card in another slot then create a file called `ethernet.slot` using `EDIT.SYSTEM` with your slot number on the first line.
## Transport Level Security (TLS)
One problem faced by any retrocomputing project of this type is that Transport Layer Security (TLS) is endemic on today's Internet. While this is great for security, the encryption algorithms are not feasible to implement on a 6502-based system. In order to bridge the plain text world of the Apple II to today's encrypted Internet, I have set up a Raspberry Pi using several common open source packages as a gateway.
@ -105,6 +110,7 @@ Please refer to the linked documents for detailed instructions on how to configu
- [Receiving Email with `POP65.SYSTEM`](README-pop65.md)
- [Sending Email with `SMTP65.SYSTEM`](README-smtp65.md)
- [Rebuilding Mailboxes with `REBUILD.SYSTEM`](README-rebuild.md)
- [Printing Files with `PRINT65.SYSTEM`](README-print65.md)
## Detailed Documentation for Usenet Functions

View File

@ -45,7 +45,7 @@ TCP =\
tweet65 \
pop65-slow
bin: wget65.bin pop65.bin smtp65.bin email.bin rebuild.bin edit.bin attacher.bin nntp65.bin nntp65.up.bin
bin: wget65.bin pop65.bin smtp65.bin email.bin rebuild.bin edit.bin attacher.bin nntp65.bin nntp65.up.bin print65.bin
wget65.bin: w5100.c w5100_http.c linenoise.c
wget65.bin: IP65LIB = ../ip65/ip65.lib
@ -67,6 +67,10 @@ nntp65.up.bin: w5100.c
nntp65.up.bin: IP65LIB = ../ip65/ip65.lib
nntp65.up.bin: A2_DRIVERLIB = ../drivers/ip65_apple2_uther2.lib
print65.bin: w5100.c
print65.bin: IP65LIB = ../ip65/ip65.lib
print65.bin: A2_DRIVERLIB = ../drivers/ip65_apple2_uther2.lib
email.bin: gettime.s
date65.bin hfs65.bin tweet65.bin: CL65FLAGS = --start-addr 0x0C00 apple2enh-iobuf-0800.o
@ -166,6 +170,8 @@ ip65.dsk: bin
java -jar $(AC) -p $@ nntp65up.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 $@ print65 < print65.bin
java -jar $(AC) -p $@ print65.system sys < $(CC65)/apple2enh/util/loader.system
java -jar $(AC) -as $@ rebuild < rebuild.bin
java -jar $(AC) -p $@ rebuild.system sys < $(CC65)/apple2enh/util/loader.system
java -jar $(AC) -as $@ smtp65 < smtp65.bin

View File

@ -335,6 +335,8 @@ struct tabent {
*/
void file_ui_draw(uint16_t i, uint16_t first, uint16_t selected, uint16_t entries) {
struct tabent *entry;
if (i < first)
return;
gotoxy(5, i - first + 6);
if (i < entries) {
entry = (struct tabent*)iobuf + i;
@ -395,14 +397,10 @@ void file_ui_draw_all(uint16_t first, uint16_t selected, uint16_t entries) {
}
/*
* Perform ProDOS MLI ON_LINE call to
* write all online volume names into iobuf[]
* Return the number of entries
* Asm code for online()
*/
uint16_t online(void) {
uint16_t entries = 0;
struct tabent *entry;
uint8_t i, j, len;
#pragma optimize (push, off)
void onlineasm(void) {
__asm__("lda #$00"); // All devices
__asm__("sta mliparam + 1");
__asm__("lda #<%v", iobuf); // iobuf LSB
@ -413,6 +411,19 @@ uint16_t online(void) {
__asm__("lda #$c5"); // ON_LINE
__asm__("ldx #$02"); // Two parms
__asm__("jsr callmli");
}
#pragma optimize (pop)
/*
* Perform ProDOS MLI ON_LINE call to
* write all online volume names into iobuf[]
* Return the number of entries
*/
uint16_t online(void) {
uint16_t entries = 0;
struct tabent *entry;
uint8_t i, j, len;
onlineasm();
entry = (struct tabent*)iobuf;
for (i = 0; i < 16; ++i) {
len = iobuf[256 + i * 16] & 0x0f;

View File

@ -419,37 +419,44 @@ void goto_prompt_row(void) {
gotoxy(0, PROMPT_ROW);
}
#define MAX_DISP_FILENAME 35 /* Max display space for filename */
/*
* Refresh the status line at the bottom of the screen
*/
void update_status_line(void) {
uint8_t nofile = 0;
uint8_t l;
static char selmsg1[] = ": Go to end of selection, then [Return]";
static char selmsg2[] = ": Go to target, then [Return] to ";
uint8_t no_name = 0;
static char dispfname[MAX_DISP_FILENAME + 1];
static char disppartnum[8 + 1];
goto_prompt_row();
if (strlen(filename) == 0) {
strcpy(filename, "<scratch>");
nofile = 1;
no_name = 1;
}
if (status[2] == 0)
strcpy(disppartnum, "");
else
snprintf(disppartnum, 8, ":Part%u", status[2]);
l = strlen(filename) + strlen(disppartnum);
if (l <= MAX_DISP_FILENAME) {
strcpy(dispfname, filename);
} else {
strcpy(dispfname, filename + (l - MAX_DISP_FILENAME));
dispfname[0] = dispfname[1] = dispfname[2] = '.';
}
strcat(dispfname, disppartnum);
revers(1);
switch (mode) {
case SEL_NONE:
if (status[2] == 0) {
cprintf("OA-? Help | [%03u] %c File:%s %2uKB free",
l_auxbank, status[0] ? '*' : ' ', filename,
(FREESPACE() + 512) / 1024);
l = 44 - strlen(filename);
} else {
snprintf(userentry, 80, "%s Part:%u", filename, status[2]);
cprintf("OA-? Help | [%03u] %c File:%s %2uKB free",
l_auxbank, status[0] ? '*' : ' ', userentry,
(FREESPACE() + 512) / 1024);
l = 45 - strlen(userentry);
}
cprintf("OA-? Help | [%03u] %c %s",
l_auxbank, status[0] ? '*' : ' ', dispfname);
l = 49 - strlen(dispfname);
cclear(l);
cprintf("| Free:%2uKB", (FREESPACE() + 512) / 1024);
l = 0;
break;
case SEL_SELECT:
cprintf("Select: OA-[Space] to end");
@ -464,27 +471,23 @@ void update_status_line(void) {
l = 80 - 23;
break;
case SRCH3:
if (status[2] == 0) {
cprintf("OA-? Help | [%03u] %c File:%s %2uKB free | Not Found",
l_auxbank, status[0] ? '*' : ' ', filename, (FREESPACE() + 512) / 1024);
l = 44 - 12 - strlen(filename);
} else {
snprintf(userentry, 80, "%s Part:%u", filename, status[2]);
cprintf("OA-? Help | [%03u] %c File:%s %2uKB free | Not Found",
l_auxbank, status[0] ? '*' : ' ', userentry,
(FREESPACE() + 512) / 1024);
l = 45 - 12 - strlen(userentry);
}
cprintf("OA-? Help | [%03u] %c %s",
l_auxbank, status[0] ? '*' : ' ', dispfname);
l = 49 - 12 - strlen(dispfname);
cclear(l);
cprintf("| Free:%2uKB | NOT FOUND", (FREESPACE() + 512) / 1024);
beep();
beep();
l = 0;
break;
}
cclear(l);
if (l > 0)
cclear(l);
revers(0);
if (nofile)
strcpy(filename, "");
gotoxy(curscol, cursrow);
cursor(1);
if (no_name)
strcpy(filename, "");
}
/*
@ -763,7 +766,7 @@ void spinner(uint32_t sz, uint8_t saving, uint8_t copymode) {
(saving ? "Saving" : "Opening"), filename, chars[(i++) % 4], sz);
revers(1);
cprintf("%s", buf);
cclear(79 - strlen(buf));
cclear(80 - strlen(buf));
revers(0);
}
@ -1627,8 +1630,8 @@ uint8_t finish_search_replace(uint16_t pos, uint8_t r, uint8_t ask) {
uint8_t i;
mode = SEL_NONE;
jump_pos(pos);
startsel = gapend + 1;
endsel = gapend + 1 + strlen(search);
// startsel = gapend + 1;
// endsel = gapend + 1 + strlen(search);
draw_screen();
if (r == 0) { // Replace mode
if (ask) {
@ -1651,7 +1654,7 @@ uint8_t finish_search_replace(uint16_t pos, uint8_t r, uint8_t ask) {
cursor_right();
return 1; // Continue
}
startsel = endsel = 65535U;
// startsel = endsel = 65535U;
return 0; // Do not continue
}
@ -1727,8 +1730,8 @@ void disconnect_ramdisk(void) {
uint16_t *s3d2 = (uint16_t*)0xbf26; // s3d2 driver vector
if (*s0d1 != *s3d2)
check_ramdisk(3 + (2 - 1) * 8); // s3d2
if (*s0d1 != *s3d1)
check_ramdisk(3 + (1 - 1) * 8); // s3d1
// if (*s0d1 != *s3d1)
// check_ramdisk(3 + (1 - 1) * 8); // s3d1
if (*s0d1 == *s3d2) {
s3d2dev = 0;
goto s3d1; // No /RAM
@ -1744,6 +1747,8 @@ void disconnect_ramdisk(void) {
*s3d2 = *s0d1;
--(*devcnt);
s3d1:
return;
#if 0
if (*s0d1 == *s3d1) {
s3d1dev = 0;
return; // No /RAM3
@ -1758,6 +1763,7 @@ s3d1:
s3d1vec = *s3d1;
*s3d1 = *s0d1;
--(*devcnt);
#endif
}
/*
@ -1805,6 +1811,7 @@ void reconnect_ramdisk(void) {
printf("Unable to reconnect S3D2");
}
s3d1:
# if 0
if (s3d1dev) {
*s3d1 = s3d1vec;
++(*devcnt);
@ -1825,6 +1832,7 @@ s3d1:
beep();
printf("Unable to reconnect S3D1");
}
#endif
done:
return;
}
@ -1888,7 +1896,7 @@ void init_aux_banks(void) {
uint16_t count;
clrscr();
revers(1);
cprintf("EDIT.SYSTEM v1.27 Bobbi 2020");
cprintf("EDIT.SYSTEM v1.30 Bobbi 2021");
revers(0);
cprintf("\n\n\n %u x 64KB aux banks -> %uKB\n", banktbl[0], banktbl[0]*64);
for (i = 1; i <= banktbl[0]; ++i) {
@ -2041,6 +2049,8 @@ struct tabent {
*/
void file_ui_draw(uint16_t i, uint16_t first, uint16_t selected, uint16_t entries) {
struct tabent *entry;
if (i < first)
return;
gotoxy(5, i - first + 6);
if (i < entries) {
entry = (struct tabent*)iobuf + i;
@ -2628,6 +2638,7 @@ int edit(char *fname) {
}
break;
case 0x80 + '?': // OA-? "Help"
case 0x80 + '/': // OA-/ "Help"
help1:
help(1);
c = cgetc();

View File

@ -1,5 +1,5 @@
--------------------------------------------------------------------------------
v1.27 }}} EDIT.SYSTEM HELP }}} Page One
v1.30 }}} EDIT.SYSTEM HELP }}} Page One
--------------------------------------+-----------------------------------------
Navigation: | Editing:
Cursor keys Move the cursor | [Return] Split line

View File

@ -1,7 +1,7 @@
/////////////////////////////////////////////////////////////////
// emai//er - Simple Email User Agent vaguely inspired by Elm
// Handles INBOX in the format created by POP65
// Bobbi June 2020 - May 2021
// Bobbi June 2020 - June 2021
/////////////////////////////////////////////////////////////////
#include <stdio.h>
@ -22,8 +22,9 @@
// Program constants
#define MSGS_PER_PAGE 19 // Number of messages shown on summary screen
#define PROMPT_ROW 24 // Row that data entry prompt appears on
#define LINEBUFSZ 1000 // According to RFC2822 Section 2.1.1 (998+CRLF)
#define READSZ 512 // Size of buffer for copying files
#define LINEBUFSZ 1024 // Max line 1000 according to RFC2822 Sect 2.1.1
// We use 1024 because this is also used for scrollback
// Characters
#define BELL 0x07
@ -88,8 +89,7 @@ struct datetime {
static char filename[80];
static char userentry[80];
static char linebuf[LINEBUFSZ];
static char halfscreen[0x0400];
static uint8_t linebuf[LINEBUFSZ];
static FILE *fp;
static struct emailhdrs *headers;
static uint16_t selection = 1;
@ -553,7 +553,7 @@ uint8_t hexdigit(char c) {
* p - Pointer to buffer to decode. Results written in place.
* Returns number of bytes decoded
*/
uint16_t decode_quoted_printable(uint8_t *p) {
uint16_t decode_quoted_printable(uint8_t *p, uint8_t isheader) {
uint16_t i = 0, j = 0;
uint8_t c;
while (c = p[i]) {
@ -564,7 +564,7 @@ uint16_t decode_quoted_printable(uint8_t *p) {
c = 16 * hexdigit(p[i + 1]) + hexdigit(p[i + 2]);
p[j++] = c;
i += 3;
} else if (c == '?')
} else if ((c == '?') && isheader)
break;
else {
p[j++] = c;
@ -589,18 +589,19 @@ void printfield(char *s, uint8_t start, uint8_t end) {
#pragma code-name (pop)
/*
* Decode Subject header which may be encoded Quoted-Printable or Base64
* Decode Subject or From header which may be encoded
* Quoted-Printable or Base64
* p - pointer to subject header content
* Decoded (and sanitized) text is returned in linebuf[]
*/
void decode_subject(char *p) {
void decode_qp_header(char *p) {
uint8_t i = 0, j = 0;
if (strncasecmp(p, "=?utf-8?", 8) == 0) {
strcpy(linebuf, p + 10); // Skip '=?UTF-8?x?'
if (p[8] == 'B')
decode_base64(linebuf);
else
decode_quoted_printable(linebuf);
decode_quoted_printable(linebuf, 1);
while (linebuf[i]) {
if ((linebuf[i] <= 127) && (linebuf[i] >= 32))
linebuf[j++] = linebuf[i];
@ -633,9 +634,10 @@ void print_one_email_summary(struct emailhdrs *h, uint8_t inverse) {
putchar('|');
printfield(h->date, 0, 16);
putchar('|');
printfield(h->from, 0, 20);
decode_qp_header(h->from);
printfield(linebuf, 0, 20);
putchar('|');
decode_subject(h->subject);
decode_qp_header(h->subject);
printfield(linebuf, 0, 39);
putchar(NORMAL);
}
@ -923,6 +925,21 @@ void sanitize_filename(char *s) {
enum aux_ops {FROMAUX, TOAUX};
/*
* Asm code for copyaux()
*/
#pragma optimize (push, off)
void copyauxasm(enum aux_ops dir) {
if (dir == TOAUX)
__asm__("sec"); // Copy main->aux
else
__asm__("clc"); // Copy aux->main
__asm__("sta $c000"); // Turn off 80STORE
__asm__("jsr $c311"); // AUXMOVE
__asm__("sta $c001"); // Turn on 80STORE
}
#pragma optimize (pop)
/*
* Aux memory copy routine
*/
@ -933,72 +950,58 @@ void copyaux(char *src, char *dst, uint16_t len, enum aux_ops dir) {
*a1 = src;
*a2 = src + len - 1; // AUXMOVE moves length+1 bytes!!
*a4 = dst;
if (dir == TOAUX)
__asm__("sec"); // Copy main->aux
else
__asm__("clc"); // Copy aux->main
__asm__("sta $c000"); // Turn off 80STORE
__asm__("jsr $c311"); // AUXMOVE
__asm__("sta $c001"); // Turn on 80STORE
copyauxasm(dir);
}
/*
* Save the current screen to the scrollback file
* We preserve linebuf[] by copying it to aux 0x0800, and recover
* it at the end. This saves us having an additional 1KB buffer.
*/
void save_screen_to_scrollback(FILE *fp) {
copyaux(linebuf, (void*)0x800, 1024, TOAUX); // Preserve linebuf[]
if (fwrite((void*)0x0400, 0x0400, 1, fp) != 1) { // Even cols
error(ERR_NONFATAL, sb_err);
return;
goto done;
}
copyaux((void*)0x400, halfscreen, 0x400, FROMAUX);
if (fwrite(halfscreen, 0x0400, 1, fp) != 1) { // Odd cols
copyaux((void*)0x400, linebuf, 0x400, FROMAUX);
if (fwrite(linebuf, 0x0400, 1, fp) != 1) { // Odd cols
error(ERR_NONFATAL, sb_err);
return;
goto done;
}
done:
copyaux((void*)0x800, linebuf, 1024, FROMAUX); // Recover linebuf[]
}
/*
* Load a screen from the scrollback file
* Screens are numbered 1, 2, 3 ...
* Does not trash the screen holes, which must be preserved!
* We preserve linebuf[] by copying it to aux 0x0800, and recover
* it at the end. This saves us having an additional 1KB buffer.
*/
void load_screen_from_scrollback(FILE *fp, uint8_t screen) {
uint8_t i;
copyaux(linebuf, (void*)0x800, 1024, TOAUX); // Preserve linebuf[]
if (fseek(fp, (screen - 1) * 0x0800, SEEK_SET) ||
(fread(halfscreen, 0x0400, 1, fp) != 1)) { // Even cols
(fread(linebuf, 0x0400, 1, fp) != 1)) { // Even cols
error(ERR_NONFATAL, sb_err);
return;
goto done;
}
for (i = 0; i < 8; ++i)
memcpy((char*)0x400 + i * 0x80, halfscreen + i * 0x80, 0x078);
if (fread(halfscreen, 0x0400, 1, fp) != 1) { // Odd cols
memcpy((char*)0x400 + i * 0x80, linebuf + i * 0x80, 0x078);
if (fread(linebuf, 0x0400, 1, fp) != 1) { // Odd cols
error(ERR_NONFATAL, sb_err);
return;
goto done;
}
for (i = 0; i < 8; ++i)
copyaux(halfscreen + i * 0x80, (char*)0x400 + i * 0x80, 0x078, TOAUX);
copyaux(linebuf + i * 0x80, (char*)0x400 + i * 0x80, 0x078, TOAUX);
if (fseek(fp, 0, SEEK_END)) {
error(ERR_NONFATAL, sb_err);
return;
goto done;
}
}
/*
* Check if text at p is a MIME boundary.
* p is assumed to point to start of line.
* Valid MIME boundaries start with "--", then have a char sequence
* with no spaces, then CR
*/
uint8_t is_mime_boundary(char *p) {
if (strncmp(p, "--", 2)) // Must start with "--"
return 0;
p += 2;
do {
if (*p == ' ') // Can not contain ' '
return 0;
++p;
} while (*p && (*p != '\r'));
return 1;
done:
copyaux((void*)0x800, linebuf, 1024, FROMAUX); // Recover linebuf[]
}
/*
@ -1023,6 +1026,65 @@ enum mime_enc mime_encoding(char *s) {
return ENC_SKIP;
}
/*
* Support three levels of nesting
*/
static char mime_boundaries[3][71];
static uint8_t mime_idx;
/*
* linebuf[] is expected to contain the Content-Type: header
* Parse it and obtain the MIME boundary, write it to buf[]
* Returns 1 if boundary was found or if there is no room for more
* boundaries to be stored, 0 if boundary was not found.
*
* Three levels of nesting are supported, to avoid mime_boundaries[]
* chewing up too much memory.
*/
uint8_t mime_get_boundary(void) {
char *p, *q;
if (mime_idx > 2)
return 1; // No point in trying if nested too deep
p = strstr(linebuf, "boundary=");
if (p) {
q = strstr(p + 9, "\r"); // Terminated by EOL ..
if (!q)
q = strstr(p + 9, ";"); // .. or by semicolon
if (q) {
if (*(p + 9) == '\"') {
// Trim quotes, if present
strncpy(mime_boundaries[mime_idx], p + 10, q - p - 10);
mime_boundaries[mime_idx][q - p - 11] = '\0';
} else {
strncpy(mime_boundaries[mime_idx], p + 9, q - p - 9);
mime_boundaries[mime_idx][q - p - 9] = '\0';
}
//printf("Boundary: %s\n", mime_boundaries[mime_idx]);
++mime_idx;
}
return 1; // Got boundary
}
return 0; // Did not get boundary
}
/*
* Check if text at p is a MIME boundary.
* Uses the MIME boundaries stored in mime_boundaries[]
* p is assumed to point to start of line.
*/
uint8_t is_mime_boundary(char *p) {
uint8_t i;
if (strncmp(p, "--", 2)) // Must start with "--"
return 0;
p += 2;
for (i = 0; i < mime_idx; ++i) {
// Match boundary followed by '\r' or "--" (or anything else, actually)
if (strncmp(mime_boundaries[i], p, strlen(mime_boundaries[i])) == 0)
return 1;
}
return 0;
}
/*
* Display email with simple pager functionality
* Includes support for decoding MIME headers
@ -1057,6 +1119,7 @@ void email_pager(struct emailhdrs *h) {
pos = hh.skipbytes;
fseek(fp, pos, SEEK_SET); // Skip over headers
mime_enc = ENC_7BIT;
mime_idx = 0;
restart:
eof = 0;
linecount = 0;
@ -1081,7 +1144,8 @@ restart:
fputs("Date: ", stdout);
printfield(hh.date, 0, 39);
fputs("\nFrom: ", stdout);
printfield(hh.from, 0, 70);
decode_qp_header(hh.from);
printfield(linebuf, 0, 70);
if (strncmp(hh.to, "News:", 5) == 0) {
fputs("\nNewsgrp: ", stdout);
printfield(&(hh.to[5]), 0, 70);
@ -1098,7 +1162,7 @@ restart:
}
}
fputs("\nSubject: ", stdout);
decode_subject(hh.subject);
decode_qp_header(hh.subject);
printfield(linebuf, 0, 70);
fputs("\n\n", stdout);
get_line(fp, 1, linebuf, LINEBUFSZ, &pos); // Reset buffer
@ -1133,8 +1197,8 @@ restart:
printf("\n<Not showing HTML>\n");
mime = 1;
} else {
mime = 2 + mime_get_boundary(); // 3 if boundary, 2 otherwise
mime_binary = 1;
mime = 3;
}
} else if (!strncasecmp(writep, cte, 27)) {
mime = 3;
@ -1172,12 +1236,14 @@ prompt_dl:
mime_enc = ENC_SKIP; // Skip over binary MIME parts with no filename
printf("\n");
}
} else if (mime == 2) {
mime = 2 + mime_get_boundary(); // 3 if boundary, 2 otherwise
}
readp = writep = NULL;
} else if (mime == 4) {
switch (mime_enc) {
case ENC_QP:
chars = decode_quoted_printable(writep);
chars = decode_quoted_printable(writep, 0);
break;
case ENC_B64:
chars = decode_base64(writep);
@ -1269,8 +1335,10 @@ retry:
get_line(fp, 1, linebuf, LINEBUFSZ, &pos); // Reset buffer
do {
get_line(fp, 0, linebuf, LINEBUFSZ, &pos);
if (!strncasecmp(linebuf, ct, 14))
mime = 4;
if (!strncasecmp(linebuf, ct, 14)) {
mime = 3 + mime_get_boundary(); // 4 if boundary, 3 otherwise
continue;
}
if (!strncasecmp(linebuf, cte, 27)) {
mime = 4;
mime_enc = mime_encoding(linebuf);
@ -1278,6 +1346,11 @@ retry:
mime = 0;
break;
}
continue;
}
if (mime == 3) { // We have Content-Type but no boundary yet
mime_get_boundary();
mime = 4;
}
} while (linebuf[0] != '\r');
pos = hh.skipbytes;
@ -1575,7 +1648,7 @@ esc_pressed:
* Adds 'Re: ' to subject line unless it is already there
*/
void prefix_subject(FILE *f, char *subject, char *prefix) {
decode_subject(subject);
decode_qp_header(subject);
fprintf(f, "Subject: %s%s\r",
(strncmp(subject, prefix, strlen(prefix)) ? prefix : ""), linebuf);
}
@ -1613,16 +1686,18 @@ uint8_t write_email_headers(FILE *fp1, FILE *fp2, struct emailhdrs *h,
if (mode == 'R') {
truncate_header(h->date, buf, 40);
fprintf(fp2, "On %s, ", buf);
truncate_header(h->from, buf, 80);
decode_qp_header(h->from);
truncate_header(linebuf, buf, 80);
fprintf(fp2, "%s wrote:\r\r", buf);
} else {
fprintf(fp2, "-------- Forwarded Message --------\r");
decode_subject(h->subject);
decode_qp_header(h->subject);
truncate_header(linebuf, buf, 80);
fprintf(fp2, "Subject: %s\r", buf);
truncate_header(h->date, buf, 40);
fprintf(fp2, "Date: %s\r", buf);
truncate_header(h->from, buf, 80);
decode_qp_header(h->from);
truncate_header(linebuf, buf, 80);
fprintf(fp2, "From: %s\r", buf);
truncate_header(h->to, buf, 80);
fprintf(fp2, "To: %s\r\r", buf);
@ -1721,19 +1796,22 @@ char prompt_okay(char *msg) {
*/
void get_email_body(struct emailhdrs *h, FILE *f, char mode) {
uint16_t chars;
char c, *readp, *writep;
uint8_t c, *readp, *writep;
uint32_t pos = 0;
const int8_t *b = b64dec - 43;
uint8_t mime = 0, mime_enc = ENC_7BIT, mime_binary = 0;
uint8_t mime = 0, mime_enc = ENC_7BIT, mime_binary, mime_hasfile;
fseek(fp, pos, SEEK_SET);
get_line(fp, 1, linebuf, LINEBUFSZ, &pos); // Reset buffer
mime_idx = 0;
do {
spinner();
get_line(fp, 0, linebuf, LINEBUFSZ, &pos);
if (!strncasecmp(linebuf, mime_ver, 17))
mime = 1;
if (!strncasecmp(linebuf, ct, 14))
mime = 4;
if (!strncasecmp(linebuf, ct, 14)) {
mime = 3 + mime_get_boundary(); // 4 if boundary, 3 otherwise
continue;
}
if (!strncasecmp(linebuf, cte, 27)) {
mime = 4;
mime_enc = mime_encoding(linebuf);
@ -1741,6 +1819,11 @@ void get_email_body(struct emailhdrs *h, FILE *f, char mode) {
error(ERR_NONFATAL, unsupp_enc, linebuf + 27);
return;
}
continue;
}
if (mime == 3) { // We have Content-Type but no boundary yet
mime_get_boundary();
mime = 4;
}
} while (linebuf[0] != '\r');
pos = h->skipbytes;
@ -1748,8 +1831,10 @@ void get_email_body(struct emailhdrs *h, FILE *f, char mode) {
readp = linebuf;
writep = linebuf;
mime_binary = 0;
mime_hasfile = 0;
get_line(fp, 1, linebuf, LINEBUFSZ, &pos); // Reset buffer
while (1) {
spinner();
if (!readp)
readp = linebuf;
if (!writep)
@ -1757,22 +1842,20 @@ void get_email_body(struct emailhdrs *h, FILE *f, char mode) {
if (get_line(fp, 0, writep, (LINEBUFSZ - (writep - linebuf)), &pos) == 0)
break;
if ((mime >= 1) && is_mime_boundary(writep)) {
if ((mime == 4) && !mime_binary) // End of Text/Plain MIME section
break;
mime = 2;
mime_enc = ENC_7BIT;
mime_binary = 0;
mime_hasfile = 0;
readp = writep = NULL;
} else if ((mime < 4) && (mime >= 2)) {
if (!strncasecmp(writep, ct, 14)) {
if (!strncmp(writep + 14, "text/plain", 10)) {
mime = 3;
} else if (!strncmp(writep + 14, "text/html", 9)) {
printf("\n<Not showing HTML>\n");
mime = 1;
} else {
mime = 2 + mime_get_boundary(); // 3 if boundary, 2 otherwise
mime_binary = 1;
mime = 3;
}
} else if (!strncasecmp(writep, cte, 27)) {
mime = 3;
@ -1781,16 +1864,20 @@ void get_email_body(struct emailhdrs *h, FILE *f, char mode) {
printf(unsupp_enc, writep + 27);
mime = 1;
}
} else if (strstr(writep, "filename=")) {
mime_hasfile = 1;
} else if ((mime == 3) && (!strncmp(writep, "\r", 1))) {
mime = 4;
if (mime_binary)
mime_enc = ENC_SKIP; // Skip over binary MIME parts
} else if (mime == 2) {
mime = 2 + mime_get_boundary(); // 3 if boundary, 2 otherwise
}
readp = writep = NULL;
} else if (mime == 4) {
switch (mime_enc) {
case ENC_QP:
chars = decode_quoted_printable(writep);
chars = decode_quoted_printable(writep, 0);
break;
case ENC_B64:
chars = decode_base64(writep);
@ -1801,7 +1888,7 @@ void get_email_body(struct emailhdrs *h, FILE *f, char mode) {
}
}
if (readp) {
if ((mime == 0) || ((mime == 4) && !mime_binary)) {
if ((mime == 0) || ((mime == 4) && !mime_hasfile)) {
do {
c = word_wrap_line(f, &readp, 78, mode);
} while (c == 1);
@ -1976,7 +2063,7 @@ uint16_t get_db_index(void) {
* on the current message. If they are, prompt the user and, if affirmative,
* iterate through the tagged messages calling copy_to_mailbox() on each.
*/
uint8_t copy_to_mailbox_tagged(char *mbox, uint8_t delete) {
uint8_t copy_to_mailbox_tagged(char *mbox, char mode, uint8_t delete) {
uint16_t count = 0, tagcount = 0;
struct emailhdrs *h;
uint16_t l;
@ -1985,7 +2072,8 @@ uint8_t copy_to_mailbox_tagged(char *mbox, uint8_t delete) {
copy_to_mailbox(h, get_db_index(), mbox, delete, ' ');
return 0;
}
snprintf(filename, 80, "%u tagged - ", total_tag);
snprintf(filename, 80, "%s %u tagged - ",
(mode == 'C' ? "Copy" : (mode == 'M' ? "Move" : "Archive")), total_tag);
if (!prompt_okay(filename))
return 0;
h = (struct emailhdrs*)malloc(sizeof(struct emailhdrs));
@ -2265,7 +2353,7 @@ void keyboard_hdlr(void) {
if (h) {
c = prompt_for_name("Copy to mbox", 1);
if ((c != 0) && (c != 255))
copy_to_mailbox_tagged(userentry, 0);
copy_to_mailbox_tagged(userentry, 'C', 0);
}
break;
case 'm':
@ -2273,14 +2361,14 @@ void keyboard_hdlr(void) {
if (h) {
c = prompt_for_name("Move to mbox", 1);
if ((c != 0) && (c != 255))
copy_to_mailbox_tagged(userentry, 1);
copy_to_mailbox_tagged(userentry, 'M', 1);
}
break;
case 'a':
case 'A':
if (h) {
goto_prompt_row();
copy_to_mailbox_tagged("RECEIVED", 1);
copy_to_mailbox_tagged("RECEIVED", 'A', 1);
}
break;
case 'p':
@ -2353,6 +2441,7 @@ void keyboard_hdlr(void) {
load_app(APP_SMTP);
break;
case 0x80 + '?': // OA-? "Help"
case 0x80 + '/': // OA-/ "Help"
help(1);
c = cgetc();
email_summary();

View File

@ -4,7 +4,7 @@ passwordgoeshere
NODELETE
192.168.10.2:25
apple2.local
/IP65
/DATA/EMAIL
/H1/IP65
/H1/DOCUMENTS/EMAIL
bobbi.8bit@gmail.com

View File

@ -1,12 +1,12 @@
/////////////////////////////////////////////////////////////////
// EMAIL_COMMON.H
// Definitions shared between pop65.c and email.c
// Bobbi June 2020
// Bobbi August 2021
/////////////////////////////////////////////////////////////////
#include <stdint.h>
#define PROGNAME "emai//er v2.1.4"
#define PROGNAME "emai//er v2.1.14"
// Configuration params from EMAIL.CFG
char cfg_server[40]; // IP of POP3 server

View File

@ -42,7 +42,7 @@ void send(unsigned char flags, const char* str, ...)
}
va_start(args, str);
send_size += vsnprintf(send_buffer + send_size, sizeof(send_buffer) - send_size, str, args);
send_size += vsnprintf((char*)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)
@ -309,9 +309,9 @@ int main(void)
{
read(file, &eth_init, 1);
close(file);
eth_init &= ~'0';
eth_init &= 7;
}
printf("- %d", eth_init);
printf("- %u", eth_init);
}
#endif

View File

@ -91,7 +91,7 @@ int ifttt_trigger(const char* key, const char* event,
return -1;
}
len = url_download(url, download, sizeof(download));
len = url_download(url, (uint8_t*)download, sizeof(download));
if (len < 12)
{

View File

@ -78,15 +78,15 @@ struct linenoiseState {
int history_index; /* The history index we are currently editing. */
};
enum KEY_ACTION{
CTRL_A = 1, /* Ctrl+a */
CTRL_B = 2, /* Ctrl-b */
CTRL_C = 3, /* Ctrl-c */
CTRL_D = 4, /* Ctrl-d */
CTRL_E = 5, /* Ctrl-e */
CTRL_F = 6, /* Ctrl-f */
CTRL_N = 14, /* Ctrl-n */
CTRL_P = 16 /* Ctrl-p */
enum KEY_ACTION {
CTRL_A = 1, /* Ctrl+a */
CTRL_B = 2, /* Ctrl-b */
CTRL_C = 3, /* Ctrl-c */
CTRL_D = 4, /* Ctrl-d */
CTRL_E = 5, /* Ctrl-e */
CTRL_F = 6, /* Ctrl-f */
CTRL_N = 14, /* Ctrl-n */
CTRL_P = 16 /* Ctrl-p */
};
static void refreshLine(struct linenoiseState *l);
@ -102,14 +102,16 @@ static int getColumns() {
return cols;
}
#ifdef __APPLE2__
#pragma code-name (push, "LC")
#endif
/* Beep, used for completion when there is nothing to complete or when all
* the choices were already shown. */
static void linenoiseBeep(void) {
#ifdef __APPLE2__
unsigned char x = wherex();
#endif
putchar('\a');
#ifdef __APPLE2__
gotox(x);
#endif
}
/* ============================== Completion ================================ */
@ -123,6 +125,10 @@ static void freeCompletions(linenoiseCompletions *lc) {
free(lc->cvec);
}
#ifdef __APPLE2__
#pragma code-name (push, "LC")
#endif
/* This is an helper function for linenoiseEdit() and is called when the
* user types the <tab> key in order to complete the string currently in the
* input.

View File

@ -1,7 +1,7 @@
144.76.35.198:119
usergoeshere
passwordgoeshere
/IP65
/DATA/EMAIL
/H1/IP65
/H1/DOCUMENTS/EMAIL
bobbi.8bit@gmail.com

View File

@ -26,6 +26,8 @@
#define BELL 7
#define BACKSPACE 8
#define LOGFILE "NNTP65.LOG"
// Both pragmas are obligatory to have cc65 generate code
// suitable to access the W5100 auto-increment registers.
#pragma optimize (on)
@ -327,7 +329,7 @@ void readconfigfile(void) {
colon = strchr(cfg_server, ':');
if (!colon)
nntp_port = 110;
nntp_port = 119;
else {
nntp_port = atoi(colon + 1);
*colon = '\0';
@ -559,7 +561,8 @@ void update_mailbox(char *mbox) {
update_email_db(mbox, &hdrs);
puts("");
sprintf(filename, "%s/NEWS.SPOOL/NEWS.%u", cfg_emaildir, msg);
//sprintf(filename, "%s/NEWS.SPOOL/NEWS.%u", cfg_emaildir, msg);
sprintf(filename, "%s/NEWS.SPOOL/%s", cfg_emaildir, d->d_name);
if (unlink(filename))
printf("Can't delete %s\n", filename);
}
@ -568,7 +571,9 @@ void update_mailbox(char *mbox) {
void main(int argc, char *argv[]) {
uint32_t nummsgs, lownum, highnum, msgnum, msg;
uint16_t msgcount, i;
char sendbuf[80];
FILE *logfp;
uint8_t eth_init = ETH_INIT_DEFAULT;
if ((argc == 2) && (strcmp(argv[1], "EMAIL") == 0))
@ -632,11 +637,12 @@ void main(int argc, char *argv[]) {
}
// Copy IP config from IP65 to W5100
w5100_config(eth_init);
w5100_init(eth_init);
w5100_config();
printf("Ok\nConnecting to %s (%u) - ", cfg_server, nntp_port);
if (!w5100_connect(parse_dotted_quad(cfg_server), nntp_port)) {
if (!w5100_connect_addr(parse_dotted_quad(cfg_server), nntp_port)) {
printf("Fail\n");
error_exit();
}
@ -649,19 +655,28 @@ void main(int argc, char *argv[]) {
if (expect(buf, "20")) // "200" if posting is allowed / "201" if no posting
error_exit();
sprintf(sendbuf, "AUTHINFO USER %s\r\n", cfg_user);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
}
if (expect(buf, "381")) // Username accepted
error_exit();
// Skip authentication?
if (strcmp(cfg_user, "-") != 0) {
sprintf(sendbuf, "AUTHINFO USER %s\r\n", cfg_user);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
}
if (expect(buf, "381")) // Username accepted
error_exit();
sprintf(sendbuf, "AUTHINFO PASS %s\r\n", cfg_pass);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
sprintf(sendbuf, "AUTHINFO PASS %s\r\n", cfg_pass);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
}
if (expect(buf, "281")) // Authentication successful
error_exit();
}
if (expect(buf, "281")) // Authentication successful
error_exit();
// Make empty log file
_filetype = PRODOS_T_TXT;
_auxtype = 0;
logfp = fopen(LOGFILE, "w");
fclose(logfp);
while (1) {
msg = fscanf(newsgroupsfp, "%s %s %ld", newsgroup, mailbox, &msgnum);
@ -703,6 +718,7 @@ void main(int argc, char *argv[]) {
break;
}
msgcount = 0;
while (1) {
if (!w5100_tcp_send_recv("NEXT\r\n", buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
@ -724,11 +740,23 @@ void main(int argc, char *argv[]) {
}
spinner(filesize, 1); // Cleanup spinner
fclose(fp);
++msgcount;
}
printf("Updating NEWSGROUPS.NEW (%s:%ld) ...\n", newsgroup, msg);
fprintf(newnewsgroupsfp, "%s %s %ld\n", newsgroup, mailbox, msg);
printf("Updating mailbox %s ...\n", mailbox);
update_mailbox(mailbox);
_filetype = PRODOS_T_TXT;
_auxtype = 0;
logfp = fopen(LOGFILE, "a");
if (logfp) {
fprintf(logfp, " %s", newsgroup);
for (i = 0; i < (30 - strlen(newsgroup)); ++i)
fputc(' ', logfp);
fprintf(logfp, "%5u messages retrieved to mailbox %s\n", msgcount, mailbox);
fclose(logfp);
}
}
fclose(newsgroupsfp);
@ -752,5 +780,17 @@ void main(int argc, char *argv[]) {
printf("Disconnecting\n");
w5100_disconnect();
logfp = fopen(LOGFILE, "r");
if (logfp) {
puts("\nNNTP65 Session Summary:\n");
i = fgetc(logfp);
while (!feof(logfp)) {
putchar(i);
i = fgetc(logfp);
}
fclose(logfp);
puts("");
}
confirm_exit();
}

View File

@ -369,7 +369,7 @@ void readconfigfile(void) {
colon = strchr(cfg_server, ':');
if (!colon)
nntp_port = 110;
nntp_port = 119;
else
nntp_port = atoi(colon + 1);
}
@ -548,7 +548,8 @@ void main(int argc, char *argv[]) {
printf("Ok\n");
// Copy IP config from IP65 to W5100
w5100_config(eth_init);
w5100_init(eth_init);
w5100_config();
sprintf(filename, "%s/NEWS.OUTBOX", cfg_emaildir);
dp = opendir(filename);
@ -577,7 +578,7 @@ void main(int argc, char *argv[]) {
linecount = 0;
while (1) {
if ((get_line(fp, 0, linebuf, LINEBUFSZ) == 0) || (linecount == 20))
if ((get_line(fp, 0, linebuf, LINEBUFSZ) == 0) || (linecount == 20) || (linebuf[0] == '\r'))
break;
++linecount;
if (!strncmp(linebuf, "Newsgroups: ", 12))
@ -632,7 +633,7 @@ sendmessage:
if (!connected) {
printf("\nConnecting to %s (%u) - ", cfg_server, nntp_port);
if (!w5100_connect(parse_dotted_quad(cfg_server), nntp_port)) {
if (!w5100_connect_addr(parse_dotted_quad(cfg_server), nntp_port)) {
printf("Fail\n");
error_exit();
}
@ -645,19 +646,22 @@ sendmessage:
if (expect(buf, "200 ")) // "200" if posting is allowed
error_exit();
sprintf(sendbuf, "AUTHINFO USER %s\r\n", cfg_user);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
}
if (expect(buf, "381")) // Username accepted
error_exit();
// Skip authentication?
if (strcmp(cfg_user, "-") != 0) {
sprintf(sendbuf, "AUTHINFO USER %s\r\n", cfg_user);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
}
if (expect(buf, "381")) // Username accepted
error_exit();
sprintf(sendbuf, "AUTHINFO PASS %s\r\n", cfg_pass);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
sprintf(sendbuf, "AUTHINFO PASS %s\r\n", cfg_pass);
if (!w5100_tcp_send_recv(sendbuf, buf, NETBUFSZ, DO_SEND, CMD_MODE)) {
error_exit();
}
if (expect(buf, "281")) // Authentication successful
error_exit();
}
if (expect(buf, "281")) // Authentication successful
error_exit();
connected = 1;
}

View File

@ -534,17 +534,19 @@ void main(int argc, char *argv[]) {
// Abort on Ctrl-C to be consistent with Linenoise
abort_key = 0x83;
w5100_init(eth_init);
printf("Ok\nObtaining IP address - ");
if (dhcp_init()) {
ip65_error_exit();
}
// Copy IP config from IP65 to W5100
w5100_config(eth_init);
w5100_config();
printf("Ok\nConnecting to %s - ", cfg_server);
if (!w5100_connect(parse_dotted_quad(cfg_server), pop_port)) {
if (!w5100_connect_addr(parse_dotted_quad(cfg_server), pop_port)) {
printf("Fail\n");
error_exit();
}

313
apps/print65.c Normal file
View File

@ -0,0 +1,313 @@
/////////////////////////////////////////////////////////////////
// PRINT65
// Print files over the network to an HP Jetdirect printer
// Bobbi June 2021
/////////////////////////////////////////////////////////////////
#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 <apple2_filetype.h>
#include "../inc/ip65.h"
#include "w5100.h"
#include "email_common.h"
#define BELL 7
#define BACKSPACE 8
#define NORMAL 0x0e
#define INVERSE 0x0f
#define CLRLINE 0x1a
// 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];
static uint8_t exec_email_on_exit = 0;
static char filename[256];
static FILE *fp;
static uint32_t filesize;
static uint16_t jetdirect_port;
/*
* Keypress before quit
*/
void confirm_exit(void) {
printf("\n[Press 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);
}
}
/*
* Read a text file a line at a time
* Returns number of chars in the line, or 0 if EOF.
* Expects Apple ][ style line endings (CR) and does no conversion
* fp - file to read from
* reset - if 1 then just reset the buffer and return
* writep - Pointer to buffer into which line will be written
* n - length of buffer. Longer lines will be truncated and terminated with CR.
*/
uint16_t get_line(FILE *fp, uint8_t reset, char *writep, uint16_t n) {
static uint16_t rd = 0; // Read
static uint16_t end = 0; // End of valid data in buf
uint16_t i = 0;
if (reset) {
rd = end = 0;
return 0;
}
while (1) {
if (rd == end) {
end = fread(buf, 1, READSZ, fp);
rd = 0;
}
if (end == 0)
goto done;
if (i == n - 1) {
writep[i - 1] = '\r';
goto done;
}
writep[i++] = buf[rd++];
if (writep[i - 1] == '\r')
goto done;
}
done:
writep[i] = '\0';
return i;
}
#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
// Read file handle fp and send message over TCP
bool w5100_tcp_send() {
//
// Handle sending of email body
//
uint16_t pos = 0;
uint8_t cont = 1;
uint16_t snd;
uint16_t len;
filesize = 0;
len = get_line(fp, 1, linebuf, LINEBUFSZ); // Reset buffer
while (cont) {
len = get_line(fp, 0, linebuf, LINEBUFSZ - 1);
pos = 0;
if (len == 0) {
strcpy(linebuf, "\r\n");
len = 2;
cont = 0;
} else {
linebuf[len++] = '\n'; // CR -> CRLF
linebuf[len] = '\0';
filesize += len;
}
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 = linebuf + 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;
}
spinner(filesize, 0);
}
spinner(filesize, 1);
return true;
}
/*
* Read parms from PRINT.CFG
*/
void readconfigfile(void) {
char *colon;
fp = fopen("PRINT.CFG", "r");
if (!fp) {
puts("Can't open config file PRINT.CFG");
error_exit();
}
fscanf(fp, "%s", cfg_server);
fclose(fp);
colon = strchr(cfg_server, ':');
if (!colon)
jetdirect_port = 9100;
else {
jetdirect_port = atoi(colon + 1);
*colon = '\0';
}
}
void main(int argc, char *argv[]) {
uint8_t eth_init = ETH_INIT_DEFAULT, connected = 0;
videomode(VIDEOMODE_80COL);
printf("%c%s PRINT%c\n", 0x0f, PROGNAME, 0x0e);
puts("\nThis utility allows printing to a network-connected printer");
puts("using the HP Jetdirect protocol.\n");
if (argc == 2) {
strcpy(filename, argv[1]);
} else {
printf("\nFilename to print >");
scanf("%s", filename);
puts("");
}
readconfigfile();
{
int file;
printf("\nSetting slot - ");
file = open("ethernet.slot", O_RDONLY);
if (file != -1) {
read(file, &eth_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();
}
printf("Ok\n");
// Copy IP config from IP65 to W5100
w5100_init(eth_init);
w5100_config();
fp = fopen(filename, "rb");
if (!fp) {
printf("Can't open %s\n", filename);
error_exit();
}
if (!connected) {
printf("\nConnecting to %s:%d - ", cfg_server, jetdirect_port);
if (!w5100_connect_addr(parse_dotted_quad(cfg_server), jetdirect_port)) {
printf("Fail\n");
error_exit();
}
printf("Ok\n\n");
}
printf("Sending to printer ");
if (!w5100_tcp_send()) {
error_exit();
}
fclose(fp);
printf("Disconnecting\n");
w5100_disconnect();
confirm_exit();
}

View File

@ -163,6 +163,9 @@ void repair_mailbox(void) {
}
fclose(fp);
maxemailnum = 0;
minemailnum = 65535;
printf("** Scanning directory %s\n", dirname);
while (d = readdir(dp)) {
@ -180,6 +183,11 @@ void repair_mailbox(void) {
}
closedir(dp);
if (maxemailnum < minemailnum) {
printf("** No messages in this directory\n");
error_exit();
}
printf("** Will process EMAIL.%u to EMAIL.%u\n", minemailnum, maxemailnum);
for (emailnum = minemailnum; emailnum <= maxemailnum; ++emailnum) {
sprintf(filename, "%s/EMAIL.%u", dirname, emailnum);

View File

@ -1,6 +1,6 @@
ca65 setmac.s
ld65 setmac.o -t none -o setmac.system\#ff2000
ld65 setmac.o -t none -o SETMAC.SYSTEM\#FF2000
cp ../ip65.dsk ip65.po
cadius replacefile ip65.po /IP65 setmac.system\#ff2000
cadius replacefile ip65.po /IP65 SETMAC.SYSTEM\#FF2000
scp ip65.po pi@pi-eth:~/virtual-1.po

View File

@ -547,7 +547,8 @@ void main(int argc, char *argv[]) {
printf("Ok\n");
// Copy IP config from IP65 to W5100
w5100_config(eth_init);
w5100_init(eth_init);
w5100_config();
sprintf(filename, "%s/OUTBOX", cfg_emaildir);
dp = opendir(filename);
@ -577,7 +578,7 @@ void main(int argc, char *argv[]) {
strcpy(recipients, "");
while (1) {
if ((get_line(fp, 0, linebuf, LINEBUFSZ) == 0) || (linecount == 20)) {
if ((get_line(fp, 0, linebuf, LINEBUFSZ) == 0) || (linecount == 20) || (linebuf[0] == '\r')) {
if (strlen(recipients) == 0) {
printf("No recipients (To or Cc) in %s. Skipping msg.\n", d->d_name);
goto skiptonext;
@ -644,7 +645,7 @@ sendmessage:
if (!connected) {
printf("\nConnecting to %s - ", cfg_smtp_server);
if (!w5100_connect(parse_dotted_quad(cfg_smtp_server), smtp_port)) {
if (!w5100_connect_addr(parse_dotted_quad(cfg_smtp_server), smtp_port)) {
printf("Fail\n");
error_exit();
}

View File

@ -152,9 +152,9 @@ int main(void)
{
read(file, &eth_init, 1);
close(file);
eth_init &= ~'0';
eth_init &= 7;
}
printf("- %d\n", eth_init);
printf("- %u\n", eth_init);
}
#endif

View File

@ -65,12 +65,8 @@ static uint16_t addr_mask [2];
static void set_addr(uint16_t addr)
{
// The variables are necessary to have cc65 generate code
// suitable to access the W5100 auto-increment registers.
uint8_t addr_hi = addr >> 8;
uint8_t addr_lo = addr;
*w5100_addr_hi = addr_hi;
*w5100_addr_lo = addr_lo;
*w5100_addr_hi = addr >> 8;
*w5100_addr_lo = addr;
}
static uint8_t get_byte(uint16_t addr)
@ -91,54 +87,44 @@ static uint16_t get_word(uint16_t addr)
{
set_addr(addr);
{
// The variables are necessary to have cc65 generate code
// suitable to access the W5100 auto-increment registers.
uint8_t data_hi = *w5100_data;
uint8_t data_lo = *w5100_data;
return data_hi << 8 | data_lo;
}
return *w5100_data << 8 | *w5100_data;
}
static void set_word(uint16_t addr, uint16_t data)
{
set_addr(addr);
{
// The variables are necessary to have cc65 generate code
// suitable to access the W5100 auto-increment registers.
uint8_t data_hi = data >> 8;
uint8_t data_lo = data;
*w5100_data = data_hi;
*w5100_data = data_lo;
}
*w5100_data = data >> 8;
*w5100_data = data;
}
static void set_quad(uint16_t addr, uint32_t data)
{
set_addr(addr);
{
// The variables are necessary to have cc65 generate code
// suitable to access the W5100 auto-increment registers.
uint8_t data_1 = data;
uint8_t data_2 = data >> 8;
uint8_t data_3 = data >> 16;
uint8_t data_4 = data >> 24;
*w5100_data = data_1;
*w5100_data = data_2;
*w5100_data = data_3;
*w5100_data = data_4;
}
*w5100_data = data;
*w5100_data = data >> 8;
*w5100_data = data >> 16;
*w5100_data = data >> 24;
}
void w5100_config(uint8_t eth_init)
bool w5100_init(uint8_t eth_init)
{
w5100_mode = (uint8_t*)(eth_init << 4 | 0xC084);
w5100_addr_hi = w5100_mode + 1;
w5100_addr_lo = w5100_mode + 2;
w5100_data = w5100_mode + 3;
// PPP Link Control Protocol Request Timer Register defaults to 0x28
// on a real W5100. However, AppleWin features a virtual W5100 that
// supports DNS offloading. On that virtual W5100, the (otherwise
// anyhow unused) register defaults to 0x00 as detection mechanism.
// https://github.com/a2retrosystems/uthernet2/wiki/Virtual-W5100-with-DNS
return get_byte(0x0028) == 0x00;
}
void w5100_config(void)
{
#ifdef SINGLE_SOCKET
// IP65 is inhibited so disable the W5100 Ping Block Mode.
@ -193,14 +179,14 @@ void w5100_config(uint8_t eth_init)
}
}
bool w5100_connect(uint32_t addr, uint16_t port)
static bool w5100_connect(uint16_t port)
{
// Socket x Mode Register: TCP
set_byte(SOCK_REG(0x00), 0x01);
// Socket x Source Port Register
set_word(SOCK_REG(0x04), ip65_random_word());
// Socket x Destination Port Register
set_word(SOCK_REG(0x10), port);
// Socket x Command Register: OPEN
set_byte(SOCK_REG(0x01), 0x01);
@ -213,12 +199,6 @@ bool w5100_connect(uint32_t addr, uint16_t port)
}
}
// Socket x Destination IP Address Register
set_quad(SOCK_REG(0x0C), addr);
// Socket x Destination Port Register
set_word(SOCK_REG(0x10), port);
// Socket x Command Register: CONNECT
set_byte(SOCK_REG(0x01), 0x04);
@ -238,6 +218,34 @@ bool w5100_connect(uint32_t addr, uint16_t port)
}
}
bool w5100_connect_addr(uint32_t addr, uint16_t port)
{
// Socket x Mode Register: TCP, Use No Delayed ACK
set_byte(SOCK_REG(0x00), 0x21);
// Socket x Destination IP Address Register
set_quad(SOCK_REG(0x0C), addr);
return w5100_connect(port);
}
bool w5100_connect_name(const char* name, uint8_t length, uint16_t port)
{
// Socket x Mode Register: TCP, Use No Delayed ACK, Use DNS Offloading
set_byte(SOCK_REG(0x00), 0x29);
// Socket x DNS name length
set_byte(SOCK_REG(0x2A), length);
// Socket x DNS name chars
while (length--)
{
*w5100_data = *name++;
}
return w5100_connect(port);
}
bool w5100_connected(void)
{
// Socket x Status Register: SOCK_ESTABLISHED ?

View File

@ -46,13 +46,23 @@ void w5100_data_commit(bool do_send, uint16_t size);
// to be sent to the server.
extern volatile uint8_t* w5100_data;
// Initialize this module after the IP65 TCP/IP stack has been initialized.
// Return true if the (virtual) W5100 supports DNS Offloading.
// https://github.com/a2retrosystems/uthernet2/wiki/Virtual-W5100-with-DNS
bool w5100_init(uint8_t eth_init);
// Configure W5100 Ethernet controller with additional information from IP65
// after the IP65 TCP/IP stack has been configured.
void w5100_config(uint8_t eth_init);
void w5100_config(void);
// Connect to server with IP address <server_addr> on TCP port <server_port>.
// Connect to server with IP address <addr> on TCP port <port>.
// Return true if the connection is established, return false otherwise.
bool w5100_connect(uint32_t addr, uint16_t port);
bool w5100_connect_addr(uint32_t addr, uint16_t port);
// Connect to server with name <name>, <length> on TCP port <port> using
// DNS Offloading.
// Return true if the connection is established, return false otherwise.
bool w5100_connect_name(const char* name, uint8_t length, uint16_t port);
// Check if still connected to server.
// Return true if the connection is established, return false otherwise.

View File

@ -39,16 +39,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma optimize (on)
#pragma static-locals (on)
bool w5100_http_open(uint32_t addr, uint16_t port, const char* selector,
char* buffer, size_t length)
static bool w5100_http_open(const char* selector, char* buffer, size_t length)
{
printf("Connecting to %s:%d ", dotted_quad(addr), port);
if (!w5100_connect(addr, port))
{
printf("- Connect failed\n");
return false;
}
register volatile uint8_t *data = w5100_data;
printf("- Ok\n\nSending request ");
{
@ -82,15 +75,11 @@ bool w5100_http_open(uint32_t addr, uint16_t port, const char* selector,
}
{
// One less to allow for faster pre-increment below
const char *dataptr = selector + pos - 1;
const char *dataptr = selector + pos;
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;
*data = *dataptr++;
}
}
@ -132,17 +121,13 @@ bool w5100_http_open(uint32_t addr, uint16_t port, const char* selector,
}
{
// One less to allow for faster pre-increment below
char *dataptr = buffer + len - 1;
char *dataptr = buffer + len;
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;
*dataptr++ = *data;
if (!memcmp(dataptr - 3, "\r\n\r\n", 4))
if (!memcmp(dataptr - 4, "\r\n\r\n", 4))
{
rcv = i + 1;
body = true;
@ -183,3 +168,31 @@ bool w5100_http_open(uint32_t addr, uint16_t port, const char* selector,
}
return true;
}
bool w5100_http_open_addr(uint32_t addr, uint16_t port, const char* selector,
char* buffer, size_t length)
{
printf("Connecting to %s:%d ", dotted_quad(addr), port);
if (!w5100_connect_addr(addr, port))
{
printf("- Connect failed\n");
return false;
}
return w5100_http_open(selector, buffer, length);
}
bool w5100_http_open_name(const char* name, uint8_t name_length, uint16_t port,
const char* selector, char* buffer, size_t buffer_length)
{
printf("Connecting to port %d ", port);
if (!w5100_connect_name(name, name_length, port))
{
printf("- Connect failed\n");
return false;
}
return w5100_http_open(selector, buffer, buffer_length);
}

View File

@ -37,12 +37,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdint.h>
#include <stdbool.h>
// Connect to server with IP address <server_addr> on TCP port <server_port>,
// then HTTP GET <selector> and consume HTTP response header. Provide feedback
// on progress to the user via STDOUT. After returning from w5100_http_open()
// the connection is ready to consume the HTTP body.
// Connect to server with IP address <addr> on TCP port <port>, then HTTP GET
// <selector> and consume HTTP response header. Provide feedback on progress
// to the user via STDOUT. After returning from w5100_http_open_addr(), the
// connection is ready to consume the HTTP body.
// Return true if the connection is established, return false otherwise.
bool w5100_http_open(uint32_t addr, uint16_t port, const char* selector,
char* buffer, size_t length);
bool w5100_http_open_addr(uint32_t addr, uint16_t port, const char* selector,
char* buffer, size_t length);
// Connect to server with name <name>, <name_length> on TCP port <port> using
// DNS Offloading, then HTTP GET <selector> and consume HTTP response header.
// Provide feedback on progress to the user via STDOUT. After returning from
// w5100_http_open_name(), the connection is ready to consume the HTTP body.
// Return true if the connection is established, return false otherwise.
bool w5100_http_open_name(const char* name, uint8_t name_length, uint16_t port,
const char* selector, char* buffer, size_t buffer_length);
#endif

View File

@ -241,6 +241,7 @@ void exit_on_key(void)
void write_file(const char *name)
{
register volatile uint8_t *data = w5100_data;
uint16_t i;
int file;
uint16_t rcv;
@ -277,14 +278,10 @@ void write_file(const char *name)
}
{
// One less to allow for faster pre-increment below
char *dataptr = buffer + len - 1;
char *dataptr = buffer + len;
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;
*dataptr++ = *data;
}
}
@ -318,6 +315,7 @@ void write_file(const char *name)
void write_device(char device)
{
register volatile uint8_t *data = w5100_data;
uint16_t i;
dhandle_t dio;
uint16_t rcv;
@ -383,8 +381,7 @@ void write_device(char device)
rcv = sizeof(buffer) - len;
}
// One less to allow for faster pre-increment below
dataptr = buffer + len - 1;
dataptr = buffer + len;
}
else
{
@ -394,16 +391,12 @@ void write_device(char device)
rcv = 0x100 - len % 0x100;
}
// One less to allow for faster pre-increment below
dataptr = buffer + (skew[len / 0x100] << 8 | len % 0x100) - 1;
dataptr = buffer + (skew[len / 0x100] << 8 | len % 0x100);
}
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;
*dataptr++ = *data;
}
}
@ -442,6 +435,7 @@ int main(int, char *argv[])
uint16_t i;
char *arg;
char device;
bool Offload_DNS;
uint8_t eth_init = ETH_INIT_DEFAULT;
if (doesclrscrafterexit())
@ -475,11 +469,11 @@ int main(int, char *argv[])
{
read(file, &eth_init, 1);
close(file);
eth_init &= ~'0';
eth_init &= 7;
}
}
printf("- %d\n\nInitializing %s ", eth_init, eth_name);
printf("- %u\n\nInitializing %s ", eth_init, eth_name);
if (ip65_init(eth_init))
{
ip65_error_exit();
@ -488,15 +482,20 @@ int main(int, char *argv[])
// Abort on Ctrl-C to be consistent with Linenoise
abort_key = 0x83;
printf("- Ok\n\nObtaining IP address ");
if (dhcp_init())
Offload_DNS = w5100_init(eth_init);
if (!Offload_DNS)
{
ip65_error_exit();
printf("- Ok\n\nObtaining IP address ");
if (dhcp_init())
{
ip65_error_exit();
}
}
printf("- Ok\n\n");
// Copy IP config from IP65 to W5100
w5100_config(eth_init);
w5100_config();
load_argument("wget.urls");
while (true)
@ -504,7 +503,7 @@ int main(int, char *argv[])
arg = get_argument(1, "URL", url_completion);
printf("\n\nProcessing URL ");
if (!url_parse(arg))
if (!url_parse(arg, !Offload_DNS))
{
break;
}
@ -657,9 +656,21 @@ int main(int, char *argv[])
save_argument("wget.files");
printf("\n\n");
if (!w5100_http_open(url_ip, url_port, url_selector, buffer, sizeof(buffer)))
if (Offload_DNS)
{
return EXIT_FAILURE;
if (!w5100_http_open_name(url_host, strlen(url_host) - 4, url_port,
url_selector, buffer, sizeof(buffer)))
{
return EXIT_FAILURE;
}
}
else
{
if (!w5100_http_open_addr(url_ip, url_port,
url_selector, buffer, sizeof(buffer)))
{
return EXIT_FAILURE;
}
}
if (device)

View File

@ -1,23 +1,21 @@
# For assembler programs
# ----------------------
# c64rrnet.lib : C64 with RR-Net (or clone)
# c64eth64.lib : C64 with ETH64
# c64combo.lib : C64 with RR-Net or ETH64
# a2uther.lib : Apple ][ with Uthernet (default slot: #3)
# a2lancegs.lib : Apple ][ with LANceGS (default slot: #3)
# a2uther2.lib : Apple ][ with Uthernet II (default slot: #3)
# a2combo.lib : Apple ][ with Uthernet or LANceGS or Uthernet II (default slot: #3)
# atrdragon.lib : ATARI 8-bit with Dragon Cart
# atrdracarys.lib : ATARI 8-bit with Dracarys (default PBI device ID: #8)
# atrcombo.lib : ATARI 8-bit with Dragon Cart or Dracarys (default PBI device ID: #8)
# vic20rrnet.lib : VIC20 with RR-Net (or clone)
# c64rrnet.lib : C64 with RR-Net (or clone)
# c64eth64.lib : C64 with ETH64
# c64combo.lib : C64 with RR-Net or ETH64
# a2uther.lib : Apple ][ with Uthernet (default slot: #3)
# a2uther2.lib : Apple ][ with Uthernet II (default slot: #3)
# a2lancegs.lib : Apple ][ with LANceGS (default slot: #3)
# a2combo.lib : Apple ][ with Uthernet or Uthernet II or LANceGS (default slot: #3)
# atrdragon.lib : ATARI 8-bit with Dragon Cart
# vic20rrnet.lib : VIC20 with RR-Net (or clone)
# For C programs
# --------------
# ip65_c64.lib : C64 with RR-Net or ETH64
# ip65_apple2.lib : Apple ][ with Uthernet or LANceGS or Uthernet II (default slot: #3)
# ip65_atari.lib : ATARI 8-bit with Dragon Cart or Dracarys (default PBI device ID: #8)
# ip65_atarixl.lib : ATARI XL with Dragon Cart or Dracarys (default PBI device ID: #8)
# ip65_apple2.lib : Apple ][ with Uthernet or Uthernet II or LANceGS (default slot: #3)
# ip65_atari.lib : ATARI 8-bit with Dragon Cart
# ip65_atarixl.lib : ATARI XL with Dragon Cart
DRIVERS=\
c64rrnet.lib \
@ -25,14 +23,12 @@ DRIVERS=\
c64combo.lib \
ip65_c64.lib \
a2uther.lib \
a2lancegs.lib \
a2uther2.lib \
a2lancegs.lib \
a2combo.lib \
ip65_apple2.lib \
ip65_apple2_uther2.lib \
atrdragon.lib \
atrdracarys.lib \
atrcombo.lib \
ip65_atari.lib \
ip65_atarixl.lib \
vic20rrnet.lib
@ -118,9 +114,9 @@ rr-net.o uthernet.o dragoncart.o vic20-rr-net.o: cs8900a.s
eth64.o lancegs.o: lan91c96.s
uthernet2.o dracarys.o: w5100.s
uthernet2.o: w5100.s
c64combo.o a2combo.o atrcombo.o: ethernetcombo.s
c64combo.o a2combo.o: ethernetcombo.s
c64rrnet.lib: rr-net.o $(CS8900AOBJS) c64init.o $(C64OBJS)
@ -132,10 +128,10 @@ ip65_c64.lib: rr-net.o eth64.o c64combo.o c64init.o $(C64_OBJS)
a2uther.lib: uthernet.o $(CS8900AOBJS) a2init.o $(A2OBJS)
a2lancegs.lib: lancegs.o $(LAN91C96OBJS) a2init.o $(A2OBJS)
a2uther2.lib: uthernet2.o $(W5100OBJS) a2init.o $(A2OBJS)
a2lancegs.lib: lancegs.o $(LAN91C96OBJS) a2init.o $(A2OBJS)
a2combo.lib: uthernet.o lancegs.o uthernet2.o a2combo.o a2init.o $(A2OBJS)
ip65_apple2.lib: uthernet.o lancegs.o uthernet2.o a2combo.o a2init.o $(A2_OBJS)
@ -144,13 +140,9 @@ ip65_apple2_uther2.lib: uthernet2.o $(W5100OBJS) a2init.o $(A2_OBJS)
atrdragon.lib: dragoncart.o $(CS8900AOBJS) atrinit.o $(ATROBJS)
atrdracarys.lib: dracarys.o $(W5100OBJS) atrinit.o $(ATROBJS)
ip65_atari.lib: dragoncart.o $(CS8900AOBJS) atrinit.o $(ATR_OBJS)
atrcombo.lib: dragoncart.o dracarys.o atrcombo.o atrinit.o $(ATROBJS)
ip65_atari.lib: dragoncart.o dracarys.o atrcombo.o atrinit.o $(ATR_OBJS)
ip65_atarixl.lib: dragoncart.o dracarys.o atrcombo.o atrinit.o $(ATRXL_OBJS)
ip65_atarixl.lib: dragoncart.o $(CS8900AOBJS) atrinit.o $(ATRXL_OBJS)
vic20rrnet.lib: vic20-rr-net.o $(CS8900AOBJS) vic20init.o $(VIC20OBJS)

View File

@ -1 +1 @@
.exportzp eth_init_default = 5 ; Apple 2 default slot
.exportzp eth_init_default = 3 ; Apple 2 default slot

View File

@ -1 +1 @@
.exportzp eth_init_default = 8 ; PBI default device ID
.exportzp eth_init_default = 0

View File

@ -132,7 +132,7 @@ set_name:
eth_init:
sta eth_init_value
.if .defined (__APPLE2__) .or .defined (__ATARI__)
.if .defined (__APPLE2__)
ldax #_w5100
jsr patch_wrapper
ldax #_w5100_name

View File

@ -41,12 +41,7 @@
; Ethernet address
mac: .byte $00, $08, $DC ; OUI of WIZnet
.ifdef __APPLE2__
.byte $A2, $A2, $A2
.endif
.ifdef __ATARI__
.byte $A8, $A8, $A8
.endif
; Buffer attributes
bufaddr:.res 2 ; Address
@ -91,8 +86,6 @@ tmp := tmp4 ; Temporary value
;=====================================================================
.ifdef __APPLE2__
.rodata
fixup: .byte fixup02-fixup01, fixup03-fixup02, fixup04-fixup03
@ -155,54 +148,6 @@ init:
bcs :- ; Always
:
.endif
;=====================================================================
.ifdef __ATARI__
.rodata
pdtab: .byte %00000001
.byte %00000010
.byte %00000100
.byte %00001000
.byte %00010000
.byte %00100000
.byte %01000000
.byte %10000000
;---------------------------------------------------------------------
.bss
pdbit: .res 1
;---------------------------------------------------------------------
mode := $D1F0
addr := $D1F1
data := $D1F3
pdvs := $D1FF ; parallel device select
shpdvs := $0248 ; shadow parallel device select
;---------------------------------------------------------------------
.code
init:
; Convert parallel device ID (1-8) to parallel device bit
tay
lda pdtab-1,y
sta pdbit
; Select parallel device
sta shpdvs
sta pdvs
.endif
;=====================================================================
; Indirect Bus I/F mode, Address Auto-Increment
@ -287,13 +232,6 @@ fixup14:sta data
;---------------------------------------------------------------------
poll:
.ifdef __ATARI__
; Select parallel device
lda pdbit
sta shpdvs
sta pdvs
.endif
; Check for completion of previous command
; Socket 0 Command Register: = 0 ?
jsr set_addrcmdreg0
@ -401,13 +339,6 @@ send:
sta adv
stx adv+1
.ifdef __ATARI__
; Select parallel device
lda pdbit
sta shpdvs
sta pdvs
.endif
; Set parameters for transmitting data
lda #>$4000 ; Socket 0 TX Base Address
ldx #$01 ; Write

View File

@ -7,9 +7,7 @@
// Ethernet driver initialization parameter values
//
#if defined(__APPLE2__)
#define ETH_INIT_DEFAULT 5 // Apple II slot number
#elif defined(__ATARI__)
#define ETH_INIT_DEFAULT 8 // ATARI PBI device ID
#define ETH_INIT_DEFAULT 3 // Apple II slot number
#else
#define ETH_INIT_DEFAULT 0 // Unused
#endif
@ -294,16 +292,18 @@ bool __fastcall__ tftp_upload_from_memory(uint32_t server, const char* name,
//
// On success the variables url_ip, url_port and url_selector (see below) are valid.
//
// Inputs: url: Zero (or ctrl char) terminated string containing the URL
// Inputs: url: Zero (or ctrl char) terminated string containing the URL
// resolve: Resolve host in URL
// Output: true if an error occured, false otherwise
//
bool __fastcall__ url_parse(const char* url);
bool __fastcall__ url_parse(const char* url, bool resolve);
// Access to parsed HTTP URL
//
// Access to the three items below is only valid after url_parse returned false.
// Access to the four items below is only valid after url_parse returned false.
//
extern uint32_t url_ip; // IP address of host in URL
extern char* url_host; // Zero terminated string containing host in URL + "\r\n\r\n"
extern uint32_t url_ip; // IP address of host in URL (only if 'resolve' is true)
extern uint16_t url_port; // Port number of URL
extern char* url_selector; // Zero terminated string containing selector part of URL

View File

@ -85,6 +85,7 @@ dhcp_message_sent_count: .res 1
dhcp_timer: .res 1
dhcp_loop_count: .res 1
dhcp_break_polling_loop: .res 1
dhcp_ip: .res 4
; DHCP constants
BOOTREQUEST = 1
@ -315,9 +316,9 @@ dhcp_in:
cmp dhcp_inp+dhcp_yiaddr ; is the first byte in the assigned address 0?
bne :+
rts ; if so, it's a bogus response - ignore
: ldx #4 ; copy the our new IP address
: ldx #3 ; copy the new IP address
: lda dhcp_inp+dhcp_yiaddr,x
sta cfg_ip,x
sta dhcp_ip,x
dex
bpl :-
@ -423,7 +424,7 @@ send_dhcprequest:
ldx #4 ; option length is 4
stx output_buffer+dhcp_options+4
dex
: lda cfg_ip,x
: lda dhcp_ip,x
sta output_buffer+dhcp_options+5,x
dex
bpl :-
@ -449,9 +450,16 @@ send_dhcprequest:
ldax #output_buffer
jsr udp_send
bcs :+ ; if we didn't send the message we probably need to wait for an ARP reply to come back.
bcs :++ ; if we didn't send the message we probably need to wait for an ARP reply to come back.
lda #dhcp_bound ; technically, we should wait till we get a DHCPACK message. but we'll assume success
sta dhcp_state
ldx #3 ; set the new IP address
: lda dhcp_ip,x
sta cfg_ip,x
dex
bpl :-
: rts

View File

@ -61,6 +61,7 @@ url_download:
sty url_selector
ldy url_download_buffer+1
sty url_selector+1
ldy #1
jsr url_parse_buffer
bcc resource_download
rts

View File

@ -65,7 +65,7 @@ _udp_recv_src_port:
ldx udp_inp+udp_src_port
rts
_udp_send:
_udp_send:
stax udp_send_src_port
jsr popax
stax udp_send_dest_port

View File

@ -13,6 +13,7 @@
.import parse_integer
.import dns_ip
.export url_host
.export url_ip
.export url_port
.export url_selector
@ -26,7 +27,8 @@ search_string = ptr1
.bss
url_string: .res 2
url_ip: .res 4 ; will be set with ip address of host in url
url_host: .res 2 ; will be set with hostname + CRLFCRLF
url_ip: .res 4 ; will be set with ip address of host in url (if resolve)
url_port: .res 2 ; will be set with port number of url
url_selector: .res 2 ; will be set with address of selector part of URL
url_type: .res 1
@ -35,6 +37,7 @@ search_string = ptr1
url_type_gopher = 1
url_type_http = 2
resolve: .res 1
src_ptr: .res 1
dest_ptr: .res 1
@ -46,28 +49,33 @@ search_string = ptr1
; inputs:
; AX = address of URL string
; any control character (i.e. <$20) is treated as 'end of string', e.g. a CR or LF, as well as $00
; Y = do resolve hostname
; outputs:
; sec if a malformed url, otherwise:
; url_ip = ip address of host in url
; url_port = port number of url
; url_selector = address of selector part of URL
url_parse:
sty resolve
ldy #<output_buffer
sty url_selector
ldy #>output_buffer
sty url_selector+1
ldy resolve
; parses a URL into a form that makes it easy to retrieve the specified resource
; caution - the resulting selector part of URL must fit into the provided buffer !!!
; inputs:
; AX = address of URL string
; any control character (i.e. <$20) is treated as 'end of string', e.g. a CR or LF, as well as $00
; Y = do resolve hostname
; url_selector = points to a buffer that selector part of URL will be placed into
; outputs:
; sec if a malformed url, otherwise:
; url_ip = ip address of host in url
; url_port = port number of url
url_parse_buffer:
sty resolve
stax url_string
ldy #url_type_http
sty url_type
@ -109,6 +117,8 @@ lda #url_type_gopher
; now pointing at hostname
bcs @exit_with_error
@no_protocol_specifier:
ldy resolve
beq @no_resolve
jsr dns_set_hostname
bcs @exit_with_sec
jsr dns_resolve
@ -125,6 +135,7 @@ lda #url_type_gopher
jsr skip_to_hostname
@no_resolve:
; skip over next colon
ldax #colon
jsr parser_skip_next
@ -212,11 +223,17 @@ lda #url_type_gopher
jsr skip_to_hostname
; AX now pointing at hostname
stax ptr1
clc
lda url_selector
sta ptr2
adc dest_ptr
sta url_host
pla
sta ptr2+1
adc #0
sta url_host+1
lda #0
sta src_ptr

View File

@ -1,22 +1,32 @@
.include "../inc/common.inc"
.export _url_parse
.export _url_host
.export _url_ip
.export _url_port
.export _url_selector
.import url_parse
.import url_host
.import url_ip
.import url_port
.import url_selector
.import popax
.importzp tmp1
_url_parse:
sta tmp1
jsr popax
ldy tmp1
jsr url_parse
ldx #$00
txa
rol
rts
_url_host := url_host
_url_ip := url_ip
_url_port := url_port

BIN
releases/emailler-2.1.0.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.1.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.10.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.11.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.12.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.13.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.14.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.2.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.3.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.4.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.5.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.6.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.7.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.8.po Normal file

Binary file not shown.

BIN
releases/emailler-2.1.9.po Normal file

Binary file not shown.

View File

@ -17,15 +17,14 @@ else ifeq ($(eth),sm)
A2DRIVERLIB = ../drivers/a2lancegs.lib
else ifeq ($(eth),wn)
A2DRIVERLIB = ../drivers/a2uther2.lib
ATRDRIVERLIB = ../drivers/atrdracarys.lib
else
C64DRIVERLIB = ../drivers/c64combo.lib
A2DRIVERLIB = ../drivers/a2combo.lib
ATRDRIVERLIB = ../drivers/atrcombo.lib
ATRDRIVERLIB = ../drivers/atrdragon.lib
VICDRIVERLIB = ../drivers/vic20rrnet.lib
endif
# See http://vice-emu.sourceforge.net/
# See https://vice-emu.sourceforge.net/
C1541 ?= c1541
# See https://applecommander.github.io/
@ -112,32 +111,28 @@ vt100.com: ATARI_CFG = ../apps/atrtelnet.cfg
%.o: %.c
%.prg: %.o ip65 drivers
ld65 -o $*.prg -C c64.cfg -m $*.c64.map -vm $< $(IP65LIB) $(C64DRIVERLIB) c64.lib
ld65 -o $*.prg -C c64.cfg -m $*.prg.map -vm $< $(IP65LIB) $(C64DRIVERLIB) c64.lib
%.bin: %.o ip65 drivers
ld65 -o $*.bin -C apple2.cfg -m $*.a2.map -vm $< $(IP65LIB) $(A2DRIVERLIB) apple2.lib
ld65 -o $*.bin -C apple2.cfg -m $*.bin.map -vm $< $(IP65LIB) $(A2DRIVERLIB) apple2.lib
%.com: %.o ip65 drivers
ld65 -o $*.com -C $(ATARI_CFG) -m $*.atr.map -vm $< $(IP65LIB) $(ATRDRIVERLIB) atari.lib
ld65 -o $*.com -C $(ATARI_CFG) -m $*.com.map -vm $< $(IP65LIB) $(ATRDRIVERLIB) atari.lib
%.vicprg: %.o ip65 drivers
ld65 -o $*.vicprg -C vic20-32k.cfg -m $*.vic.map -vm $< $(IP65LIB) $(VICDRIVERLIB) vic20.lib
ld65 -o $*.vicprg -C vic20-32k.cfg -m $*.vicprg.map -vm $< $(IP65LIB) $(VICDRIVERLIB) vic20.lib
%.prg: %.c ip65 drivers
cl65 -o $*.prg -O -t c64 -m $*.c64.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_c64.lib
rm $*.o
cl65 -o $*.prg -O -t c64 -m $*.prg.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_c64.lib
%.bin: %.c ip65 drivers
cl65 -o $*.bin -O -t apple2 -m $*.a2.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_apple2.lib
rm $*.o
cl65 -o $*.bin -O -t apple2 -m $*.bin.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_apple2.lib
%.com: %.c ip65 drivers
cl65 -o $*.com -O -t atari -m $*.atr.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_atari.lib
rm $*.o
cl65 -o $*.com -O -t atari -m $*.com.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_atari.lib
%.xl.com: %.c ip65 drivers
cl65 -o $*.xl.com -O -t atarixl -m $*.atrxl.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_atarixl.lib
rm $*.o
cl65 -o $*.xl.com -O -t atarixl -m $*.xl.com.map -vm $< $(wildcard $**.s) $(IP65LIB) ../drivers/ip65_atarixl.lib
ip65test.d64: prg
$(C1541) -format ip65,00 d64 $@

View File

@ -17,7 +17,6 @@
.import url_port
.import url_selector
.import url_resource_type
.import url_parse
.import url_download
.import url_download_buffer
.import url_download_buffer_length

View File

@ -100,6 +100,7 @@ test_url_parse:
jsr print
jsr print_cr
ldax temp_url_ptr
ldy #1
jsr url_parse
bcc :+
jmp print_errorcode