/* * (c) 2002 Thomas Osterried DL9SAU * License: GPL. See http://www.fsf.org/ * Sources: http://x-berg.in-berlin.de/cgi-bin/viewcvs.cgi/ampr/axgetput/ */ #include "includes.h" #include "proto_bin.h" #include "axgetput.h" #include "util.h" static int crctab[256]; static int crcbit[8] = { 0x9188, 0x48c4, 0x2462, 0x1231, 0x8108, 0x4084, 0x2042, 0x1021 }; /*---------------------------------------------------------------------------*/ static int init_crc(void) { int i,j; for (i = 0; i < 256; i++) { crctab[i] = 0; for (j = 0; j < 8; j++) { if ((i << j) & 0x80) crctab[i] = crctab[i] ^ crcbit[j]; } } return 0; } /*---------------------------------------------------------------------------*/ static int do_crc(char b, unsigned int crc) { return (crctab[(crc >> 8)] ^ ((crc << 8) | (b & 0xff))) & 0xffff; } static unsigned int calc_crc(char *buf, int n, unsigned crc) { while (--n >= 0) crc = do_crc(*buf++, crc); return crc; } /*---------------------------------------------------------------------------*/ static long parse_sfbin_date_to_unix(const char *s) { unsigned long x; sscanf(s, "%lX", &x); return date_dos2unix(((x << 16) >> 16), (x >> 16)); } /*---------------------------------------------------------------------------*/ static char * unix_to_sfbin_date_string(long gmt) { static char buf[9]; unsigned short s_time, s_date; date_unix2dos(((gmt == -1) ? time(NULL) : gmt), &s_time, &s_date); sprintf(buf, "%X", ((s_date << 16) + s_time)); return buf; } /*---------------------------------------------------------------------------*/ int bput(void) { struct stat statbuf; char buf[1024]; /* < signed int */ char filename_given[PATH_MAX]; unsigned long len_read_expected = 0L; unsigned long len_read_left; time_t file_time = 0L; unsigned int msg_crc = 0; unsigned int crc = 0; char *term_line = NULL; int last_line_had_CR = 0; int len_termline = 0; int len = 0; int fddata = fdout; int is_eof; int i; char *p; char *p_buf; #define save_close(x) { \ if (!fdout_is_pipe) \ close(x); \ } for (;;) { len = my_read(fdin, buf, sizeof(buf), &is_eof, "\r\n"); if (is_eof || len < 1) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); return 1; } if (buf[len-1] == '\n') { #if 0 p = "warning: received. not 8bit clean?\r"; secure_write(fdout, p, strlen(p)); #endif sprintf(err_msg, "bad EOL: \n"); return 1; } if (IS_BIN_ABORT(buf, len)) { sprintf(err_msg, "Aborted by user request\n"); return 1; } if (buf[len-1] == '\r' && len > 5 && !memcmp(buf, "#BIN#", 5)) { break; } if (len == sizeof(buf)) { sprintf(err_msg, "line to long\n"); return 1; } } buf[len-1] = 0; /* without trailing \r. and: string termination */ send_on_signal = bin_send_no_on_sig; /* parse #BIN arguments */ *filename_given = 0; p_buf = buf; for (i = 0; (p = strsep(&p_buf, "#")); i++) { switch (i) { case 0: case 1: break; case 2: if (*p) len_read_expected = (unsigned long ) atol(p); break; default: if (*p == '|') { msg_crc = (unsigned int ) atoi(p+1); } else if (*p == '$') { file_time = parse_sfbin_date_to_unix(p+1); } else { strncpy(filename_given, p, sizeof(filename_given)-1); filename_given[sizeof(filename_given)-1] = 0; } } } if (!fdout_is_pipe) { /* normal mode: store in given filename */ if (!*filename) { p = get_fixed_filename(filename_given, len_read_expected, msg_crc, 1); strncpy(filename, p, sizeof(filename)-1); filename[sizeof(filename)-1] = 0; } if (!stat(filename, &statbuf)) { /* file exist */ if (unlink(filename)) { sprintf(err_msg, "error: cannot unlink %s (%s)\n", filename, strerror(errno)); goto abort; } } fddata = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0640); if (fddata < 0) { sprintf(err_msg, "error: cannot open %s (%s)\n", filename, strerror(errno)); write(fderr, "\r#NO#\r", 6); return 1; } } if (!len_read_expected) { term_line = "***END\r"; len_termline = strlen(term_line); } /* say helo */ send_on_signal = bin_send_abort_on_sig; write(fderr, "\r#OK#\r", 6); len_read_left = len_read_expected; /* #bin# chechsum initialization */ init_crc(); for (;;) { len = my_read(fdin, buf, ((term_line || len_read_left > sizeof(buf)) ? sizeof(buf) : len_read_left), &is_eof, "\r"); if (len < 1) { save_close(fddata); sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); goto abort; } if (!len) { save_close(fddata); if (!term_line) { sprintf(err_msg, "error: unexpected end of file during read: %s\n", strerror(errno)); return 1; } return 0; } if (msg_crc) crc = calc_crc(buf, len, crc); if (buf[len-1] == '\r') { if (last_line_had_CR) { if (IS_BIN_ABORT(buf, len)) { /* "\r#ABORT#\r" was sent */ if (!fdout_is_pipe) { close(fddata); /* clean up */ unlink(filename); } return 1; } if (term_line && len == len_termline && !memcmp(buf, term_line, len_termline)) { /* sucessfully read until termination string */ break; } } last_line_had_CR = 1; } else { last_line_had_CR = 0; } if (!term_line) len_read_left -= len; if (secure_write(fddata, buf, len) == -1) { save_close(fddata); sprintf(err_msg, "error: write failed (%s)\n", strerror(errno)); goto abort; } /* nothing left? */ if (!term_line && len_read_left == 0L) break; if (is_eof) { if (!term_line && len_read_left) { save_close(fddata); goto abort; } break; } } if (crc != msg_crc) { sprintf(err_msg, "Invalid crc: computed %d, expected %d.\n", crc, msg_crc); /* don't unlink */ save_close(fddata); return 1; } if (!fdout_is_pipe) { close(fddata); if (file_time != 0L) { struct utimbuf utb; utb.modtime = file_time; utb.actime = time(NULL); utime(filename, &utb); } } send_on_signal = NULL; return 0; abort: sleep(1); write(fderr, "\r#ABORT#\r", 9); return 1; #undef save_close } /*---------------------------------------------------------------------------*/ int bget(void) { struct strlist { struct strlist *next; size_t len; char data[1]; /* actually a the address of char * pointer */ }; struct strlist *stored_file = NULL; struct strlist *sl_tail = NULL; struct strlist *sl; struct timeval timeout; struct stat statbuf; unsigned int crc = 0; char buf[1024]; fd_set readfds; int fddata = fdin; int len; unsigned long file_size = 0; unsigned long len_remains; int is_eof; time_t file_time = 0L; #define save_close(x) { \ if (!fdin_is_pipe) \ close(x); \ } #define store_line(s, len) { \ if (!(sl = (struct strlist *) malloc(sizeof(struct strlist *) + sizeof(size_t) + len))) \ return 1; \ sl->next = NULL; \ sl->len = len; \ memcpy(sl->data, s, len); \ if (!stored_file) { \ stored_file = sl; \ } else { \ sl_tail->next = sl; \ } \ sl_tail = sl; \ } if (BLOCKSIZ < 1 || BLOCKSIZ > sizeof(buf)) BLOCKSIZ = BLOCKSIZ_DEFAULT; init_crc(); if (!fdin_is_pipe && *filename) { fddata = open(filename, O_RDONLY); if (fddata == -1) { sprintf(err_msg, "error: cannot open %s (%s)\n", filename, strerror(errno)); return 1; } if (!fstat(fddata, &statbuf)) file_time = statbuf.st_mtime; else file_time = time(NULL); /* compute crc */ while ((len = read(fddata, buf, BLOCKSIZ)) > 0) { crc = calc_crc(buf, len, crc); file_size += len; } if (len < 0) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); close(fddata); return 1; } /* rewind */ if (lseek(fddata, 0L, SEEK_SET) != 0L) { sprintf(err_msg, "error: file io failed on lseek() (%s)\n", strerror(errno)); close(fddata); return 1; } sprintf(buf, "\r#BIN#%ld#|%d#$%s#%s\r", file_size, crc, unix_to_sfbin_date_string(file_time), get_fixed_filename(filename, file_size, crc, 1)); } else { file_time = time(NULL); if (!is_stream || do_crc_only) { sprintf(err_msg, "error: not enough memory\n"); while ((len = read(fddata, buf, sizeof(buf))) > 0) { crc = calc_crc(buf, len, crc); file_size += len; if (!do_crc_only) store_line(buf, len); } if (len < 0) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); close(fddata); return 1; } *err_msg = 0; sprintf(buf, "\r#BIN#%ld#|%d#$%s#%s\r", file_size, crc, unix_to_sfbin_date_string(file_time), get_fixed_filename(filename, file_size, crc, 1)); } else { sprintf(buf, "\r#BIN###$%s#%s\r", unix_to_sfbin_date_string(file_time), get_fixed_filename(filename, 0, 0, 1)); } /* * hack: check for #ABORT# from fdout (fd 1), because fddata (fd 0) is * our pipe we read the data from, which we actually tx. * believe me, it does work. */ fdin = fdout; } if (do_crc_only) { printf("File information for %s:\n", get_fixed_filename(filename, file_size, crc, 1)); printf(" size %ld bytes, crc %d, date %s (%ld)\n", file_size, crc, unix_to_sfbin_date_string(file_time), file_time); return 0; } send_on_signal = bin_send_abort_on_sig; if (secure_write(fdout, buf, strlen(buf)) == -1) { sprintf(err_msg, "error: write failed (%s)\n", strerror(errno)); save_close(fddata); return 1; } /* wait for answer */ for (;;) { /* * make sure we do not read from a pipe. fdout is also * assigned to the tty */ len = my_read(fdout, buf, sizeof(buf), &is_eof, "\r\n"); if (is_eof || len < 1) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); save_close(fddata); return 1; } if (buf[len-1] == '\n') { #if 0 char *p = "warning: received. not 8bit clean?\r"; secure_write(fdout, p, strlen(p)); #endif sprintf(err_msg, "bad EOL: \n"); goto abort; } else if (buf[len-1] != '\r') { sprintf(err_msg, "line to long\n"); continue; } if (IS_BIN_OK(buf, len)) break; if (IS_BIN_NO(buf, len)) { save_close(fddata); return 0; } if (IS_BIN_ABORT(buf, len)) { sprintf(err_msg, "Aborted by user request\n"); save_close(fddata); return 1; } } len_remains = file_size; timeout.tv_sec = 0; timeout.tv_usec = 0; for (;;) { char *p_buf; /* check for user \r#ABORT#\r on tty stream */ FD_ZERO(&readfds); FD_SET(fdin, &readfds); if (select(fdin+1, &readfds, NULL, NULL, &timeout) && FD_ISSET(fdin, &readfds)) { len = read(fdin, buf, sizeof(buf)); if (len < 0) { sprintf(err_msg, "read from tty failed (%s)\n", strerror(errno)); save_close(fddata); goto abort; } if (IS_BIN_ABORT(buf, len)) { sprintf(err_msg, "Aborted by user request\n"); save_close(fddata); return 1; } } /* read data */ if (!fdin_is_pipe || is_stream) { p_buf = buf; len = my_read(fddata, buf, ((len_remains > BLOCKSIZ || is_stream) ? BLOCKSIZ : len_remains), &is_eof, NULL); if (len < 1) { save_close(fddata); if (len < 0) { sprintf(err_msg, "error: read failed (%s)\n", strerror(errno)); goto abort; } break; } len_remains -= len; } else { p_buf = stored_file->data; len = stored_file->len; } /* write to client */ if (secure_write(fdout, p_buf, len) == -1) { sprintf(err_msg, "error: write failed (%s)\n", strerror(errno)); save_close(fddata); goto abort; } if (fdin_is_pipe && !is_stream) { sl = stored_file; stored_file = stored_file->next; if (!stored_file) is_eof = 1; free(sl); } if (!fdin_is_pipe && !len_remains) { if (read(fddata, buf, 1) == 1) { sprintf(err_msg, "Warning: file has grown in the meantime\n"); } is_eof = 1; break; } /* * need this because my_read may returned lenth != 0 (data to be written) * but also has detected EOF. */ if (is_eof) break; } sleep(10); return 0; abort: sleep(1); write(fderr, "\r#ABORT#\r", 9); return 1; #undef save_close }