/* 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. */ /* * Slightly modified for use with MFJTerm by Mats Petersson. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mfjterm.h" #include "yapp.h" #include "dostime.h" #include "misc.h" #include "timefuncs.h" struct termios yapp_settings, yapp_old_settings; 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 */ int paclen = 128; WINDOW *yappwin; static void Write_Status(char *s) { char tmp[80]; wmove(yappwin, 2, 3); sprintf(tmp, "State: %-60s", s); waddstr(yappwin, tmp); wrefresh(yappwin); } static void Send_RR(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x01; write(tnc, buffer, 2); } static void Send_RF(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x02; write(tnc, buffer, 2); } static void Send_RT(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = ACK; write(tnc, buffer, 2); } static void Send_AF(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x03; write(tnc, buffer, 2); } static void Send_AT(void) { char buffer[2]; buffer[0] = ACK; buffer[1] = 0x04; write(tnc, 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(tnc, 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(tnc, buffer, len + 2); } static void Send_SI(void) { char buffer[2]; buffer[0] = ENQ; buffer[1] = 0x01; write(tnc, 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(tnc, 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(tnc, buffer, len + 2); } static void Send_ET(void) { char buffer[2]; buffer[0] = EOT; buffer[1] = 0x01; write(tnc, buffer, 2); } static void Send_DT(int length) { char buffer[2]; if (length > 255) length = 0; buffer[0] = STX; buffer[1] = length; write(tnc, buffer, 2); } static void Send_EF(void) { char buffer[2]; buffer[0] = ETX; buffer[1] = 0x01; write(tnc, 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], tmp[80], tstr[80]; 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) { wmove(yappwin, 1, 3); sprintf(tmp, "Unable to open %s", hfield[0]); waddstr(yappwin, tmp); wrefresh(yappwin); Send_NR("Invalid filename"); return(FALSE); } } wmove(yappwin, 1, 3); strcpy(tstr, yappc ? ctime((time_t *)&file_time) : " "); if(tstr[strlen(tstr) - 1] == '\n') tstr[strlen(tstr) - 1] = 0; sprintf(tmp, "Receiving %s %s %s", hfield[0], hfield[1], tstr); waddstr(yappwin, tmp); wrefresh(yappwin); 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; int c; 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(tnc, &sock_read); timeval.tv_usec = 0; timeval.tv_sec = TIMEOUT; n = select(tnc + 1, &sock_read, NULL, NULL, &timeval); if (n == -1) { if (errno == EAGAIN) continue; wmove(yappwin, 1, 3); 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)) { c = wgetch(yappwin); if((c == 'a') || (c == 'A')) { Send_CN("Cancelled by user"); Write_Status("SndABORT"); return; } } if (FD_ISSET(tnc, &sock_read)) { if ((length = read(tnc, 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); } } if(chk_time(0)) wrefresh(yappwin); } } 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; int c; 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(tnc, &sock_read); if (state == STATE_SD) { FD_SET(tnc, &sock_write); n = select(tnc + 1, &sock_read, &sock_write, NULL, NULL); } else { timeval.tv_usec = 0; timeval.tv_sec = TIMEOUT; n = select(tnc + 1, &sock_read, NULL, NULL, &timeval); } if (n == -1) { if (errno == EAGAIN) continue; 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)) { c = wgetch(yappwin); if((c == 'a') || (c == 'A')) { Write_Status("SndABORT"); Send_CN("Cancelled by user"); return; } } if (FD_ISSET(tnc, &sock_write)) { /* Writable, only STATE_SD */ if (outlen > 0) { if ((n = write(tnc, 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(tnc, &sock_read)) { if ((length = read(tnc, 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); } } if(chk_time(0)) wrefresh(yappwin); } } static void transp_mode(void) { write(tnc, "\003", 1); usleep(500000); write(tnc, "t\r", 2); } static void exit_transp_mode(void) { tcsendbreak(tnc, 0); write(tnc, "k\r", 2); } static void raw_tty(void) { tcgetattr(tnc, &yapp_old_settings); tcgetattr(tnc, &yapp_settings); yapp_settings.c_iflag = 0; yapp_settings.c_lflag = 0; yapp_settings.c_oflag = 0; tcsetattr(tnc, TCSAFLUSH, &yapp_settings); } static void rest_tty(void) { tcsetattr(tnc, TCSAFLUSH, &yapp_old_settings); } void yapp(int mode) { int filefd; long size = 0L; char *modestr[2] = {"Download", "Upload"}, tmp[80]; static char fname[51]; attrset(A_REVERSE); move(19, 1); sprintf(tmp, "YAPP %s filename: ", modestr[mode]); addstr(tmp); edgets(stdscr, fname, 50, FALSE); curs_set(0); move(19, 1); addstr(" "); refresh(); yappwin = newwin(4, 76, 9, 2); keypad(yappwin, TRUE); wbkgd(yappwin, A_REVERSE); werase(yappwin); box(yappwin, 0, 0); wrefresh(yappwin); transp_mode(); raw_tty(); switch (mode) { case YAPP_DL: if(! strlen(fname)) filefd = -1; else if ((filefd = open(fname, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { wmove(yappwin, 1, 3); sprintf(tmp, "Unable to open %s", fname); waddstr(yappwin, tmp); wrefresh(yappwin); Send_NR("Invalid filename"); break; } wmove(yappwin, 1, 3); waddstr(yappwin, "Downloading using YAPP"); wrefresh(yappwin); yapp_download(filefd); close(filefd); wmove(yappwin, 1, 3); sprintf(tmp, "Finished YAPP Download, %d bytes received ", total); waddstr(yappwin, tmp); wrefresh(yappwin); break; case YAPP_UL: if ((filefd = open(fname, O_RDONLY)) == -1) { wmove(yappwin, 1, 3); sprintf(tmp, "Unable to open '%s'", fname); waddstr(yappwin, tmp); wrefresh(yappwin); Send_NR("Invalid filename"); break; } if (lseek(filefd, 0L, SEEK_END) != -1) size = lseek(filefd, 0L, SEEK_CUR); lseek(filefd, 0L, SEEK_SET); wmove(yappwin, 1, 3); if (size != -1) sprintf(tmp, "Uploading %ld bytes from '%s' using YAPP", size, fname); else sprintf(tmp, "Uploading from '%s' using YAPP", fname); waddstr(yappwin, tmp); wrefresh(yappwin); yapp_upload(filefd, fname, size); close(filefd); wmove(yappwin, 1, 3); sprintf(tmp, "Finished YAPP Upload, %d bytes Transmitted ", total); waddstr(yappwin, tmp); wrefresh(yappwin); break; } sleep(2); delwin(yappwin); refresh(); rest_tty(); exit_transp_mode(); curs_set(1); move(tnc_y, tnc_x); old_min = 0xffff; } .