home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume16 / pcomm2 / part07 / x_rcv.c < prev   
Encoding:
C/C++ Source or Header  |  1988-09-14  |  11.5 KB  |  522 lines

  1. /*
  2.  * Receive a list of files using a version of Ward Christensen's file
  3.  * transfer protocol.  A return code of 1 means the user must acknowledge
  4.  * an error condition (a user generated abort returns a 0).  Write errors
  5.  * are considered fatal.
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include <curses.h>
  10. #include "config.h"
  11. #include "dial_dir.h"
  12. #include "misc.h"
  13. #include "xmodem.h"
  14.  
  15. unsigned char buf[1029];
  16. char file_name[15];
  17. long file_length;
  18.  
  19. static int err_method, tot_err, block_size;
  20.  
  21. int
  22. rcv_xmodem(win, list, type, fast)
  23. WINDOW *win;
  24. char *list;
  25. int type, fast;
  26. {
  27.     extern char *protocol[];
  28.     FILE *fp, *my_fopen();
  29.     int i, default_err, is_batch, max_block, code, file_count, got_hdr;
  30.     int hours, mins, secs, len;
  31.     long block, recv;
  32.     float percent, performance;
  33.     unsigned char blk;
  34.     unsigned int sleep();
  35.     char *file, *name, *strcpy(), *strrchr(), *strtok();
  36.     void cancel_xfer();
  37.                     /* which protocol? */
  38.     switch (type) {
  39.         case XMODEM:
  40.             default_err = CRC_CHECKSUM;
  41.             is_batch = 0;
  42.             max_block = 128;
  43.             break;
  44.         case XMODEM_1k:
  45.             default_err = CRC_CHECKSUM;
  46.             is_batch = 0;
  47.             max_block = 1024;
  48.             break;
  49.         case MODEM7:
  50.             default_err = CHECKSUM;
  51.             is_batch = 1;
  52.             max_block = 128;
  53.             break;
  54.         case YMODEM:
  55.             default_err = CRC;
  56.             is_batch = 1;
  57.             max_block = 1024;
  58.             performance = 1.09;
  59.             break;
  60.         case YMODEM_G:
  61.             default_err = NONE;
  62.             is_batch = 1;
  63.             max_block = 1024;
  64.             performance = 1.02;
  65.             break;
  66.         default:
  67.             return(1);
  68.     }
  69.  
  70.     tot_err = 0;
  71.     file_count = 0;
  72.     mvwaddstr(win, 2, 24, protocol[type]);
  73.     mvwaddstr(win, 11, 24, "0  ");
  74.  
  75.     while (1) {
  76.         file_count++;
  77.         file_length = 0L;
  78.                     /* user supplied name */
  79.         if (!is_batch) {
  80.             if (file_count > 1)
  81.                 break;
  82.  
  83.             file = strtok(list, "     ");
  84.                     /* dissect the file name */
  85.             if ((name = strrchr(file, '/')))
  86.                 strcpy(file_name, name++);
  87.             else
  88.                 strcpy(file_name, file);
  89.         }
  90.                     /* get the modem7 file name */
  91.         if (type == MODEM7) {
  92.             if (code = rcv_modem7(win, default_err))
  93.                 return(code +1);
  94.  
  95.             file = file_name;
  96.         }
  97.                     /* get the block 0 */
  98.         if (type == YMODEM || type == YMODEM_G) {
  99.             if (code = send_first(win, max_block, default_err))
  100.                 return(code +1);
  101.  
  102.             if (code = rcv_ymodem(win))
  103.                 return(code +1);
  104.  
  105.                     /* at the end? */
  106.             if (buf[3] == NULL) {
  107.                 beep();
  108.                 wrefresh(win);
  109.                 putc_line(ACK);
  110.                 sleep(1);
  111.                 return(0);
  112.             }
  113.             file = file_name;
  114.         }
  115.                     /* any trouble? */
  116.         if (file_name[0] == NULL)
  117.             continue;
  118.  
  119.         clear_line(win, 3, 24, 1);
  120.         waddstr(win, file_name);
  121.                     /* if file length is known */
  122.         if (file_length != 0L) {
  123.             mvwprintw(win, 4, 24, "%-10ld", file_length);
  124.  
  125.             secs = (file_length * 10.0 / dir->baud[dir->d_cur]) * performance;
  126.             hours = secs / 3600;
  127.             mins = (secs % 3600) / 60;
  128.             secs = (secs % 3600) % 60;
  129.  
  130.             mvwprintw(win, 6, 24, "%d:%02d:%02d", hours, mins, secs);
  131.         }
  132.                     /* some starting numbers */
  133.         mvwaddstr(win, 7, 24, "0    ");
  134.         if (file_length != 0L && fast)
  135.             mvwaddstr(win, 8, 24, "0%  ");
  136.         if (fast)
  137.             mvwaddstr(win, 9, 24, "0          ");
  138.         mvwaddstr(win, 10, 24, "0 ");
  139.         clear_line(win, 12, 24, 1);
  140.         waddstr(win, "NONE");
  141.         wrefresh(win);
  142.  
  143.         /*
  144.          * If the user supplied the name, write permission is checked
  145.          * by the get_names() routine in xfer_menu().  If modem7
  146.          * or ymodem supplied name, the name is unique and the write
  147.          * permission on the directory is checked by the change_name()
  148.          * routines.  So why is this here?
  149.          */
  150.                     /* open the file */
  151.         if (!(fp = my_fopen(file, "w"))) {
  152.             beep();
  153.             clear_line(win, 12, 24, 1);
  154.             wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
  155.             wrefresh(win);
  156.             cancel_xfer(1);
  157.             return(1);
  158.         }
  159.                     /* ACK the block 0 */
  160.         if (type == YMODEM || type == YMODEM_G)
  161.             putc_line(ACK);
  162.  
  163.         if (code = send_first(win, max_block, default_err)) {
  164.             fclose(fp);
  165.             return(code +1);
  166.         }
  167.                     /* here we go... */
  168.         clear_line(win, 12, 24, 1);
  169.         waddstr(win, "NONE");
  170.         wrefresh(win);
  171.         blk = 1;
  172.         block = 1L;
  173.         recv = 0L;
  174.         got_hdr = 1;
  175.         while (1) {
  176.             code = rcv_block(win, got_hdr, max_block, blk);
  177.  
  178.             if (code < 0) {
  179.                 fclose(fp);
  180.                 return(code +1);
  181.             }
  182.             got_hdr = 0;
  183.                     /* are we done? */
  184.             if (buf[0] == EOT) {
  185.                 if (!is_batch) {
  186.                     beep();
  187.                     wrefresh(win);
  188.                     sleep(1);
  189.                 }
  190.                 break;
  191.             }
  192.                     /* if not a duplicate block */
  193.             if (!code) {
  194.                 if (file_length != 0L) {
  195.                     len = file_length - recv;
  196.                     if (len > block_size)
  197.                         len = block_size;
  198.                 }
  199.                 else
  200.                     len = block_size;
  201.  
  202.                 if (fwrite((char *) &buf[3], sizeof(char), len, fp) != len) {
  203.                     beep();
  204.                     clear_line(win, 12, 24, 1);
  205.                     wattrstr(win, A_BOLD, "WRITE ERROR");
  206.                     wrefresh(win);
  207.                     cancel_xfer(1);
  208.                     fclose(fp);
  209.                     /* fatal */
  210.                     return(1);
  211.                 }
  212.                 mvwprintw(win, 7, 24, "%-5ld", block);
  213.                 recv = recv + (unsigned int) len;
  214.                 if (fast)
  215.                     mvwprintw(win, 9, 24, "%-10ld", recv);
  216.                 blk++;
  217.                 block++;
  218.             }
  219.             /*
  220.              * If the length is known, give the same status
  221.              * report as uploading
  222.              */
  223.             if (file_length != 0L && fast) {
  224.                 percent = recv * 100.0 / file_length;
  225.                 if (percent > 100.0)
  226.                     percent = 100.0;
  227.                 mvwprintw(win, 8, 24, "%0.1f%%", percent);
  228.             }
  229.             wrefresh(win);
  230.             putc_line(ACK);
  231.         }
  232.         if (file_length != 0L && fast) {
  233.             mvwaddstr(win, 8, 24, "100%  ");
  234.             wrefresh(win);
  235.         }
  236.         /*
  237.          * If the file length is not known, search backwards from
  238.          * the end of the file until you find a character that is
  239.          * not the ^Z padding character.
  240.          */
  241.         if (file_length == 0L) {
  242.             for (i=block_size+2; i>2; i--) {
  243.                 if (buf[i] != CTRLZ)
  244.                     break;
  245.             }
  246.             file_length = recv - (unsigned int) block_size + (unsigned int) i -2L;
  247.             fclose(fp);
  248.             if (fix_length(file_name, file_length)) {
  249.                 beep();
  250.                 clear_line(win, 12, 24, 1);
  251.                 wattrstr(win, A_BOLD, "TRUNCATE ERROR");
  252.                 wrefresh(win);
  253.                     /* fatal */
  254.                 return(1);
  255.             }
  256.         }
  257.         else
  258.             fclose(fp);
  259.                     /* ACK the EOT */
  260.         putc_line(ACK);
  261.     }
  262.     return(0);
  263. }
  264.  
  265. /*
  266.  * Send the first character to start the transmission and set the error
  267.  * checking method.  Returns the standard error codes or 0 on success.
  268.  * The variables err_method and block_size are global.
  269.  */
  270.  
  271. static int
  272. send_first(win, max_block, default_err)
  273. WINDOW *win;
  274. int max_block, default_err;
  275. {
  276.     int i, err_count;
  277.     unsigned int sleep();
  278.     void cancel_xfer();
  279.                     /* default error method */
  280.     err_method = default_err;
  281.     if (default_err == CRC_CHECKSUM)
  282.         err_method = CRC;
  283.                     /* send the first char */
  284.     err_count = 0;
  285.     while (err_count < MAX_ERRORS*2) {
  286.         mvwprintw(win, 10, 24, "%-2d", err_count);
  287.  
  288.                     /* check for keyboard abort */
  289.         if (wgetch(win) == ESC) {
  290.             beep();
  291.             clear_line(win, 12, 24, 1);
  292.             waddstr(win, "ABORTED");
  293.             wrefresh(win);
  294.             cancel_xfer(1);
  295.             sleep(3);
  296.             return(ABORT);
  297.         }
  298.                     /* switch to checksum? */
  299.         if (default_err == CRC_CHECKSUM && err_count > MAX_ERRORS/2)
  300.             err_method = CHECKSUM;
  301.  
  302.                     /* send error method code */
  303.         clear_line(win, 5, 24, 1);
  304.         switch (err_method) {
  305.             case CHECKSUM:
  306.                 waddstr(win, "CHECKSUM");
  307.                 putc_line(NAK);
  308.                 break;
  309.             case CRC:
  310.                 waddstr(win, "CRC");
  311.                 putc_line('C');
  312.                 break;
  313.             case NONE:
  314.                 waddstr(win, "NONE");
  315.                 putc_line('G');
  316.                 break;
  317.         }
  318.         /*
  319.          * We've cut the delay time in half, so we double
  320.          * the allowable errors
  321.          */
  322.         if ((i = getc_line(5)) == -1) {
  323.             err_count++;
  324.             clear_line(win, 12, 24, 1);
  325.             waddstr(win, "NO RESPONSE");
  326.             wrefresh(win);
  327.             continue;
  328.         }
  329.         buf[0] = i;
  330. #ifdef DEBUG
  331.         fprintf(stderr, "send_first: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
  332. #endif /* DEBUG */
  333.  
  334.         switch (buf[0]) {
  335.             case SOH:    /* small block follows */
  336.                 block_size = 128;
  337.                 return(0);
  338.             case STX:    /* large block follows */
  339.                 if (max_block == 1024) {
  340.                     block_size = 1024;
  341.                     return(0);
  342.                 }
  343.                 /* fall thru */
  344.             default:
  345.                 err_count++;
  346.                 clear_line(win, 12, 24, 1);
  347.                 waddstr(win, "BAD HEADER");
  348.                 wrefresh(win);
  349.                     /* read some garbage... */
  350.                 fread_line(buf, 1028, 3);
  351.                 putc_line(NAK);
  352.                 break;
  353.         }
  354.     }
  355.     beep();
  356.     clear_line(win, 12, 24, 1);
  357.     wattrstr(win, A_BOLD, "TIMED OUT");
  358.     wrefresh(win);
  359.     return(ERROR);
  360. }
  361.  
  362. /*
  363.  * Receive a block of info from the host.  Returns a 0 on success, a 1 on
  364.  * a duplicate block or the standard error codes.  The variables
  365.  * err_method and block_size are global.
  366.  */
  367.  
  368. int
  369. rcv_block(win, got_hdr, max_block, blk)
  370. WINDOW *win;
  371. int got_hdr, max_block;
  372. unsigned char blk;
  373. {
  374.     int i, err_count, bad_block, out_of_sync;
  375.     unsigned short crc, calc_crc();
  376.     unsigned int packet, sleep();
  377.     unsigned char calc_sum(), crc_1, crc_2;
  378.     void cancel_xfer();
  379.  
  380.     err_count = 0;
  381.     while (err_count < MAX_ERRORS) {
  382.         mvwprintw(win, 10, 24, "%-2d", err_count);
  383.         mvwprintw(win, 11, 24, "%-3d", tot_err);
  384.  
  385.                     /* scan the keyboard for abort */
  386.         if (wgetch(win) == ESC) {
  387.             beep();
  388.             clear_line(win, 12, 24, 1);
  389.             waddstr(win, "ABORTED");
  390.             wrefresh(win);
  391.             cancel_xfer(1);
  392.             sleep(3);
  393.             return(ABORT);
  394.         }
  395.                     /* have we already got a hdr? */
  396.         if (!got_hdr) {
  397.             if ((i = getc_line(10)) == -1) {
  398.                 err_count++;
  399.                 tot_err++;
  400.                 clear_line(win, 12, 24, 1);
  401.                 waddstr(win, "NO RESPONSE");
  402.                 wrefresh(win);
  403.                 continue;
  404.             }
  405.             buf[0] = i;
  406. #ifdef DEBUG
  407.             fprintf(stderr, "rcv_block: got header %02x, %03o, %d\n", buf[0], buf[0], buf[0]);
  408. #endif /* DEBUG */
  409.                     /* what'd we get? */
  410.             switch (buf[0]) {
  411.                 case EOT:    /* we're done! */
  412.                     clear_line(win, 12, 24, 1);
  413.                     waddstr(win, "TRANSFER COMPLETE");
  414.                     wrefresh(win);
  415.                     sleep(1);
  416.                     return(0);
  417.                 case SOH:    /* small block follows */
  418.                     block_size = 128;
  419.                     break;
  420.                 case STX:    /* large block follows */
  421.                     if (max_block == 1024) {
  422.                         block_size = 1024;
  423.                         break;
  424.                     }
  425.                     /* fall thru... */
  426.                 default:
  427.                     err_count++;
  428.                     tot_err++;
  429.                     clear_line(win, 12, 24, 1);
  430.                     waddstr(win, "BAD HEADER");
  431.                     wrefresh(win);
  432.  
  433.                     /* flush a bad packet */
  434.                     fread_line(buf, 1028, 5);
  435.                     putc_line(NAK);
  436.                     continue;
  437.             }
  438.         }
  439.         got_hdr = 0;
  440.                     /* read the rest of the packet */
  441.         packet = block_size + 2 + (err_method == CHECKSUM ? 1 : 2);
  442.         if (fread_line(&buf[1], packet, 10) == -1) {
  443.             clear_line(win, 12, 24, 1);
  444.             waddstr(win, "TIMED OUT");
  445.             wrefresh(win);
  446.             putc_line(NAK);
  447.             err_count++;
  448.             tot_err++;
  449.             continue;
  450.         }
  451.  
  452.         /*
  453.          * Validation of the packet includes checking the
  454.          * block number, its complement, and the crc/checksum.
  455.          */
  456.         out_of_sync = 0;
  457.         if (buf[1] != blk || buf[2] != (unsigned char) ~blk)
  458.             out_of_sync++;
  459.  
  460.         bad_block = 0;
  461.         switch (err_method) {
  462.             case CHECKSUM:
  463. #ifdef DEBUG
  464.                 fprintf(stderr, "blk=%d, checksum=%d\n", blk, calc_sum(&buf[3], block_size));
  465. #endif /* DEBUG */
  466.                 if (buf[block_size +3] != calc_sum(&buf[3], block_size))
  467.                     bad_block++;
  468.                 break;
  469.             case CRC:
  470.                 crc = calc_crc(&buf[3], block_size);
  471.                 crc_1 = crc >> 8;
  472.                 crc_2 = crc;
  473. #ifdef DEBUG
  474.                 fprintf(stderr, "blk=%d, crc1=%d, crc2=%d\n", blk, crc_1, crc_2);
  475. #endif /* DEBUG */
  476.                 if (buf[block_size +3] != crc_1 || buf[block_size +4] != crc_2)
  477.                     bad_block++;
  478.                 break;
  479.             case NONE:
  480.                 return(0);
  481.         }
  482.                     /* handle errors */
  483.         if (bad_block) {
  484.             clear_line(win, 12, 24, 1);
  485.             if (err_method == CRC)
  486.                 waddstr(win, "CRC FAILED");
  487.             else
  488.                 waddstr(win, "CHECKSUM FAILED");
  489.             wrefresh(win);
  490.             putc_line(NAK);
  491.             err_count++;
  492.             tot_err++;
  493.             continue;
  494.         }
  495.                     /* not really an error */
  496.         if (out_of_sync) {
  497.             /*
  498.              * If a perfect packet is off by 1 block number,
  499.              * (a lost ACK could cause this) then treat it as
  500.              * a good block but don't write it to disk.
  501.              */
  502.             if (buf[1] == (unsigned char) blk-1)
  503.                 return(1);
  504.  
  505.             clear_line(win, 12, 24, 1);
  506.             waddstr(win, "OUT OF SYNC");
  507.             wrefresh(win);
  508.             putc_line(NAK);
  509.             err_count++;
  510.             tot_err++;
  511.             continue;
  512.         }
  513.         return(0);
  514.     }
  515.     beep();
  516.     clear_line(win, 12, 24, 1);
  517.     waddstr(win, "TOO MANY ERRORS");
  518.     wrefresh(win);
  519.     cancel_xfer(1);
  520.     return(ERROR);
  521. }
  522.