home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / pcomm-2.0.2 / part04 / x_rcv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-13  |  11.8 KB  |  527 lines

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