summaryrefslogtreecommitdiffstats
path: root/call/yapp.c
diff options
context:
space:
mode:
Diffstat (limited to 'call/yapp.c')
-rw-r--r--call/yapp.c840
1 files changed, 840 insertions, 0 deletions
diff --git a/call/yapp.c b/call/yapp.c
new file mode 100644
index 0000000..3deb6d1
--- /dev/null
+++ b/call/yapp.c
@@ -0,0 +1,840 @@
+/* yapp.c
+ *
+ * Copyright (C) 1994 by Jonathan Naylor
+ *
+ * This module implements the YAPP file transfer protocol as defined by Jeff
+ * Jacobsen WA7MBL in the files yappxfer.doc and yappxfer.pas.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the license, or (at your option) any later version.
+ */
+
+/*
+ * Yapp C and Resume support added by S N Henson.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+#include <linux/ax25.h>
+#include <sys/stat.h>
+
+#include "call.h"
+
+#define TIMEOUT 300 /* 5 Minutes */
+
+#define NUL 0x00
+#define SOH 0x01
+#define STX 0x02
+#define ETX 0x03
+#define EOT 0x04
+#define ENQ 0x05
+#define ACK 0x06
+#define DLE 0x10
+#define NAK 0x15
+#define CAN 0x18
+
+#define STATE_S 0
+#define STATE_SH 1
+#define STATE_SD 2
+#define STATE_SE 3
+#define STATE_ST 4
+#define STATE_R 5
+#define STATE_RH 6
+#define STATE_RD 7
+
+static int state;
+static int total = 0;
+
+static int readlen = 0;
+static int outlen = 0;
+static int outbufptr = 0;
+static unsigned char outbuffer[512];
+static char yappc; /* Nonzero if using YAPP C */
+
+static void Write_Status(char *s)
+{
+ fprintf(stdout, "State: %-60s\r", s);
+ fflush(stdout);
+}
+
+static void Send_RR(void)
+{
+ char buffer[2];
+
+ buffer[0] = ACK;
+ buffer[1] = 0x01;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_RF(void)
+{
+ char buffer[2];
+
+ buffer[0] = ACK;
+ buffer[1] = 0x02;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_RT(void)
+{
+ char buffer[2];
+
+ buffer[0] = ACK;
+ buffer[1] = ACK;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_AF(void)
+{
+ char buffer[2];
+
+ buffer[0] = ACK;
+ buffer[1] = 0x03;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_AT(void)
+{
+ char buffer[2];
+
+ buffer[0] = ACK;
+ buffer[1] = 0x04;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_NR(char *reason)
+{
+ char buffer[257];
+ int length;
+
+ if ((length = strlen(reason)) > 255)
+ length = 255;
+
+ buffer[0] = NAK;
+ buffer[1] = length;
+ memcpy(buffer + 2, reason, length);
+
+ write(fd, buffer, length + 2);
+}
+
+/* Send a Resume Sequence */
+
+static void Send_RS(int length)
+{
+ char buffer[256];
+ int len;
+
+ buffer[0] = NAK;
+ buffer[2] = 'R';
+ buffer[3] = 0;
+
+ len = sprintf(buffer + 4, "%d", length) + 5;
+
+ buffer[len] = 'C';
+ buffer[len + 1] = 0;
+ buffer[1] = len;
+
+ write(fd, buffer, len + 2);
+}
+
+static void Send_SI(void)
+{
+ char buffer[2];
+
+ buffer[0] = ENQ;
+ buffer[1] = 0x01;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_CN(char *reason)
+{
+ char buffer[257];
+ int length;
+
+ if ((length = strlen(reason)) > 255)
+ length = 255;
+
+ buffer[0] = CAN;
+ buffer[1] = length;
+ memcpy(buffer + 2, reason, length);
+
+ write(fd, buffer, length + 2);
+}
+
+static void Send_HD(char *filename, long length)
+{
+ char buffer[257];
+ char size_buffer[10];
+ int len_filename;
+ int len_size;
+ int len;
+ struct stat sb;
+
+ sprintf(size_buffer, "%ld", length);
+
+ len_filename = strlen(filename) + 1; /* Include the NUL */
+ len_size = strlen(size_buffer) + 1; /* Include the NUL */
+
+ len = len_filename + len_size;
+
+ if (!stat(filename, &sb))
+ {
+ unix2yapp(sb.st_mtime, buffer + len + 2);
+ len += 9;
+ }
+
+ buffer[0] = SOH;
+ buffer[1] = len;
+
+ memcpy(buffer + 2, filename, len_filename);
+ memcpy(buffer + len_filename + 2, size_buffer, len_size);
+
+ write(fd, buffer, len + 2);
+}
+
+static void Send_ET(void)
+{
+ char buffer[2];
+
+ buffer[0] = EOT;
+ buffer[1] = 0x01;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_DT(int length)
+{
+ char buffer[2];
+
+ if (length > 255) length = 0;
+
+ buffer[0] = STX;
+ buffer[1] = length;
+
+ write(fd, buffer, 2);
+}
+
+static void Send_EF(void)
+{
+ char buffer[2];
+
+ buffer[0] = ETX;
+ buffer[1] = 0x01;
+
+ write(fd, buffer, 2);
+}
+
+static unsigned char checksum(unsigned char *buf, int len)
+{
+ int i;
+ unsigned char sum = 0;
+
+ for (i = 0; i < len; i++)
+ sum += buf[i];
+
+ return sum;
+}
+
+static int yapp_download_data(int *filefd, unsigned char *buffer)
+{
+ int length,file_time;
+ char Message[50];
+
+ if (buffer[0] == CAN || buffer[0] == NAK)
+ {
+ Write_Status("RcdABORT");
+ return(FALSE);
+ }
+
+ switch (state)
+ {
+ case STATE_R:
+ if (buffer[0] == ENQ && buffer[1] == 0x01)
+ {
+ Send_RR();
+ Write_Status("RcvHeader");
+ state = STATE_RH;
+ break;
+ }
+
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+
+ case STATE_RH:
+ if (buffer[0] == SOH)
+ {
+ /* Parse header: 3 fields == YAPP C */
+ char *hptr, *hfield[3];
+ if ((length = buffer[1]) == 0) length = 256;
+ hptr = (char *)buffer + 2;
+ while (length > 0)
+ {
+ int hlen;
+ hlen = strlen(hptr) + 1;
+ hfield[(int)yappc++] = hptr;
+ hptr += hlen;
+ length -= hlen;
+ }
+
+ if (yappc < 3)
+ {
+ yappc = 0;
+ }
+ else
+ {
+ file_time = yapp2unix(hfield[2]);
+ yappc = 1;
+ }
+
+ if (*filefd == -1)
+ {
+ if ((*filefd = open(hfield[0], O_RDWR | O_APPEND | O_CREAT, 0666)) == -1)
+ {
+ printf("\n[Unable to open %s]\n", hfield[0]);
+ Send_NR("Invalid filename");
+ return(FALSE);
+ }
+ }
+
+ printf("Receiving %s %s %s", hfield[0], hfield[1],
+ yappc ? ctime((time_t *)&file_time) : " ");
+
+ if (yappc)
+ {
+ struct stat sb;
+
+ if (!fstat(*filefd, &sb) && sb.st_size)
+ Send_RS(sb.st_size);
+ else
+ Send_RT();
+ }
+ else
+ {
+ Send_RF();
+ }
+
+ state = STATE_RD;
+ break;
+ }
+
+ if (buffer[0] == ENQ && buffer[1] == 0x01)
+ {
+ break;
+ }
+
+ if (buffer[0] == EOT && buffer[1] == 0x01)
+ {
+ Send_AT();
+ Write_Status("RcvEOT");
+ return(FALSE);
+ }
+
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+
+ case STATE_RD:
+ if (buffer[0] == STX)
+ {
+ if ((length = buffer[1]) == 0) length = 256;
+ total += length;
+ sprintf(Message, "RcvData %5d bytes received", total);
+ Write_Status(Message);
+
+ if (yappc)
+ {
+ int i;
+ unsigned char checksum = 0;
+
+ for (i = 0; i < length; i++)
+ checksum += buffer[i + 2];
+
+ if (checksum != buffer[length + 2])
+ {
+ Send_CN("Bad Checksum");
+ Write_Status("SndABORT: Bad Checksum");
+ return(FALSE);
+ }
+ }
+
+ write(*filefd, buffer + 2, length);
+ break;
+ }
+
+ if (buffer[0] == ETX && buffer[1] == 0x01)
+ {
+ Send_AF();
+ Write_Status("RcvEof");
+ state = STATE_RH;
+ close(*filefd);
+ *filefd = -1;
+ break;
+ }
+
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+static void yapp_download(int filefd)
+{
+ struct timeval timeval;
+ fd_set sock_read;
+ int n;
+ int buflen = 0;
+ int length;
+ int used;
+ unsigned char buffer[1024];
+
+ Write_Status("RcvWait");
+
+ state = STATE_R;
+ total = 0;
+ yappc = 0;
+
+ while (TRUE)
+ {
+ FD_ZERO(&sock_read);
+ FD_SET(STDIN_FILENO, &sock_read);
+ FD_SET(fd, &sock_read);
+
+ timeval.tv_usec = 0;
+ timeval.tv_sec = TIMEOUT;
+
+ n = select(fd + 1, &sock_read, NULL, NULL, &timeval);
+
+ if (n == -1)
+ {
+ if (!interrupted && errno == EAGAIN)
+ continue;
+ if (!interrupted)
+ perror("select");
+ Send_CN("Internal error");
+ Write_Status("SndABORT");
+ return;
+ }
+
+ if (n == 0) /* Timeout */
+ {
+ Send_CN("Timeout");
+ Write_Status("SndABORT");
+ return;
+ }
+
+ if (FD_ISSET(STDIN_FILENO, &sock_read))
+ {
+ Send_CN("Cancelled by user");
+ Write_Status("SndABORT");
+ return;
+ }
+
+ if (FD_ISSET(fd, &sock_read))
+ {
+ if ((length = read(fd, buffer + buflen, 511)) > 0)
+ {
+ buflen += length;
+
+ do
+ {
+ used = FALSE;
+
+ switch (buffer[0])
+ {
+ case ACK:
+ case ENQ:
+ case ETX:
+ case EOT:
+ if (buflen >= 2)
+ {
+ if (!yapp_download_data(&filefd, buffer))
+ return;
+ buflen -= 2;
+ memcpy(buffer, buffer + 2, buflen);
+ used = TRUE;
+ }
+ break;
+ default:
+ if ((length = buffer[1]) == 0)
+ length = 256;
+ if (buffer[0] == STX)
+ length += yappc;
+ if (buflen >= (length + 2))
+ {
+ if (!yapp_download_data(&filefd, buffer))
+ return;
+ buflen -= length + 2;
+ memcpy(buffer, buffer + length + 2, buflen);
+ used = TRUE;
+ }
+ break;
+ }
+ }
+ while (used);
+ }
+ }
+ }
+}
+
+static int yapp_upload_data(int filefd, char *filename, int filelength, unsigned char *buffer)
+{
+ char Message[80];
+
+ if (buffer[0] == CAN || buffer[0] == NAK)
+ {
+ Write_Status("RcvABORT");
+ return(FALSE);
+ }
+
+ switch (state)
+ {
+ case STATE_S:
+ if (buffer[0] == ACK && buffer[1] == 0x01)
+ {
+ Write_Status("SendHeader");
+ Send_HD(filename, filelength);
+ state = STATE_SH;
+ break;
+ }
+
+ if (buffer[0] == ACK && buffer[1] == 0x02)
+ {
+ sprintf(Message, "SendData %5d bytes transmitted", total);
+ Write_Status(Message);
+ outlen = read(filefd, outbuffer, readlen);
+ outbufptr = 0;
+
+ if (outlen) Send_DT(outlen);
+
+ if (yappc)
+ {
+ outbuffer[outlen] = checksum(outbuffer, outlen);
+ outlen++;
+ }
+
+ state = STATE_SD;
+ break;
+ }
+
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+
+ case STATE_SH:
+ /* Could get three replies here:
+ * ACK 02 : normal acknowledge.
+ * ACK ACK: yappc acknowledge.
+ * NAK ...: resume request.
+ */
+ if (buffer[0] == NAK && buffer[2] == 'R')
+ {
+ int len;
+ off_t rpos;
+
+ len = buffer[1];
+ if (buffer[len] == 'C') yappc=1;
+ rpos = atol((char *)buffer + 4);
+ lseek(filefd, rpos, SEEK_SET);
+ buffer[0] = ACK;
+ buffer[1] = yappc ? ACK : 0x02;
+ }
+
+ if (buffer[0] == ACK &&
+ (buffer[1] == 0x02 || buffer[1] == ACK))
+ {
+ if (buffer[1] == ACK) yappc = 1;
+
+ sprintf(Message, "SendData %5d bytes transmitted", total);
+ Write_Status(Message);
+ outlen = read(filefd, outbuffer, readlen);
+ outbufptr = 0;
+ if (outlen) Send_DT(outlen);
+ state = STATE_SD;
+
+ if (yappc)
+ {
+ outbuffer[outlen] = checksum(outbuffer, outlen);
+ outlen++;
+ }
+ break;
+ }
+
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+
+ case STATE_SD:
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+
+ case STATE_SE:
+ if (buffer[0] == ACK && buffer[1] == 0x03)
+ {
+ Write_Status("SendEOT");
+ Send_ET();
+ state = STATE_ST;
+ break;
+ }
+
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+
+ case STATE_ST:
+ if (buffer[0] == ACK && buffer[1] == 0x04)
+ {
+ return(FALSE);
+ }
+
+ Send_CN("Unknown code");
+ Write_Status("SndABORT");
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+static void yapp_upload(int filefd, char *filename, long filelength)
+{
+ struct timeval timeval;
+ fd_set sock_read;
+ fd_set sock_write;
+ int n;
+ unsigned char buffer[1024];
+ int buflen = 0;
+ int length;
+ int used;
+ char Message[80];
+
+ Write_Status("SendInit");
+
+ readlen = (paclen - 2 > 253) ? 253 : paclen - 2;
+ state = STATE_S;
+ total = 0;
+ yappc = 0;
+
+ Send_SI();
+
+ while (TRUE)
+ {
+ FD_ZERO(&sock_read);
+ FD_ZERO(&sock_write);
+ FD_SET(STDIN_FILENO, &sock_read);
+ FD_SET(fd, &sock_read);
+
+ if (state == STATE_SD)
+ {
+ FD_SET(fd, &sock_write);
+
+ n = select(fd + 1, &sock_read, &sock_write, NULL, NULL);
+ }
+ else
+ {
+ timeval.tv_usec = 0;
+ timeval.tv_sec = TIMEOUT;
+
+ n = select(fd + 1, &sock_read, NULL, NULL, &timeval);
+ }
+
+ if (n == -1)
+ {
+ if (!interrupted && errno == EAGAIN)
+ continue;
+ if (!interrupted)
+ perror("select");
+ Write_Status("SndABORT");
+ Send_CN("Internal error");
+ return;
+ }
+
+ if (n == 0) /* Timeout, not STATE_SD */
+ {
+ Write_Status("SndABORT");
+ Send_CN("Timeout");
+ return;
+ }
+
+ if (FD_ISSET(STDIN_FILENO, &sock_read))
+ {
+ Write_Status("SndABORT");
+ Send_CN("Cancelled by user");
+ return;
+ }
+
+ if (FD_ISSET(fd, &sock_write)) /* Writable, only STATE_SD */
+ {
+ if (outlen > 0)
+ {
+ if ((n = write(fd, outbuffer + outbufptr, outlen)) > 0)
+ {
+ outbufptr += n;
+ outlen -= n;
+ total += n;
+ }
+ }
+
+ if (outlen == 0)
+ {
+ total -= yappc;
+ if ((outlen = read(filefd, outbuffer, readlen)) > 0)
+ {
+ sprintf(Message, "SendData %5d bytes transmitted", total);
+ Write_Status(Message);
+
+ outbufptr = 0;
+ Send_DT(outlen);
+
+ if (yappc)
+ {
+ outbuffer[outlen] = checksum(outbuffer, outlen);
+ outlen++;
+ }
+ }
+ else
+ {
+ Write_Status("SendEof");
+ state = STATE_SE;
+ Send_EF();
+ }
+ }
+ }
+
+ if (FD_ISSET(fd, &sock_read))
+ {
+ if ((length = read(fd, buffer + buflen, 511)) > 0)
+ {
+ buflen += length;
+
+ do
+ {
+ used = FALSE;
+
+ switch (buffer[0])
+ {
+ case ACK:
+ case ENQ:
+ case ETX:
+ case EOT:
+ if (buflen >= 2)
+ {
+ if (!yapp_upload_data(filefd, filename, filelength, buffer))
+ return;
+ buflen -= 2;
+ memcpy(buffer, buffer + 2, buflen);
+ used = TRUE;
+ }
+ break;
+ default:
+ if ((length = buffer[1]) == 0)
+ length = 256;
+ if (buflen >= (length + 2))
+ {
+ if (!yapp_upload_data(filefd, filename, filelength, buffer))
+ return;
+ buflen -= length + 2;
+ memcpy(buffer, buffer + length + 2, buflen);
+ used = TRUE;
+ }
+ break;
+ }
+ }
+ while (used);
+ }
+ }
+ }
+}
+
+void cmd_yapp(char *buf, int bytes)
+{
+ int filefd;
+ long size = 0L;
+ char *t;
+
+ if (bytes == 0) return;
+
+ switch (buf[0])
+ {
+ case 'U':
+ case 'u':
+ if ((t = strchr(buf, '\n')) != NULL)
+ *t = '\0';
+ t = buf + 2;
+ while (*t != '\0' && isspace(*t))
+ t++;
+ if (*t == '\0')
+ {
+ printf("\n[YAPP Upload requires a filename - eg ~yu hello.txt]\n");
+ Send_NR("No filename");
+ return;
+ }
+ if ((filefd = open(t, O_RDONLY)) == -1)
+ {
+ printf("\n[Unable to open upload file]\n");
+ Send_NR("Invalid filename");
+ return;
+ }
+ if (lseek(filefd, 0L, SEEK_END) != -1)
+ size = lseek(filefd, 0L, SEEK_CUR);
+ lseek(filefd, 0L, SEEK_SET);
+ if (size != -1)
+ printf("\n[Uploading %ld bytes from %s using YAPP]\n", size, t);
+ else
+ printf("\n[Uploading from %s using YAPP]\n", t);
+ yapp_upload(filefd, t, size);
+ close(filefd);
+ printf("[Finished YAPP Upload, %d bytes Transmitted]\n", total);
+ break;
+
+ case 'D':
+ case 'd':
+ if ((t = strchr(buf, '\n')) != NULL)
+ *t = '\0';
+ t = buf + 2;
+ while (*t != '\0' && isspace(*t))
+ t++;
+ if (*t == '\0') filefd=-1;
+ else if ((filefd = open(t, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1)
+ {
+ printf("\n[Unable to open %s]\n", buf + 2);
+ Send_NR("Invalid filename");
+ return;
+ }
+ printf("\n[Downloading using YAPP]\n");
+ yapp_download(filefd);
+ close(filefd);
+ printf("[Finished YAPP Download, %d bytes received]\n", total);
+ break;
+
+ default:
+ printf("\nUnknown '~y' escape. Type ~h for a list.\n");
+ break;
+ }
+}
+