home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume14 / pcomm / part06 / x_rcv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-05-18  |  10.3 KB  |  477 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.
  5.  */
  6.  
  7. #include <stdio.h>
  8. #include <curses.h>
  9. #include "dial_dir.h"
  10. #include "misc.h"
  11. #include "xmodem.h"
  12.  
  13. unsigned char buf[1029];
  14. char file_name[15];
  15. int file_length, err_method, tot_err, block_size;
  16.  
  17. int
  18. rcv_xmodem(win, list, type, fast)
  19. WINDOW *win;
  20. char *list;
  21. int type, fast;
  22. {
  23.     FILE *fp;
  24.     int default_err, is_batch, max_block, code, file_count, i, got_hdr;
  25.     int block, recv, hours, mins, secs;
  26.     float percent, performance;
  27.     unsigned char blk;
  28.     unsigned int sleep();
  29.     char *file, *name, *strcpy(), *strrchr(), *strtok();
  30.     void cancel_xfer();
  31.  
  32.     /*
  33.      * What type of xmodem?  The default_err means:  0=checksum only,
  34.      * 1=CRC or checksum, 2=CRC only, and 3=none.
  35.      */
  36.     switch(type) {
  37.         case XMODEM:
  38.             mvwaddstr(win, 2, 24, "xmodem");
  39.             default_err = 1;
  40.             is_batch = 0;
  41.             max_block = 128;
  42.             break;
  43.         case XMODEM_1k:
  44.             mvwaddstr(win, 2, 24, "xmodem-1k");
  45.             default_err = 1;
  46.             is_batch = 0;
  47.             max_block = 1024;
  48.             break;
  49.         case MODEM7:
  50.             mvwaddstr(win, 2, 24, "modem7");
  51.             default_err = 0;
  52.             is_batch = 1;
  53.             max_block = 128;
  54.             break;
  55.         case YMODEM:
  56.             mvwaddstr(win, 2, 24, "ymodem");
  57.             default_err = 2;
  58.             is_batch = 1;
  59.             max_block = 1024;
  60.             performance = 1.09;
  61.             break;
  62.         case YMODEM_G:
  63.             mvwaddstr(win, 2, 24, "ymodem-g");
  64.             default_err = 3;
  65.             is_batch = 1;
  66.             max_block = 1024;
  67.             performance = 1.02;
  68.             break;
  69.     }
  70.  
  71.     tot_err = 0;
  72.     file_count = 0;
  73.     mvwaddstr(win, 11, 24, "0  ");
  74.  
  75.     while (1) {
  76.         file_count++;
  77.         file_length = 0;
  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) {
  123.             mvwprintw(win, 4, 24, "%-10d", 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 && 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 a modem7
  146.          * or ymodem supplied name, the permission is checked by the
  147.          * rcv_modem7() and rcv_ymodem() routines.
  148.          */
  149.                     /* open the file */
  150.         if (!(fp = fopen(file, "w"))) {
  151.             beep();
  152.             clear_line(win, 12, 24, 1);
  153.             wattrstr(win, A_BOLD, "CAN'T OPEN FILE");
  154.             wrefresh(win);
  155.             cancel_xfer();
  156.             return(1);
  157.         }
  158.                     /* ACK the block 0 */
  159.         if (type == YMODEM || type == YMODEM_G)
  160.             putc_line(ACK);
  161.  
  162.         if (code = send_first(win, max_block, default_err)) {
  163.             fclose(fp);
  164.             cancel_xfer();
  165.             return(code +1);
  166.         }
  167.                     /* here we go... */
  168.         blk = 1;
  169.         block = 1;
  170.         recv = 0;
  171.         got_hdr = 1;
  172.         while (1) {
  173.             code = rcv_block(win, got_hdr, max_block, blk);
  174.  
  175.             if (code < 0) {
  176.                 fclose(fp);
  177.                 cancel_xfer();
  178.                 return(code +1);
  179.             }
  180.             got_hdr = 0;
  181.                     /* are we done? */
  182.             if (buf[0] == EOT) {
  183.                 if (!is_batch) {
  184.                     beep();
  185.                     wrefresh(win);
  186.                     sleep(1);
  187.                 }
  188.                 break;
  189.             }
  190.                     /* if not a duplicate block */
  191.             if (!code) {
  192.                 fwrite((char *) &buf[3], sizeof(buf[0]), block_size, fp);
  193.                 mvwprintw(win, 7, 24, "%-5d", block);
  194.                 if (fast) {
  195.                     recv += block_size;
  196.                     mvwprintw(win, 9, 24, "%-10d", recv);
  197.                 }
  198.                 blk++;
  199.                 block++;
  200.             }
  201.             /*
  202.              * If the length is known, give the same status
  203.              * report as uploading
  204.              */
  205.             if (file_length && fast) {
  206.                 percent = recv * 100.0 / file_length;
  207.                 if (percent > 100.0)
  208.                     percent = 100.0;
  209.                 mvwprintw(win, 8, 24, "%0.1f%%", percent);
  210.             }
  211.             wrefresh(win);
  212.             putc_line(ACK);
  213.         }
  214.         if (file_length && fast) {
  215.             mvwaddstr(win, 8, 24, "100%  ");
  216.             wrefresh(win);
  217.         }
  218.         /*
  219.          * If the file length is not known, then search backwards
  220.          * from the end of the file until you find a character that
  221.          * is not the ^Z padding character
  222.          */
  223.         if (!file_length) {
  224.             for (i=block_size+2; i>2; i--) {
  225.                 if (buf[i] != CTRLZ)
  226.                     break;
  227.             }
  228.             file_length = recv - block_size + i -2;
  229.         }
  230.         fclose(fp);
  231.         fix_length(file_name, file_length);
  232.                     /* ACK the EOT */
  233.         putc_line(ACK);
  234.     }
  235.     return(0);
  236. }
  237.  
  238. /*
  239.  * Send the first character to start the transmission and set the error
  240.  * checking method.  Returns the standard error codes or 0 on success.
  241.  * The variables err_method and block_size are global.
  242.  */
  243.  
  244. int
  245. send_first(win, max_block, default_err)
  246. WINDOW *win;
  247. int max_block, default_err;
  248. {
  249.     int i, err_count;
  250.     unsigned int sleep();
  251.                     /* default error method */
  252.     err_method = default_err;
  253.     if (err_method > 1)
  254.         err_method--;
  255.                     /* send the first char */
  256.     err_count = 0;
  257.     while (err_count < MAX_ERRORS*2) {
  258.         mvwprintw(win, 10, 24, "%-2d", err_count);
  259.  
  260.                     /* check for keyboard abort */
  261.         if (wgetch(win) == 27) {
  262.             beep();
  263.             clear_line(win, 12, 24, 1);
  264.             waddstr(win, "ABORTED");
  265.             wrefresh(win);
  266.             sleep(3);
  267.             return(ABORT);
  268.         }
  269.                     /* switch to checksum? */
  270.         if (default_err == 1 && err_count > MAX_ERRORS)
  271.             err_method = 0;
  272.  
  273.                     /* send error method code */
  274.         clear_line(win, 5, 24, 1);
  275.         switch (err_method) {
  276.             case 0:
  277.                 waddstr(win, "CHECKSUM");
  278.                 putc_line(NAK);
  279.                 break;
  280.             case 1:
  281.                 waddstr(win, "CRC");
  282.                 putc_line('C');
  283.                 break;
  284.             case 2:
  285.                 waddstr(win, "NONE");
  286.                 putc_line('G');
  287.                 break;
  288.         }
  289.         /*
  290.          * We've cut the delay time in half, so we double
  291.          * the allowable errors
  292.          */
  293.         if ((i = getc_line(5)) == -1) {
  294.             err_count++;
  295.             clear_line(win, 12, 24, 1);
  296.             waddstr(win, "NO RESPONSE");
  297.             wrefresh(win);
  298.             continue;
  299.         }
  300.         buf[0] = i;
  301.  
  302.         switch(buf[0]) {
  303.             case SOH:    /* small block follows */
  304.                 block_size = 128;
  305.                 return(0);
  306.             case STX:    /* large block follows */
  307.                 if (max_block == 1024) {
  308.                     block_size = 1024;
  309.                     return(0);
  310.                 }
  311.                 /* fall thru */
  312.             default:
  313.                 err_count++;
  314.                 clear_line(win, 12, 24, 1);
  315.                 waddstr(win, "BAD HEADER");
  316.                 wrefresh(win);
  317.                     /* read some garbage... */
  318.                 fread_line(buf, 1028, 3);
  319.                 putc_line(NAK);
  320.                 break;
  321.         }
  322.     }
  323.     beep();
  324.     clear_line(win, 12, 24, 1);
  325.     wattrstr(win, A_BOLD, "TIMED OUT");
  326.     wrefresh(win);
  327.     return(ERROR);
  328. }
  329.  
  330. /*
  331.  * Receive a block of info from the host.  Returns a 0 on success, a 1 on
  332.  * a duplicate block or the standard error codes.   The variables 
  333.  * err_method and block_size are global.
  334.  */
  335.  
  336. int
  337. rcv_block(win, got_hdr, max_block, blk)
  338. WINDOW *win;
  339. int got_hdr, max_block;
  340. unsigned char blk;
  341. {
  342.     int i, err_count, bad_block, out_of_sync, crc;
  343.     unsigned int packet, sleep();
  344.     unsigned char calc_sum(), crc_1, crc_2;
  345.  
  346.     err_count = 0;
  347.     while (err_count < MAX_ERRORS) {
  348.         mvwprintw(win, 10, 24, "%-2d", err_count);
  349.         mvwprintw(win, 11, 24, "%-3d", tot_err);
  350.  
  351.                     /* scan the keyboard for abort */
  352.         if (wgetch(win) == 27) {
  353.             beep();
  354.             clear_line(win, 12, 24, 1);
  355.             waddstr(win, "ABORTED");
  356.             wrefresh(win);
  357.             sleep(3);
  358.             return(ABORT);
  359.         }
  360.                     /* have we already got a hdr? */
  361.         if (!got_hdr) {
  362.             if ((i = getc_line(10)) == -1) {
  363.                 err_count++;
  364.                 tot_err++;
  365.                 clear_line(win, 12, 24, 1);
  366.                 waddstr(win, "NO RESPONSE");
  367.                 wrefresh(win);
  368.                 continue;
  369.             }
  370.             buf[0] = i;
  371.                     /* what'd we get? */
  372.             switch(buf[0]) {
  373.                 case EOT:    /* we're done! */
  374.                     clear_line(win, 12, 24, 1);
  375.                     waddstr(win, "TRANSFER COMPLETE");
  376.                     wrefresh(win);
  377.                     sleep(1);
  378.                     return(0);
  379.                 case SOH:    /* small block follows */
  380.                     block_size = 128;
  381.                     break;
  382.                 case STX:    /* large block follows */
  383.                     if (max_block == 1024) {
  384.                         block_size = 1024;
  385.                         break;
  386.                     }
  387.                     /* fall thru... */
  388.                 default:
  389.                     err_count++;
  390.                     tot_err++;
  391.                     clear_line(win, 12, 24, 1);
  392.                     waddstr(win, "BAD HEADER");
  393.                     wrefresh(win);
  394.  
  395.                     /* flush a bad packet */
  396.                     fread_line(buf, 1028, 5);
  397.                     putc_line(NAK);
  398.                     continue;
  399.             }
  400.         }
  401.         got_hdr = 0;
  402.                     /* read the rest of the packet */
  403.         packet = block_size + 2 + (err_method ? 2 : 1);
  404.         if (fread_line(&buf[1], packet, 10) == -1) {
  405.             clear_line(win, 12, 24, 1);
  406.             waddstr(win, "TIMED OUT");
  407.             wrefresh(win);
  408.             putc_line(NAK);
  409.             err_count++;
  410.             tot_err++;
  411.             continue;
  412.         }
  413.  
  414.         /*
  415.          * Validation of the packet includes checking the
  416.          * block number, its complement, and the crc/checksum.
  417.          */
  418.         out_of_sync = 0;
  419.         if (buf[1] != blk || buf[2] != (unsigned char) ~blk)
  420.             out_of_sync++;
  421.  
  422.         bad_block = 0;
  423.         switch(err_method) {
  424.             case 0:        /* checksum */
  425.                 if (buf[block_size +3] != calc_sum(&buf[3], block_size))
  426.                     bad_block++;
  427.                 break;
  428.             case 1:        /* CRC */
  429.                 crc = calc_crc(&buf[3], block_size);
  430.                 crc_1 = crc >> 8;
  431.                 crc_2 = crc;
  432.                 if (buf[block_size +3] != crc_1 || buf[block_size +4] != crc_2)
  433.                     bad_block++;
  434.                 break;
  435.             case 2:        /* none */
  436.                 return(0);
  437.         }
  438.                     /* handle errors */
  439.         if (bad_block) {
  440.             clear_line(win, 12, 24, 1);
  441.             if (err_method)
  442.                 waddstr(win, "CRC FAILED");
  443.             else
  444.                 waddstr(win, "CHECKSUM FAILED");
  445.             wrefresh(win);
  446.             putc_line(NAK);
  447.             err_count++;
  448.             tot_err++;
  449.             continue;
  450.         }
  451.                     /* not really an error */
  452.         if (out_of_sync) {
  453.             /*
  454.              * If a perfect packet is off by 1 block number,
  455.              * (a lost ACK could cause this) then treat it as
  456.              * a good block but don't write it to disk.
  457.              */
  458.             if (buf[1] == (unsigned char) blk-1)
  459.                 return(1);
  460.  
  461.             clear_line(win, 12, 24, 1);
  462.             waddstr(win, "OUT OF SYNC");
  463.             wrefresh(win);
  464.             putc_line(NAK);
  465.             err_count++;
  466.             tot_err++;
  467.             continue;
  468.         }
  469.         return(0);
  470.     }
  471.     beep();
  472.     clear_line(win, 12, 24, 1);
  473.     waddstr(win, "TOO MANY ERRORS");
  474.     wrefresh(win);
  475.     return(ERROR);
  476. }
  477.