/* 03.04.1995 add binary download "#BIN#-Protocol" Alexander Tietzel */ /* 01.05.1995 talkmode Alexander Tietzel (DG6XA) */ /* 15.07.1995 Pull-Down-Menus Alexander Tietzel (DG6XA) */ /* 17.07.1995 auto7+ newer #BIN#-Protocol Alexander Tietzel(DG6XA) */ /* 18.07.1995 Remote commands Alexander Tietzel (DG6XA) */ /* 19.07.1995 statusline Alexander Tietzel (DG6XA) */ /* 25.07.1995 some bug-fixes Alexander Tietzel (DG6XA) */ /* 14.08.1995 merged with mainstream call.c code Jonathan Naylor (G4KLX) */ /* 01.03.1996 support for different screen sizes, fixed 7plus download (DL1BKE) */ /* 19.08.1996 fixed enter key handling (G4KLX) */ /* 27.08.1996 added Rose support (G4KLX) */ /* 30.11.1996 added the called user in the call windows and set talk mode as default (IW0FBB) */ /* 07.12.1996 updated status line to cope with callsign, bits and status message (VK2KTJ) */ /* 02.02.1997 removed NETROM_PACLEN setting to match Jonathon removing it from kernel (VK2KTJ) */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef TM_IN_SYS_TIME #include #endif #include #include #include #include #include #include #include #ifdef HAVE_NETAX25_AX25_H #include #else #include #endif #ifdef HAVE_NETROM_NETROM_H #include #else #include #endif #ifdef HAVE_NETROSE_ROSE_H #include #else #include #endif #include #include #include #include #include "../pathnames.h" #include "call.h" #include "crc.h" #include "menu.h" #define CTRL_C 0x03 #define MAX_PACKETLEN 512 #define MAX_BUFLEN 2*MAX_PACKETLEN #define MAX_CMPSTRLEN MAX_PACKETLEN #define STD_DWN_DIR "/var/spool/ax25/" #define FLAG_RECONNECT 0x01 #define STATW_BITS 12 #define STATW_STAT 20 static int backoff = -1; static int ax25mode = -1; static int debug = FALSE; static int af_mode = AF_AX25; static int window = 0; static char *port = NULL; int interrupted = FALSE; int paclen = 0; int fd; typedef struct { char file_name[255]; long dwn_cnt; int dwn_file; int file_crc; int calc_crc; struct utimbuf ut; int new_header; } t_gp; typedef struct { WINDOW *ptr; int max_y; int max_x; char string[MAX_BUFLEN]; int bytes; int curs_pos; } t_win; #define TALKMODE 001 /* two windows (outgoing and incoming) with menu */ #define SLAVEMODE 002 /* Menu mode */ #define RAWMODE 004 /* mode used by earlier versions */ WINDOW *win; const char *key_words[] = {"//", "#BIN#", " go_7+. ", " stop_7+. ", "\0" }; #define MAXCMDLEN 10 void convert_cr_lf(char *buf, int len) { while (len--) { if (*buf == '\r') *buf = '\n'; buf++; } } void convert_lf_cr(char *buf, int len) { while (len--) { if (*buf == '\n') *buf = '\r'; buf++; } } void convert_upper_lower(char *buf, int len) { while (len--) { *buf = tolower(*buf); buf++; } } static int nr_convert_call(char *address, struct full_sockaddr_ax25 *addr) { char buffer[100], *call, *alias; FILE *fp; int addrlen; for (call = address; *call != '\0'; call++) *call = toupper(*call); if ((fp = fopen(PROC_NR_NODES_FILE, "r")) == NULL) { fprintf(stderr, "call: NET/ROM not included in the kernel\n"); return -1; } fgets(buffer, 100, fp); while (fgets(buffer, 100, fp) != NULL) { call = strtok(buffer, " \t\n\r"); alias = strtok(NULL, " \t\n\r"); if (strcmp(address, call) == 0 || strcmp(address, alias) == 0) { addrlen = ax25_aton(call, addr); fclose(fp); return (addrlen == -1) ? -1 : sizeof(struct sockaddr_ax25); } } fclose(fp); fprintf(stderr, "call: NET/ROM callsign or alias not found\n"); return -1; } static int connect_to(char *address[]) { int fd = 0; int addrlen = 0; union { struct full_sockaddr_ax25 ax25; struct sockaddr_rose rose; } sockaddr; char *digi; int one = debug; switch (af_mode) { case AF_ROSE: if (address[0] == NULL || address[1] == NULL) { fprintf(stderr, "call: too few arguments for Rose\n"); return (-1); } if ((fd = socket(AF_ROSE, SOCK_SEQPACKET, 0)) < 0) { perror("socket"); return (-1); } break; case AF_NETROM: if (address[0] == NULL) { fprintf(stderr, "call: too few arguments for NET/ROM\n"); return (-1); } if ((fd = socket(AF_NETROM, SOCK_SEQPACKET, 0)) < 0) { perror("socket"); return (-1); } ax25_aton(nr_config_get_addr(port), &sockaddr.ax25); sockaddr.ax25.fsa_ax25.sax25_family = AF_NETROM; addrlen = sizeof(struct full_sockaddr_ax25); break; case AF_AX25: if (address[0] == NULL) { fprintf(stderr, "call: too few arguments for AX.25\n"); return (-1); } if ((fd = socket(AF_AX25, SOCK_SEQPACKET, 0)) < 0) { perror("socket"); return (-1); } ax25_aton(ax25_config_get_addr(port), &sockaddr.ax25); if (sockaddr.ax25.fsa_ax25.sax25_ndigis == 0) { ax25_aton_entry(ax25_config_get_addr(port), sockaddr.ax25.fsa_digipeater[0].ax25_call); sockaddr.ax25.fsa_ax25.sax25_ndigis = 1; } sockaddr.ax25.fsa_ax25.sax25_family = AF_AX25; addrlen = sizeof(struct full_sockaddr_ax25); if (setsockopt(fd, SOL_AX25, AX25_WINDOW, &window, sizeof(window)) == -1) { perror("AX25_WINDOW"); close(fd); return (-1); } if (setsockopt(fd, SOL_AX25, AX25_PACLEN, &paclen, sizeof(paclen)) == -1) { perror("AX25_PACLEN"); close(fd); return (-1); } if (backoff != -1) { if (setsockopt(fd, SOL_AX25, AX25_BACKOFF, &backoff, sizeof(backoff)) == -1) { perror("AX25_BACKOFF"); close(fd); return (-1); } } if (ax25mode != -1) { if (setsockopt(fd, SOL_AX25, AX25_EXTSEQ, &ax25mode, sizeof(ax25mode)) == -1) { perror("AX25_EXTSEQ"); close(fd); return (-1); } } break; } if (debug && setsockopt(fd, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) == -1) { perror("SO_DEBUG"); close(fd); return (-1); } if (af_mode != AF_ROSE) { /* Let Rose autobind */ if (bind(fd, (struct sockaddr *) &sockaddr, addrlen) == -1) { perror("bind"); close(fd); return (-1); } } switch (af_mode) { case AF_ROSE: memset(&sockaddr.rose, 0x00, sizeof(struct sockaddr_rose)); if (ax25_aton_entry(address[0], sockaddr.rose.srose_call.ax25_call) == -1) { close(fd); return (-1); } if (rose_aton(address[1], sockaddr.rose.srose_addr.rose_addr) == -1) { close(fd); return (-1); } if (address[2] != NULL) { digi = address[2]; if (strcasecmp(address[2], "VIA") == 0) { if (address[3] == NULL) { fprintf(stderr, "call: callsign must follow 'via'\n"); close(fd); return (-1); } digi = address[3]; } if (ax25_aton_entry(digi, sockaddr.rose.srose_digi.ax25_call) == -1) { close(fd); return (-1); } sockaddr.rose.srose_ndigis = 1; } sockaddr.rose.srose_family = AF_ROSE; addrlen = sizeof(struct sockaddr_rose); break; case AF_NETROM: if (nr_convert_call(address[0], &sockaddr.ax25) == -1) { close(fd); return (-1); } sockaddr.rose.srose_family = AF_NETROM; addrlen = sizeof(struct sockaddr_ax25); break; case AF_AX25: if (ax25_aton_arglist((const char**)address, &sockaddr.ax25) == -1) { close(fd); return (-1); } sockaddr.rose.srose_family = AF_AX25; addrlen = sizeof(struct full_sockaddr_ax25); break; } printf("Trying...\r"); fflush(stdout); if (connect(fd, (struct sockaddr *) &sockaddr, addrlen)) { printf("\n"); perror("connect"); close(fd); return (-1); } printf("*** Connected to %s\n", address[0]); return (fd); } void cmd_intr(int sig) { signal(SIGQUIT, cmd_intr); interrupted = TRUE; } void statline(int mode, char *s) { static int oldlen = 0; int l, cnt; if (*s == '\0') { if (mode == RAWMODE) return; if (oldlen > 0) { move(0, STATW_STAT); attron(A_REVERSE); for (cnt = 0; cnt < oldlen; cnt++) addch(' '); oldlen = 0; attroff(A_REVERSE); refresh(); } return; } if (mode == RAWMODE) { printf(">>%s\n", s); return; } if (strlen(s) > 80 - STATW_STAT) s[80 - STATW_STAT] = '\0'; move(0, STATW_STAT); attron(A_REVERSE); addstr(s); if (oldlen > strlen(s)) { l = oldlen - strlen(s); for (cnt = 0; cnt < l; cnt++) addch(' '); } attroff(A_REVERSE); oldlen = strlen(s); refresh(); } WINDOW * opnstatw(int mode, wint * wintab, char *s, int lines, int cols) { WINDOW *win; if (mode == RAWMODE) { printf(">>%s\n", s); return NULL; } win = winopen(wintab, lines, cols, ((LINES - 1) - lines) / 2, ((COLS) - cols) / 2, TRUE); mvwaddstr(win, 1, 1 + (cols - strlen(s)) / 2, s); wmove(win, 3, 2); return win; } void wrdstatw(WINDOW * win, char s[]) { int y, x; if (win == NULL) { printf(" %s\n", s); return; } waddstr(win, s); getyx(win, y, x); wmove(win, y + 1, 2); wrefresh(win); } void dupdstatw(WINDOW * win, char *s, int add) { static char infostr[80]; static int y, x; static int oldlen; int l, cnt; if (add) { oldlen = 0; strcpy(infostr, s); if (win == NULL) { printf(" %s", s); return; } waddstr(win, s); getyx(win, y, x); wrefresh(win); return; } if (win == NULL) { printf("\r %s%s", infostr, s); } else { mvwaddstr(win, y, x, s); } if (oldlen > strlen(s)) { l = oldlen - strlen(s); for (cnt = 0; cnt < l; cnt++) if (win == NULL) printf(" "); else waddch(win, ' '); } if (win == NULL) { fflush(stdout); } else { wrefresh(win); } oldlen = strlen(s); } int start_ab_download(int mode, WINDOW ** swin, wint * wintab, char parms[], int parmsbytes, char buf[], int bytes, t_gp * gp, char *address[]) { int crcst; /* startposition crc-field */ int datest = 0; /* startposition date-field */ int namest = 0; /* startposition name-field */ int cnt; int date = 0; struct tm ft; char s[80]; for (crcst = 2; (!(parms[crcst - 2] == '#' && parms[crcst - 1] == '|') && crcst < parmsbytes - 1); crcst++); if (crcst < parmsbytes - 1) { gp->file_crc = atoi(parms + crcst); for (datest = crcst; (!(parms[datest - 2] == '#' && parms[datest - 1] == '$')); datest++); date = (int) strtol(parms + datest, NULL, 16); ft.tm_sec = (date & 0x1F) * 2; date >>= 5; ft.tm_min = date & 0x3F; date >>= 6; ft.tm_hour = date & 0x1F; date >>= 5; ft.tm_mday = date & 0x1F; date >>= 5; ft.tm_mon = date & 0x0F; date >>= 4; ft.tm_year = (date & 0x7F) + 70; ft.tm_isdst = 0; ft.tm_yday = 0; ft.tm_wday = 0; gp->ut.actime = mktime(&ft); gp->ut.modtime = gp->ut.actime; for (namest = datest; (parms[namest - 1] != '#' && namest < parmsbytes - 1); namest++); } else { gp->ut.actime = 0; gp->ut.modtime = 0; } gp->dwn_cnt = atol(parms); strcpy(gp->file_name, STD_DWN_DIR); if (crcst == parmsbytes - 1 || datest - crcst > 7 || namest - datest > 10) { *swin = opnstatw(mode, wintab, "Remote starts AutoBin transfer", 6, 52); gp->new_header = FALSE; wrdstatw(*swin, "old styled Header (no filename)"); strcat(gp->file_name, address[0]); strcat(gp->file_name, ".dwnfile"); } else { *swin = opnstatw(mode, wintab, "Remote starts AutoBin transfer", 10, 52); gp->new_header = TRUE; for (cnt = parmsbytes - namest; !(parms[cnt + namest - 1] == '\\' || parms[cnt + namest - 1] == '/') && cnt > 0; cnt--); strncpy(s, &parms[namest + cnt], parmsbytes - namest - cnt); convert_upper_lower(s, parmsbytes - namest - cnt); strncat(gp->file_name, s, parmsbytes - namest - cnt); gp->file_name[strlen(gp->file_name) + parmsbytes - namest - cnt - 1] = 0; sprintf(s, "size of file : %u", (unsigned int) gp->dwn_cnt); wrdstatw(*swin, s); sprintf(s, "filename : %s", gp->file_name); wrdstatw(*swin, s); sprintf(s, "last mod. date : %02i.%02i.%04i", ft.tm_mday, ft.tm_mon, ft.tm_year + 1900); wrdstatw(*swin, s); sprintf(s, "last mod. time : %02i:%02i:%02i", ft.tm_hour, ft.tm_min, ft.tm_sec); wrdstatw(*swin, s); } dupdstatw(*swin, "Bytes to receive: ", TRUE); if ((gp->dwn_file = open(gp->file_name, O_RDWR | O_CREAT, 0666)) == -1) { sprintf(s, "Unable to open %s", gp->file_name); statline(mode, s); if (write(fd, "#ABORT#\r", 8) == -1) { perror("write"); gp->dwn_cnt = 0; gp->file_name[0] = '\0'; return -1; } } if (bytes == 1) { if (write(fd, "#OK#\r", 5) == -1) { perror("write"); gp->dwn_cnt = 0; gp->file_name[0] = '\0'; return -1; } gp->calc_crc = 0; } else { write(gp->dwn_file, buf, bytes); gp->calc_crc = calc_crc(buf, bytes, 0); gp->dwn_cnt -= bytes; } return 0; } int ab_down(int mode, WINDOW * swin, wint * wintab, char buf[], int *bytes, t_gp * gp) { int extrach = 0; char s[80]; if (strncmp(buf, "#ABORT#\r", 8) == 0 && *bytes == 8) { gp->dwn_cnt = 0; close(gp->dwn_file); statline(mode, "Remote aborts AutoBin transfer!"); *bytes = 0; if (mode != RAWMODE) { delwin(swin); winclose(wintab); } else { printf("\n"); } return 0; } if (gp->dwn_cnt < *bytes) { extrach = *bytes - gp->dwn_cnt; *bytes = gp->dwn_cnt; } if (write(gp->dwn_file, buf, *bytes) != *bytes) { close(gp->dwn_file); gp->dwn_cnt = 0; statline(mode, "Error while writing download file. Download aborted."); if (mode != RAWMODE) { delwin(swin); winclose(wintab); } else { printf("\n"); } } else { gp->calc_crc = calc_crc(buf, *bytes, gp->calc_crc); gp->dwn_cnt -= *bytes; if (gp->dwn_cnt == 0) { if (mode != RAWMODE) { delwin(swin); winclose(wintab); } else printf("\n"); strcpy(s, "AutoBin download finished "); if (gp->new_header) if (gp->calc_crc == gp->file_crc) strcat(s, "CRC check ok"); else { strcat(s, "CRC check failed!"); } else { sprintf(s + strlen(s), "CRC=%u", gp->calc_crc); } statline(mode, s); close(gp->dwn_file); utime(gp->file_name, &gp->ut); if (extrach != 0) { memmove(buf, buf + *bytes, extrach); *bytes = extrach; } else *bytes = 0; } else { sprintf(s, "%u", (unsigned int) gp->dwn_cnt); dupdstatw(swin, s, FALSE); *bytes = 0; } } return 0; } int start_screen(char *call[]) { int cnt; char idString[11]; sprintf(idString, " %8s ", call[0]); if ((win = initscr()) == NULL) return -1; attron(A_REVERSE); move(0, 0); addstr(idString); addch(ACS_VLINE); addstr("--------"); addch(ACS_VLINE); for (cnt = STATW_STAT; cnt <= 80; cnt++) addch(' '); attroff(A_REVERSE); noecho(); raw(); nodelay(win, TRUE); keypad(win, TRUE); refresh(); return 0; } int start_slave_mode(wint * wintab, t_win * win_in, t_win * win_out) { win_in->max_y = LINES - 2; win_in->max_x = COLS; win_in->ptr = winopen(wintab, win_in->max_y + 1, win_in->max_x, 1, 0, FALSE); win_out->ptr = win_in->ptr; scrollok(win_in->ptr, TRUE); wclear(win_out->ptr); wrefresh(win_out->ptr); win_out->bytes = 0; win_out->curs_pos = 0; win_in->bytes = 0; win_in->curs_pos = 0; return 0; } int start_talk_mode(wint * wintab, t_win * win_in, t_win * win_out) { int cnt; WINDOW *win; win_out->max_y = 4; /* TXLINES */ win_out->max_x = COLS; win_in->max_y = (LINES - 4) - win_out->max_y; win_in->max_x = COLS; win_out->ptr = winopen(wintab, win_out->max_y + 1, win_out->max_x, (win_in->max_y + 3), 0, FALSE); win_in->ptr = winopen(wintab, win_in->max_y + 1, win_in->max_x, 1, 0, FALSE); win = winopen(wintab, 1, win_out->max_x, win_in->max_y + 2, 0, FALSE); for (cnt = 0; cnt < COLS; cnt++) waddch(win, '-'); wrefresh(win); scrollok(win_in->ptr, TRUE); scrollok(win_out->ptr, TRUE); wclear(win_out->ptr); wrefresh(win_out->ptr); wclear(win_in->ptr); wrefresh(win_in->ptr); win_out->bytes = 0; win_out->curs_pos = 0; win_in->bytes = 0; win_out->curs_pos = 0; return 0; } int change_mode(int oldmode, int newmode, wint * wintab, t_win * win_in, t_win * win_out, char *call[]) { switch (oldmode) { case RAWMODE: if (newmode == TALKMODE) { start_screen(call); start_talk_mode(wintab, win_in, win_out); } if (newmode == SLAVEMODE) { start_screen(call); start_slave_mode(wintab, win_in, win_out); } break; case TALKMODE: if (newmode == RAWMODE) { wclear(win_out->ptr); wrefresh(win_out->ptr); wclear(win_in->ptr); wrefresh(win_in->ptr); wintab->next = 0; endwin(); } if (newmode == SLAVEMODE) { delwin(win_out->ptr); delwin(win_in->ptr); wintab->next = 0; start_slave_mode(wintab, win_in, win_out); } break; case SLAVEMODE: if (newmode == RAWMODE) { wclear(win_out->ptr); wrefresh(win_out->ptr); wintab->next = 0; endwin(); } if (newmode == TALKMODE) { delwin(win_out->ptr); wintab->next = 0; start_talk_mode(wintab, win_in, win_out); } break; } return newmode; } void writeincom(int mode, t_win * win_in, unsigned char buf[], int bytes) { int cnt; if (mode & RAWMODE) { write(STDOUT_FILENO, buf, bytes); return; } for (cnt = 0; cnt < bytes; cnt++) { switch (buf[cnt]) { case 201: case 218: waddch(win_in->ptr, ACS_ULCORNER); break; case 187: case 191: waddch(win_in->ptr, ACS_URCORNER); break; case 200: case 192: waddch(win_in->ptr, ACS_LLCORNER); break; case 188: case 217: waddch(win_in->ptr, ACS_LRCORNER); break; case 204: case 195: waddch(win_in->ptr, ACS_LTEE); break; case 185: case 180: waddch(win_in->ptr, ACS_RTEE); break; case 203: case 194: waddch(win_in->ptr, ACS_TTEE); break; case 202: case 193: waddch(win_in->ptr, ACS_BTEE); break; case 205: case 196: waddch(win_in->ptr, ACS_HLINE); break; case 186: case 179: waddch(win_in->ptr, ACS_VLINE); break; case 129: waddch(win_in->ptr, 252); /*u umlaut */ break; case 132: waddch(win_in->ptr, 228); /*a umlaut */ break; case 142: waddch(win_in->ptr, 196); /*A umlaut */ break; case 148: waddch(win_in->ptr, 246); /*o umlaut */ break; case 153: waddch(win_in->ptr, 214); /*O umlaut */ break; case 154: waddch(win_in->ptr, 220); /*U umlaut */ break; case 225: waddch(win_in->ptr, 223); /*sz */ break; default: { if (buf[cnt] > 127) waddch(win_in->ptr, '.'); else waddch(win_in->ptr, buf[cnt]); } } } /* waddnstr(win_in->ptr, buf, bytes); */ wrefresh(win_in->ptr); return; } int getstring(wint * wintab, char text[], char buf[]) { int c; int ypos = 0, xpos = 0; int bytes = 0; WINDOW *win = winopen(wintab, 3, COLS, 10, 0, TRUE); wmove(win, 1, 2); waddstr(win, text); wrefresh(win); do { c = getch(); if (c != ERR) { switch (c) { case KEY_BACKSPACE: case 127: { getyx(win, ypos, xpos); if (xpos > 0 && bytes > 0) { wmove(win, ypos, --xpos); waddch(win, ' '); wmove(win, ypos, xpos); bytes--; } } break; case (int) '\n': case (int) '\r': case KEY_ENTER: { waddch(win, '\n'); buf[bytes++] = (char) '\n'; wrefresh(win); buf[bytes] = 0; } break; default: { waddch(win, (char) c); buf[bytes++] = (char) c; } } wrefresh(win); } } while (c != '\n' && c != '\r' && c != KEY_ENTER); delwin(win); winclose(wintab); return 0; } int readoutg(t_win * win_out, wint * wintab, menuitem * top, char buf[], int keyesc) { int out_cnt; int c; int ypos = 0, xpos = 0; int value; c = getch(); if (c == ERR) return 0; if (c == keyesc) { if ((value = top_menu(wintab, top, 1)) == 0) return 0; buf[0] = '~'; switch (value) { case 0x01: { buf[1] = 'r'; return 2; } case 0x02: { buf[1] = '.'; return 2; } case 0x11: { buf[1] = 'o'; getstring(wintab, "Please enter filename: ", &buf[2]); return strlen(buf); } case 0x12: { buf[1] = 'c'; return 2; } case 0x13: case 0x14: case 0x15: { switch (value) { case 0x13: buf[1] = 'u'; break; case 0x14: buf[1] = 'b'; break; case 0x15: buf[1] = 'a'; } getstring(wintab, "Please enter filename: ", buf + 2); return strlen(buf); } case 0x21: { buf[1] = '1'; return 2; } case 0x22: { buf[1] = '2'; return 2; } case 0x23: { buf[1] = '0'; return 2; } case 0x31: return -1; } wrefresh(win_out->ptr); return 2; } switch (c) { case KEY_BACKSPACE: case 127: { getyx(win_out->ptr, ypos, xpos); if (win_out->bytes > 0) { if (win_out->curs_pos < win_out->bytes) { mvwaddnstr(win_out->ptr, ypos, --xpos, &win_out->string[win_out->curs_pos], win_out->bytes - win_out->curs_pos); waddch(win_out->ptr, ' '); memmove(&win_out->string[win_out->curs_pos - 1], &win_out->string[win_out->curs_pos], win_out->bytes - win_out->curs_pos); } else mvwaddch(win_out->ptr, ypos, --xpos, ' '); wmove(win_out->ptr, ypos, xpos); win_out->bytes--; win_out->curs_pos--; } } break; case KEY_LEFT: if (win_out->curs_pos > 0) { win_out->curs_pos--; getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos - 1); } break; case KEY_RIGHT: if (win_out->curs_pos < win_out->bytes) { win_out->curs_pos++; getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos + 1); } break; case KEY_ENTER: case (int) '\n': case (int) '\r': { if (win_out->curs_pos < win_out->bytes) { getyx(win_out->ptr, ypos, xpos); wmove(win_out->ptr, ypos, xpos + win_out->bytes - win_out->curs_pos); } waddch(win_out->ptr, '\n'); win_out->string[win_out->bytes++] = (char) '\n'; wrefresh(win_out->ptr); strncpy(buf, win_out->string, win_out->bytes); wrefresh(win_out->ptr); out_cnt = win_out->bytes; win_out->bytes = 0; win_out->curs_pos = 0; return out_cnt; } break; default: { waddch(win_out->ptr, (char) c); if (win_out->curs_pos < win_out->bytes) { getyx(win_out->ptr, ypos, xpos); waddnstr(win_out->ptr, &win_out->string[win_out->curs_pos], win_out->bytes - win_out->curs_pos); memmove(&win_out->string[win_out->curs_pos + 1], &win_out->string[win_out->curs_pos], win_out->bytes - win_out->curs_pos); win_out->string[win_out->curs_pos] = (char) c; wmove(win_out->ptr, ypos, xpos); } else win_out->string[win_out->bytes] = (char) c; win_out->bytes++; win_out->curs_pos++; } } wrefresh(win_out->ptr); return 0; } void writemsg(char fname[], char caller[]) { char text_row[255]; char *text_ptr; char buf[255]; FILE *f = fopen(fname, "r"); if (f == NULL) { perror(fname); return; } do { if (fgets(text_row, 255, f) != 0) { text_row[strlen(text_row) - 1] = '\r'; text_ptr = strchr(text_row, '$'); if (text_ptr != NULL) { strcpy(buf, text_ptr + 2); switch (*(text_ptr + 1)) { case 'c': { strcpy(text_ptr, caller); strcat(text_ptr, buf); } } } write(fd, text_row, strlen(text_row)); } } while (!feof(f)); } void remotecommand(char buf[], int bytes) { int firstchar; if (bytes == 0) return; switch (buf[0]) { case 'e': case 'E': { for (firstchar = 0; buf[firstchar] != ' '; firstchar++); firstchar++; buf[bytes] = '\n'; convert_lf_cr(buf + firstchar, bytes - firstchar + 1); write(fd, buf + firstchar, bytes - firstchar + 1); } break; default: write(fd, "Unknown command\r", 16); } } int compstr(const char st1[], char st2[], int maxbytes) { int cnt; for (cnt = 0; st1[cnt] == st2[cnt] && cnt + 1 < maxbytes && st1[cnt + 1] != 0; cnt++); if (st1[cnt] != st2[cnt]) return -1; if (st1[cnt + 1] == 0) return 0; if (cnt == maxbytes - 1) return -2; return -1; } int eol(char c) { if (c == '\r' || c == '\n' || c == 0x1A) return TRUE; else return FALSE; } int searche_key_words(char buf[], int *bytes, char *parms, int *parmsbytes, char restbuf[], int *restbytes) { static char cmpstr[MAX_CMPSTRLEN]; static int cmpstrbyte = 0; static int command = -1; int cmdstpos = 0; int cnt = 0; int t = 0; if (cmpstrbyte != 0) { memmove(buf + cmpstrbyte, buf, *bytes); *bytes += cmpstrbyte; strncpy(buf, cmpstr, cmpstrbyte); cmpstrbyte = 0; for (cnt = 0; !eol(buf[cnt]) && cnt < *bytes - 1; cnt++); if (cnt == *bytes - 1 && !eol(buf[cnt])) { printf("Problem!!!\n"); command = -1; *restbytes = 0; *parmsbytes = 0; return -1; } } if (command == -1) { cnt = 0; do { command = 0; cmdstpos = cnt; do { if ((t = compstr(key_words[command], &buf[cnt], *bytes - cnt)) != -1) break; } while (*key_words[++command] != '\0'); for (; !eol(buf[cnt]) && cnt < *bytes - 1; cnt++); if (cnt < *bytes - 1) cnt++; else break; } while (t == -1); if (t < 0) command = -1; } if (t == -2 || (command != -1 && cnt == *bytes - 1 && !eol(buf[*bytes - 1]))) { cmpstrbyte = *bytes - cmdstpos; strncpy(cmpstr, &buf[cmdstpos], cmpstrbyte); cmpstr[cmpstrbyte] = 0; *bytes -= cmpstrbyte; *restbytes = 0; return -1; } if (t == -1) { command = -1; cmpstrbyte = 0; *restbytes = 0; return -1; } t = cmdstpos + strlen(key_words[command]); *restbytes = *bytes - cnt; strncpy(parms, &buf[t], cnt - t); *parmsbytes = cnt - t; strncpy(restbuf, buf + cnt, *restbytes); *bytes = cmdstpos; t = command; command = -1; return t; } int sevenplname(int mode, WINDOW ** swin, wint * wintab, int *f, int* logfile, char parms[], int parmsbytes, char buf[], int bytes) { int cnt; int part; int nrparts; int lines; char orgn[13]; char prtn[13]; char strn[255]; char v[20]; char s[80]; if (parmsbytes >= 40) if (strcmp(" of ", &parms[3]) == 0 || parmsbytes < 41 || parms[10] != ' ' || parms[23] != ' ' || parms[31] != ' ' || parms[36] != ' ' || parms[40] != ' ') { return -1; } part = atof(parms); lines = (int) strtol(parms + 37, NULL, 16); nrparts = (int) strtol(parms + 7, NULL, 10); strncpy(orgn, &parms[11], 12); convert_upper_lower(orgn, 12); for (cnt = 11; orgn[cnt] == ' '; cnt--); orgn[cnt + 1] = 0; if (orgn[cnt - 3] == '.') { strncpy(prtn, orgn, cnt - 2); if (nrparts == 1) sprintf(prtn + cnt - 2, "7pl"); else sprintf(prtn + cnt - 2, "p%02x", part); } else { strcpy(prtn, orgn); if (nrparts == 1) sprintf(prtn + cnt, ".7pl"); else sprintf(prtn + cnt, ".p%02x", part); } strcpy(strn, STD_DWN_DIR); strcat(strn, prtn); for (cnt = 0; parms[cnt + 41] != ')' && cnt + 41 != parmsbytes; cnt++); if (parms[cnt + 41] != ')') { return -1; } strncpy(v, &parms[41], cnt + 1); v[cnt + 1] = 0; *swin = opnstatw(mode, wintab, "Remote starts 7+ Download", 11, 55); sprintf(s, "7plus version : %s", v); wrdstatw(*swin, s); sprintf(s, "Name of decoded file : %s", orgn); wrdstatw(*swin, s); sprintf(s, "Storagename : %s", strn); wrdstatw(*swin, s); sprintf(s, "Parts : %i", nrparts); wrdstatw(*swin, s); sprintf(s, "Number of this Part : %i", part); wrdstatw(*swin, s); sprintf(s, "Lines : %i", lines); wrdstatw(*swin, s); dupdstatw(*swin, "Outstanding lines : ", TRUE); if (*f != -1) { close(*f); } if ((*f = open(strn, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { sprintf(s, "Unable to open %s", strn); statline(mode, s); } else if (*logfile != -1) { sprintf(s, "*** 7plus download into file: %s ***\n", strn); write(*logfile, s, strlen(s)); } write(*f, key_words[2], strlen(key_words[2])); convert_cr_lf(parms, parmsbytes); write(*f, parms, parmsbytes); return lines; } void statbits(int mode, char stat, int m) { if (mode == RAWMODE) return; move(0, STATW_BITS + m); attron(A_REVERSE); addch(stat); attroff(A_REVERSE); refresh(); return; } int cmd_call(char *call[], int mode) { menuitem con[] = { {"~Reconnect", 'R', M_ITEM, (void *) 0x01}, {"~Exit", 'E', M_ITEM, (void *) 0x02}, {"\0", 0, M_END, 0} }; menuitem fil[] = { {"~Open Logfile", 'O', M_ITEM, 0}, {"~Close Logfile", 'C', M_ITEM, 0}, {"Send ~Textfile", 'T', M_ITEM, 0}, {"Send ~Binary", 'B', M_ITEM, 0}, {"Send ~AutoBin", 'A', M_ITEM, 0}, {"\0", 0, M_END, 0} }; menuitem mod[] = { {"~Slavemode", 'S', M_ITEM, 0}, {"~Talkmode", 'T', M_ITEM, 0}, {"~Rawmode", 'R', M_ITEM, 0}, {"\0", 0, M_END, 0} }; menuitem win[] = { {"~Clear", 'C', M_ITEM, 0}, {"~Resize", 'R', M_ITEM, 0}, {"\0", 0, M_END, 0} }; menuitem top[] = { {"~Connect", 'C', M_P_DWN, con}, {"~File", 'F', M_P_DWN, fil}, {"~Mode", 'M', M_P_DWN, mod}, {"~Window", 'W', M_P_DWN, win}, {"\0", 0, M_END, 0} }; wint wintab; fd_set sock_read; fd_set sock_write; char buf[MAX_BUFLEN]; char restbuf[MAX_PACKETLEN]; char parms[256]; int sevenplus = FALSE; int sevenplcnt = 0; int bytes; int restbytes; int parmsbytes; int com_num; int logfile = -1; int uploadfile = -1; int downloadfile = -1; int binup = FALSE; long uplsize = 0; long uplpos = 0; char uplbuf[128]; /* Upload buffer */ int upldp = 0; int upllen = 0; char *c, *t; int extrach = 0; t_gp gp; t_win win_in; t_win win_out; WINDOW *swin = 0; int cnt; int crc = 0; char s[80]; int flags = 0; init_crc(); gp.dwn_cnt = 0; wintab.next = 0; if ((fd = connect_to(call)) == -1) return FALSE; interrupted = FALSE; signal(SIGQUIT, cmd_intr); signal(SIGINT, SIG_IGN); signal(SIGTSTP, SIG_IGN); fcntl(fd, F_SETFL, O_NONBLOCK); fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); if (mode != RAWMODE) start_screen(call); switch (mode) { case TALKMODE: start_talk_mode(&wintab, &win_in, &win_out); break; case SLAVEMODE: start_slave_mode(&wintab, &win_in, &win_out); break; case RAWMODE: printf("Rawmode\n"); } while (TRUE) { FD_ZERO(&sock_read); FD_SET(STDIN_FILENO, &sock_read); FD_SET(fd, &sock_read); FD_ZERO(&sock_write); if (uploadfile != -1) FD_SET(fd, &sock_write); if (select(fd + 1, &sock_read, &sock_write, NULL, NULL) == -1) { if (!interrupted && errno == EAGAIN) continue; if (!interrupted) perror("select"); break; } if (FD_ISSET(fd, &sock_read)) { bytes = read(fd, buf, 511); if (bytes == -1 && errno != EWOULDBLOCK && errno != EAGAIN) { if (errno != ENOTCONN) perror("read"); break; } if (gp.dwn_cnt != 0) { ab_down(mode, swin, &wintab, buf, &bytes, &gp); if (bytes == 0) continue; } do { com_num = searche_key_words(buf, &bytes, parms, &parmsbytes, restbuf, &restbytes); if (bytes != 0) { convert_cr_lf(buf, bytes); if (!sevenplus) { writeincom(mode, &win_in, buf, bytes); } else { for (cnt = 0; cnt < bytes; cnt++) if (eol(buf[cnt])) sevenplcnt--; dupdstatw(swin, s, FALSE); } if (downloadfile != -1) { if (write(downloadfile, buf, bytes) != bytes) { close(downloadfile); downloadfile = -1; statline(mode, "Error while writing file. Downloadfile closed."); } } else if (logfile != -1) { if (write(logfile, buf, bytes) != bytes) { close(logfile); logfile = -1; statline(mode, "Error while writing log. Log closed."); } } } switch (com_num) { case 0: { #if 0 /* FIXME! We should, no: WE MUST be able to turn off all remote commands to avoid mail bombs generating offensive mails with //e while sucking the BBS */ remotecommand(parms, parmsbytes); #endif } break; case 1: { start_ab_download(mode, &swin, &wintab, parms, parmsbytes, restbuf, restbytes, &gp, call); restbytes = 0; extrach = 0; } break; case 2: { if ((sevenplcnt = sevenplname(mode, &swin, &wintab, &downloadfile, &logfile, parms, parmsbytes, buf, bytes)) != -1) sevenplus = TRUE; } break; case 3: { if (!sevenplus) break; write(downloadfile, key_words[3], strlen(key_words[3])); convert_cr_lf(parms, parmsbytes); write(downloadfile, parms, parmsbytes); if (mode != RAWMODE) { delwin(swin); winclose(&wintab); } else printf("\n"); statline(mode, "7+ Download finished."); sevenplus = FALSE; close(downloadfile); downloadfile = -1; } break; } strncpy(buf, restbuf, restbytes); bytes = restbytes; } while (restbytes != 0); } if (FD_ISSET(STDIN_FILENO, &sock_read)) { if ((mode & RAWMODE) == RAWMODE) bytes = read(STDIN_FILENO, buf, 511); else { bytes = readoutg(&win_out, &wintab, top, buf, 0x1d); if (bytes == -1) { wclear(win_in.ptr); wrefresh(win_in.ptr); wclear(win_out.ptr); wrefresh(win_out.ptr); bytes = 0; } } if (bytes > 0) statline(mode, ""); if (bytes > 1 && *buf == '~') { buf[bytes] = 0; switch (buf[1]) { case '.': { bytes = 0; interrupted = TRUE; } break; case '!': change_mode(mode, RAWMODE, &wintab, &win_in, &win_out, call); if (buf[2] != '\0' && buf[2] != '\n') { c = buf + 2; if ((t = strchr(c, '\n')) != NULL) *t = '\0'; } else { if ((c = getenv("SHELL")) == NULL) c = "/bin/sh"; } fcntl(STDIN_FILENO, F_SETFL, 0); printf("\n[Spawning subshell]\n"); system(c); printf("\n[Returned to connect]\n"); fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); change_mode(RAWMODE, mode, &wintab, &win_in, &win_out, call); continue; case 'z': case 'Z': case 'Z' - 64: fcntl(STDIN_FILENO, F_SETFL, 0); kill(getpid(), SIGSTOP); statline(mode, "Resumed"); fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); continue; case '?': case 'h': case 'H': printf("\n\rTilde escapes:\n\r. close\n\r! shell\n\rZ suspend\n\rs Stop upload\n\ro Open log\n\rc Close log\n\ru Upload\n\ryd YAPP Download\n\ryu YAPP Upload\n\r"); continue; case 'S': case 's': if (uploadfile != -1) { statline(mode, "Upload file closed"); close(uploadfile); uploadfile = -1; } else { statline(mode, "No upload in progress"); } continue; case 'A': case 'a': case 'b': case 'B': case 'u': case 'U': if (uploadfile != -1) { statline(mode, "Already uploading"); continue; } if ((t = strchr(buf, '\n')) != NULL) *t = '\0'; t = buf + 2; while (*t != '\0' && isspace(*t)) t++; if (*t == '\0') { statline(mode, "Upload requires a filename"); continue; } uploadfile = open(t, O_RDONLY); if (uploadfile == -1) { statline(mode, "Unable to open upload file"); continue; } if (lseek(uploadfile, 0L, SEEK_END) != -1) uplsize = lseek(uploadfile, 0L, SEEK_CUR); else uplsize = 0; lseek(uploadfile, 0L, SEEK_SET); uplpos = 0; upldp = -1; upllen = 0; if (uplsize != -1) { sprintf(s, "Uploading %ld bytes from %s", uplsize, t); swin = opnstatw(mode, &wintab, s, 6, 50); dupdstatw(swin, "bytes sent : ", TRUE); } else { sprintf(s, "Uploading from %s", t); swin = opnstatw(mode, &wintab, s, 6, 50); dupdstatw(swin, "bytes sent : ", TRUE); } switch (buf[1]) { case 'a': case 'A': { binup = TRUE; crc = 0; do { upllen = read(uploadfile, uplbuf, 128); if (upllen == -1) { close(uploadfile); uploadfile = -1; delwin(swin); winclose(&wintab); sprintf(s, "Error reading upload file: upload aborted"); statline(mode, s); break; } crc = calc_crc(uplbuf, upllen, crc); } while (upllen > 0); lseek(uploadfile, 0L, SEEK_SET); sprintf(s, "#BIN#%ld#$%u#|000000#%s\r", uplsize, crc, t); write(fd, s, strlen(s)); uplpos = 0; upldp = -1; upllen = 0; } break; case 'b': case 'B': binup = TRUE; break; case 'u': case 'U': binup = FALSE; } continue; case 'O': case 'o': if ((t = strchr(buf, '\n')) != NULL) *t = '\0'; if (logfile != -1) { close(logfile); logfile = -1; } if (downloadfile != -1) { close(downloadfile); downloadfile = -1; } t = buf + 2; while (*t != '\0' && isspace(*t)) t++; if (*t == '\0') t = "logfile.txt"; if ((logfile = open(t, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) { sprintf(s, "Unable to open %s", buf + 2); statline(mode, s); } else statbits(mode, 'L', 1); continue; case 'C': case 'c': if (logfile != -1) { close(logfile); logfile = -1; statbits(mode, '-', 1); } else { statline(mode, "Log file not open"); } continue; case 'Y': case 'y': cmd_yapp(buf + 2, bytes - 2); continue; case '~': bytes--; memmove(buf, buf + 1, strlen(buf)); break; case 'R': case 'r': flags |= FLAG_RECONNECT; bytes = 0; interrupted = TRUE; continue; case '0': mode = change_mode(mode, RAWMODE, &wintab, &win_in, &win_out, call); continue; case '1': mode = change_mode(mode, SLAVEMODE, &wintab, &win_in, &win_out, call); continue; case '2': mode = change_mode(mode, TALKMODE, &wintab, &win_in, &win_out, call); continue; default: statline(mode, "Unknown '~' escape. Type ~h for a list"); continue; } } /* if (bytes == -1 && errno != EWOULDBLOCK && errno != EAGAIN) */ /* if ((bytes == 0 && (mode & (TALKMODE|SLAVEMODE)) == 0) || (bytes == -1 && errno != EWOULDBLOCK && errno != EAGAIN)) */ if (interrupted || (bytes == -1 && errno != EWOULDBLOCK && errno != EAGAIN)) { if (!interrupted) perror("input"); break; } if (bytes > 0) { sevenplus = FALSE; if (uploadfile != -1) { statline(mode, "Ignored. Type ~s to stop upload"); continue; } convert_lf_cr(buf, bytes); if (write(fd, buf, bytes) == -1) { perror("write"); break; } } if (uploadfile != -1) { if (uplsize == 0) { close(uploadfile); uploadfile = -1; delwin(swin); winclose(&wintab); statline(mode, "Upload complete: 0 bytes"); continue; } if (upldp == -1) { upllen = read(uploadfile, uplbuf, 128); if (upllen == 0) { close(uploadfile); uploadfile = -1; delwin(swin); winclose(&wintab); sprintf(s, "Upload complete: %ld bytes", uplpos); statline(mode, s); continue; } if (upllen == -1) { close(uploadfile); uploadfile = -1; delwin(swin); winclose(&wintab); sprintf(s, "Error reading upload file: upload aborted at %ld bytes", uplpos); statline(mode, s); continue; } if (!binup) convert_lf_cr(uplbuf, upllen); upldp = 0; } bytes = write(fd, uplbuf + upldp, upllen - upldp); if ((bytes == 0 || bytes == -1) && errno != EWOULDBLOCK && errno != EAGAIN) { sprintf(s, "Write error during upload. Connection lost"); statline(mode, s); perror("write"); break; } /* if (uplpos / 1024 != (uplpos + bytes) / 1024) { */ /* printf("\r%ld bytes sent ", uplpos + bytes); */ sprintf(s, "%ld", uplpos + bytes); dupdstatw(swin, s, FALSE); /* } */ uplpos += bytes; upldp += bytes; if (upldp >= upllen) upldp = -1; } } } close(fd); if (logfile != -1) { close(logfile); logfile = -1; } if (downloadfile != -1) { close(downloadfile); downloadfile = -1; } fcntl(STDIN_FILENO, F_SETFL, 0); signal(SIGQUIT, SIG_IGN); signal(SIGINT, SIG_DFL); if (mode != RAWMODE) endwin(); printf("*** Cleared\n"); if (flags & FLAG_RECONNECT) { return TRUE; } else { return FALSE; } } int main(int argc, char **argv) { int p; int mode = TALKMODE; while ((p = getopt(argc, argv, "b:dhm:p:rtvw:")) != -1) { switch (p) { case 'b': if (*optarg != 'e' && *optarg != 'l') { fprintf(stderr, "call: invalid argument for option '-b'\n"); return 1; } backoff = *optarg == 'e'; break; case 'd': debug = TRUE; break; case 'h': mode = SLAVEMODE; break; case 'm': if (*optarg != 's' && *optarg != 'e') { fprintf(stderr, "call: invalid argument for option '-m'\n"); return 1; } ax25mode = *optarg == 'e'; break; case 'p': if ((paclen = atoi(optarg)) == 0) { fprintf(stderr, "call: option '-p' requires a numeric argument\n"); return 1; } if (paclen < 1 || paclen > 500) { fprintf(stderr, "call: paclen must be between 1 and 500\n"); return 1; } break; case 'r': mode = RAWMODE; break; case 't': mode = TALKMODE; break; case 'v': printf("call: %s\n", VERSION); return 0; case 'w': if ((window = atoi(optarg)) == 0) { fprintf(stderr, "call: option '-w' requires a numeric argument\n"); return 1; } if (ax25mode) { if (window < 1 || window > 63) { fprintf(stderr, "call: window must be between 1 and 63 frames\n"); return 1; } } else { if (window < 1 || window > 7) { fprintf(stderr, "call: window must be between 1 and 7 frames\n"); return 1; } } break; case '?': case ':': fprintf(stderr, "usage: call [-b l|e] [-d] [-h] [-m s|e] [-p paclen] [-r] [-t] [-v] [-w window] port callsign [[via] digipeaters...]\n"); return 1; } } if (optind == argc || optind == argc - 1) { fprintf(stderr, "usage: call [-b l|e] [-d] [-h] [-m s|e] [-p paclen] [-r] [-t] [-v] [-w window] port callsign [[via] digipeaters...]\n"); return 1; } port = argv[optind]; if (ax25_config_load_ports() == 0) { fprintf(stderr, "call: no AX.25 port data configured\n"); return 1; } if (ax25_config_get_addr(port) == NULL) { nr_config_load_ports(); if (nr_config_get_addr(port) == NULL) { rs_config_load_ports(); if (rs_config_get_addr(port) == NULL) { fprintf(stderr, "call: invalid port setting\n"); return 1; } else { af_mode = AF_ROSE; } } else { af_mode = AF_NETROM; } } else { af_mode = AF_AX25; } switch (af_mode) { case AF_ROSE: paclen = rs_config_get_paclen(port); break; case AF_NETROM: if (paclen == 0) paclen = nr_config_get_paclen(port); break; case AF_AX25: if (window == 0) window = ax25_config_get_window(port); if (paclen == 0) paclen = ax25_config_get_paclen(port); break; } printf("GW4PTS AX.25 Connect v1.11\n"); while (cmd_call(argv + optind + 1, mode)) { printf("Wait 60 sec before reconnect\n"); sleep(60); } return 0; }