home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / lora299s.zip / RCVFAX.CPP < prev    next >
C/C++ Source or Header  |  1998-05-12  |  16KB  |  615 lines

  1.  
  2. // LoraBBS Version 2.99 Free Edition
  3. // Copyright (C) 1987-98 Marco Maccaferri
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. #include "_ldefs.h"
  20. #include "lorawin.h"
  21.  
  22. #define ETX          0x03
  23. #define DLE          0x10
  24. #define DC2          0x12
  25.  
  26. /* Constants for Class 2 commands */
  27.  
  28. /* exit codes  */
  29.  
  30. #define FAXSENT      0
  31. #define FAXINSYNC    0
  32. #define FAXNOSYNC    1
  33. #define FAXNODIAL    2
  34. #define FAXBUSY      3
  35. #define FAXHANG      4
  36. #define FAXERROR     5
  37.  
  38. /* My own page reception codes */
  39.  
  40. #define PAGE_GOOD             0
  41. #define ANOTHER_DOCUMENT      1
  42. #define END_OF_DOCUMENT       2
  43. #define PAGE_HANGUP           4
  44. #define PAGE_ERROR            5
  45.  
  46. /********************************************************/
  47. /* Class 2 session parameters                           */
  48.  
  49. /* Set desired transmission params with +FDT=DF,VR,WD,LN
  50.  * DF = Data Format :   0  1-d huffman
  51.  *                      *1 2-d modified Read
  52.  *                      *2 2-d uncompressed mode
  53.  *                      *3 2-d modified modified Read
  54.  *
  55.  * VR = Vertical Res :  0 Normal, 98 lpi
  56.  *                      1 Fine, 196 lpi
  57.  *
  58.  * WD = width :         0  1728 pixels in 215 mm
  59.  *                      *1 2048 pixels in 255 mm
  60.  *
  61.  * LN = page length :   0 A4, 297 mm
  62.  *                      1 B4, 364 mm
  63.  *                      2  Unlimited
  64.  *
  65.  * EC = error correction :      0 disable ECM
  66.  *
  67.  * BF = binary file transfer :  0 disable BFT
  68.  *
  69.  * ST = scan time/line :        VR = normal     VR = fine
  70.  *                         0    0 ms            0 ms
  71.  *
  72.  */
  73.  
  74. /* data format */
  75.  
  76. #define DF_1DHUFFMAN    0
  77. #define DF_2DMREAD      1
  78. #define DF_2DUNCOMP     2
  79. #define DF_2DMMREAD     3
  80.  
  81. /* vertical resolution */
  82.  
  83. #define VR_NORMAL       0
  84. #define VR_FINE         1
  85.  
  86. /* width */
  87.  
  88. #define WD_1728         0
  89. #define WD_2048         1
  90.  
  91. /* page length */
  92.  
  93. #define LN_A4           0
  94. #define LN_B4           1
  95. #define LN_UNLIMITED    2
  96.  
  97. /* Baud rate */
  98.  
  99. #define BR_2400         0
  100. #define BR_4800         1
  101. #define BR_7200         2
  102. #define BR_9600         3
  103.  
  104. TFax::TFax (void)
  105. {
  106.    Format = 0;
  107.    DataPath[0] = '\0';
  108.    swaptableinit = FALSE;
  109.    faxsize = 0L;
  110.    opage = 0;
  111. }
  112.  
  113. TFax::~TFax (void)
  114. {
  115. }
  116.  
  117. /*--------------------------------------------------------------------------*/
  118. /* FAX RECEIVE Routines                                                     */
  119. /*--------------------------------------------------------------------------*/
  120.  
  121. /* receive fax files into basefilename */
  122.  
  123. int TFax::faxreceive (void)
  124. {
  125.    int result, page;
  126.  
  127.    if (!swaptableinit)
  128.       init_swaptable ();
  129.  
  130.    Com->SetParameters (19200, 8, 'N', 1);
  131.  
  132.    init_modem_response ();
  133.    gEnd_of_document = FALSE;
  134.    response.fcon = TRUE;            /* we already connected */
  135.  
  136.    result = 0;
  137.  
  138.    for (page = 0; gEnd_of_document == FALSE; page++) {
  139.       result = get_fax_file (page);
  140.       Log->Write (">FAX get_fax_file returns = %d", result);
  141.       switch ((int) result) {
  142.          case PAGE_GOOD:
  143.             continue;
  144.  
  145.          case PAGE_HANGUP:
  146.             Log->Write (" FAX Received %d pages", page);
  147.             result = 1;
  148.             gEnd_of_document = TRUE;
  149.             break;
  150.  
  151.          default:
  152.             Log->Write (" FAX Error during transmission");
  153.             result = page;
  154.             gEnd_of_document = TRUE;
  155.             break;
  156.       }
  157.    }
  158.  
  159.    return (result);
  160. }
  161.  
  162. /* This executes the +FDR receive page command, and looks for
  163.  * the proper CONNECT response, or, if the document is finished,
  164.  * looks for the FHNG/FHS code.
  165.  *
  166.  * returns:
  167.  *  PAGE_GOOD                no error conditions occured during reception
  168.  *  PAGE_HANGUP              normal end of transmission
  169.  *  PAGE_ERROR               something's wrong
  170.  */
  171.  
  172. int TFax::get_fax_file (int page)
  173. {
  174.    char buf[256], j[100];
  175.    int result, TaskNumber = 1;
  176.    FILE *fp = NULL;
  177.    struct stat statbuf;
  178.  
  179.    Log->Write (">FAX [get_fax_file]");
  180.  
  181.    if (page == 0) {
  182.       do {
  183.          sprintf (buf, "%sfax%03d%02d.fax", DataPath, opage++, page + 1);
  184.       } while (!stat (buf, &statbuf) && (opage < 256));
  185.    }
  186.    else
  187.       sprintf (buf, "%sfax%03d%02d.fax", DataPath, opage, page + 1);
  188.  
  189.    if (opage == 1000) {
  190.       Log->Write ("!FAX Couldn't create output file");
  191.       return (PAGE_ERROR);
  192.    }
  193.  
  194.    if ((result = faxmodem_receive_page ()) == 0) {
  195.       /* filename to create for this page of document */
  196.       if ((fp = fopen (buf, "ab")) == NULL) {
  197.          Log->Write ("!FAX Couldn't create output file %s", buf);
  198.          return (PAGE_ERROR);
  199.       }
  200.  
  201.       if (!page)
  202.          Log->Write (" FAX Connect with %s", response.remote_id);
  203.  
  204.       sprintf (j, "%s %s; page %02x", "FAX Rcv", buf, page);
  205.       result = read_g3_stream (fp);
  206.    }
  207.  
  208.    if (fp != NULL) {
  209.       fclose (fp);
  210.       if (faxsize <= 256L)
  211.          unlink (buf);
  212.       else
  213.          Log->Write (" FAX File received %s (%lub)", buf, faxsize);
  214.    }
  215.  
  216.    return (result);
  217. }
  218.  
  219. /* Reads a data stream from the faxmodem, unstuffing DLE characters.
  220.  * Returns the +FET value (2 if no more pages) or 4 if hangup.
  221.  */
  222.  
  223. int TFax::read_g3_stream (FILE * fp)
  224. {
  225.    short c;
  226.    char e_input_buf[11];
  227.    unsigned char *secbuf, *p;
  228.    long ltimer = 0L;                  /* MB 94-01-01 */
  229.    int pseudo_carrier;                /* MB 94-01-01 */
  230.  
  231.    Log->Write (">FAX [read_g3_stream]");
  232.  
  233.    response.post_page_response_code = -1;    /* reset page codes         */
  234.    response.post_page_message_code = -1;
  235.  
  236.    Com->ClearInbound ();
  237.  
  238.    if ((secbuf = (unsigned char *) calloc (1, 1024)) == NULL)
  239.       goto fax_error;
  240.  
  241.    p = secbuf;
  242.  
  243.    sprintf (e_input_buf, "%lu", faxsize);
  244.    fax_status (e_input_buf);
  245.  
  246.    pseudo_carrier = !(Com->Carrier ());        /* test if modem sets DCD */
  247.    if (pseudo_carrier)
  248.       Log->Write (">FAX modem doesn't assert DCD [read_g3_stream]");
  249.  
  250.    Log->Write (">FAX DC2  [read_g3_stream]");
  251.  
  252.    /* Send DC2 to start phase C data stream */
  253.  
  254.    Com->SendByte ((UCHAR) DC2);
  255.  
  256.    while (pseudo_carrier || Com->Carrier () == TRUE) {
  257.       if (Com->BytesReady () == FALSE) {
  258.          if (pseudo_carrier) {
  259.             if (!ltimer)
  260.                ltimer = TimerSet (1500);    /* 15 secs timeout */
  261.             else if (TimeUp(ltimer))
  262.                goto fax_error;   /* Houston, we lost the downlink   */
  263.          }
  264.          continue;            /* process timeouts */
  265.       }
  266.       else
  267.          ltimer = 0L;         /* reset no char waiting timer */
  268.  
  269.       c = (short)(Com->ReadByte () & 0xff);    /* get a character  */
  270.  
  271.       if (c == DLE) {
  272.          long ltimer2 = 0L;
  273.  
  274.          while (Com->BytesReady () == FALSE) {
  275.             if (!ltimer2)
  276.                ltimer2 = TimerSet (400);
  277.             else if (TimeUp (ltimer2)) {
  278.                faxsize = 0L;
  279.                goto fax_error;      /* give up */
  280.             }
  281.          }
  282.  
  283.          c = Com->ReadByte ();
  284.  
  285.          if (c == ETX)     /* end of stream */
  286.             goto end_page;
  287.       }
  288.  
  289.       *p++ = swaptable[(unsigned char) c];
  290.       faxsize++;
  291.  
  292.       if (!(faxsize % 1024)) {
  293.          sprintf (e_input_buf, "%lu", faxsize);
  294.          fax_status (e_input_buf);
  295.          if (fwrite (secbuf, 1, 1024, fp) != 1024)
  296.             goto fax_error;   /* hoppala */
  297.          p = secbuf;
  298.       }
  299.    }
  300.  
  301. end_page:
  302.  
  303.    if (faxsize % 1024) {
  304.       if (fwrite (secbuf, 1, (size_t) (faxsize % 1024), fp) != (size_t) (faxsize % 1024))
  305.          goto fax_error;      /* hoppala */
  306.       sprintf (e_input_buf, "%lu", faxsize);
  307.       fax_status (e_input_buf);
  308.    }
  309.  
  310.    free (secbuf);
  311.  
  312.    Log->Write (">FAX Waiting for +FET/+FHNG  [read_g3_stream]");
  313.    c = 0;
  314.    while (response.post_page_message_code == -1) {
  315.       get_modem_result_code ();
  316.       c++;
  317.       if ((!response.post_page_response_code) || (c > 5) || (response.error))
  318.          return (PAGE_ERROR);
  319.       if (response.hangup_code != -1)
  320.          return (PAGE_HANGUP);
  321.    }
  322.    return (PAGE_GOOD);
  323.  
  324. fax_error:
  325.    if (secbuf != NULL)
  326.       free (secbuf);
  327.    Log->Write ("!FAX Error receiving page");
  328.    get_modem_result_code ();
  329.    return (PAGE_ERROR);
  330. }
  331.  
  332. /*--------------------------------------------------------------------------*/
  333. /* Class 2 Faxmodem Protocol Functions                                      */
  334. /*                                                                          */
  335. /* Taken from EIA Standards Proposal No. 2388: Proposed New Standard        */
  336. /* "Asynchronous Facsimile DCE Control Standard" (if approved,              */
  337. /* to be published as EIA/TIA-592)                                          */
  338. /*--------------------------------------------------------------------------*/
  339.  
  340. /* reads a line of characters, terminated by a newline */
  341.  
  342. void TFax::get_faxline (char *p, int nbytes, unsigned int wtime)
  343. {
  344.    short c;             /* current modem character   */
  345.    int count = 1;          /* character count (+null)   */
  346.    long t;
  347.    char *resp;
  348.  
  349.    t = TimerSet (wtime);
  350.  
  351.    resp = p;
  352.  
  353.    while ((count < nbytes) && (!TimeUp (t))) {
  354.       if (Com->BytesReady () == FALSE) {
  355.          continue;
  356.       }
  357.       c = (short)(Com->ReadByte () & 0xff); /* get a character           */
  358.       if (c == '\n')
  359.          continue;
  360.       if (c == '\r')
  361.          if (count > 1)
  362.             break;         /* get out                   */
  363.          else
  364.             continue;      /* otherwise just keep going */
  365.       *p++ = (char) c;     /* store the character       */
  366.       ++count;          /* increment the counter     */
  367.    }
  368.  
  369.    *p = '\0';              /* terminate the new string  */
  370. }
  371.  
  372. void TFax::init_swaptable (void)
  373. {
  374.    int i, j;
  375.  
  376.    for (i = 0; i < 256; i++) {
  377.       j = (((i & 0x01) << 7) |
  378.          ((i & 0x02) << 5) |
  379.          ((i & 0x04) << 3) |
  380.          ((i & 0x08) << 1) |
  381.          ((i & 0x10) >> 1) |
  382.          ((i & 0x20) >> 3) |
  383.          ((i & 0x40) >> 5) |
  384.          ((i & 0x80) >> 7));
  385.       swaptable[i] = (unsigned char) j;
  386.    }
  387.    swaptableinit = TRUE;
  388. }
  389.  
  390. /****************************************************************
  391.  * Initialize a faxmodem_response struct
  392.  */
  393.  
  394. void TFax::init_modem_response (void)
  395. {
  396.    response.remote_id[0] = '\0';
  397.    response.fcon = FALSE;
  398.    response.connect = FALSE;
  399.    response.ok = FALSE;
  400.    response.error = FALSE;
  401.    response.hangup_code = -1;
  402.    response.post_page_response_code = -1;
  403.    response.post_page_message_code = -1;
  404.    response.T30.ec = response.T30.bf = 0;
  405. }
  406.  
  407. /* This function parses numeric responses from the faxmodem.
  408.  * It fills in any relevant slots of the faxmodem_response structure.
  409.  */
  410.  
  411. void TFax::get_modem_result_code (void)
  412. {
  413.    char buf[256];
  414.    long t;
  415.  
  416.    Log->Write (">FAX [get_modem_result_code]");
  417.  
  418.    t = TimerSet (400);
  419.  
  420.    while (!TimeUp (t)) {
  421.       buf[0] = '\0';
  422.       get_faxline (buf, 255, 100);
  423.       if (buf[0]) {
  424.          parse_text_response (buf);
  425.          return;
  426.       }
  427.    }
  428.    return;
  429. }
  430.  
  431. void TFax::fax_status (char *str)
  432. {
  433.    str = str;
  434. }
  435.  
  436. void TFax::parse_text_response (char *str)
  437. {
  438.    /* Look for +FCON, +FDCS, +FDIS, +FHNG, +FHS, +FPTS, +FK, +FTSI */
  439.    if (!strnicmp ("+FCON", str, 5)) {
  440.       response.fcon = TRUE;
  441.       fax_status ("+FCON     ");
  442.       return;
  443.    }
  444.  
  445.    if (!strnicmp (str, "OK", 2)) {
  446.       response.ok = TRUE;
  447.       return;
  448.    }
  449.  
  450.    if (!strnicmp (str, "CONNECT", 7)) {
  451.       response.connect = TRUE;
  452.       return;
  453.    }
  454.  
  455.    if (!strnicmp (str, "NO CARRIER", 10) || !strnicmp (str, "ERROR", 5)) {
  456.       response.error = TRUE;
  457.       response.hangup_code = 0;
  458.       return;
  459.    }
  460.  
  461.    if (!strnicmp (str, "+FDCS", 5)) {
  462.       sscanf (str + 6, "%d,%d,%d,%d,%d,%d,%d,%d",
  463.          &response.T30.vr, &response.T30.br, &response.T30.wd,
  464.          &response.T30.ln, &response.T30.df, &response.T30.ec,
  465.          &response.T30.bf, &response.T30.st);
  466.       fax_status ("+FDCS     ");
  467.       return;
  468.    }
  469.  
  470.    if (!strnicmp (str, "+FHNG", 5)) {
  471.       sscanf (str + 6, "%d", &response.hangup_code);
  472.       fax_status ("+FHNG     ");
  473.       return;
  474.    }
  475.  
  476.    if (!strnicmp (str, "+FPTS", 5)) {
  477.       sscanf (str + 6, "%d", &response.post_page_response_code);
  478.       fax_status ("+FPTS     ");
  479.       return;
  480.    }
  481.  
  482.    if (!strnicmp (str, "+FTSI", 5)) {
  483.       strcpy (response.remote_id, str + 6);
  484.       fax_status ("+FTSI     ");
  485.       return;
  486.    }
  487.  
  488.    if (!strnicmp (str, "+FET", 4)) {
  489.       sscanf (str + 5, "%d", &response.post_page_message_code);
  490.       fax_status ("+FET      ");
  491.       return;
  492.    }
  493.  
  494.    if (!strnicmp (str, "+FHS", 4)) {
  495.       sscanf (str + 5, "%d", &response.hangup_code);
  496.       fax_status ("+FHS      ");
  497.       return;
  498.    }
  499.  
  500.    if (!strnicmp (str, "+FCS", 4)) {
  501.       sscanf (str + 5, "%d,%d,%d,%d,%d,%d,%d,%d",
  502.          &response.T30.vr, &response.T30.br, &response.T30.wd,
  503.          &response.T30.ln, &response.T30.df, &response.T30.ec,
  504.          &response.T30.bf, &response.T30.st);
  505.       fax_status ("+FCS      ");
  506.       return;
  507.    }
  508.  
  509.    if (!strnicmp (str, "+FPS", 4)) {
  510.       sscanf (str + 5, "%d", &response.post_page_response_code);
  511.       fax_status ("+FPS      ");
  512.       return;
  513.    }
  514.  
  515.    if (!strnicmp (str, "+FTI", 4)) {
  516.       strcpy (response.remote_id, str + 5);
  517.       fax_status ("+FTI      ");
  518.       return;
  519.    }
  520.  
  521. }
  522.  
  523. /****************************************************************
  524.  * Action Commands
  525.  */
  526.  
  527. /* Receive a page
  528.  * after receiving OK,
  529.  * send +FDR
  530.  * This is somewhat ugly, because the FDR command can return
  531.  * a couple of possible results;
  532.  * If the connection is valid, it returns something like
  533.  *  +FCFR
  534.  *  +FDCS: <params>
  535.  *  CONNECT
  536.  *
  537.  * If, on the other hand, the other machine has hung up, it returns
  538.  * +FHNG: <code>  or
  539.  * +FHS: <code>
  540.  *
  541.  * and if the connection was never made at all, it returns ERROR (actually numeric
  542.  * code 4)
  543.  *
  544.  * faxmodem_receive_page returns values:
  545.  * PAGE_GOOD     page reception OK, data coming
  546.  * PAGE_HANGUP   normal hangup
  547.  * PAGE_ERROR    page error
  548.  */
  549.  
  550. int TFax::faxmodem_receive_page (void)
  551. {
  552.    long t;
  553.    char buf[100];
  554.  
  555.    faxsize = 0L;
  556.    response.connect = response.ok = FALSE;
  557.  
  558.   /* We wait until a string "OK" is seen
  559.    * or a "+FHNG"
  560.    * or a "ERROR" or "NO CARRIER"
  561.    * or until 10 seconds for a response.
  562.    */
  563.  
  564.    t = TimerSet (1000);
  565.  
  566.    Log->Write (">FAX Waiting for OK  [faxmodem_receive_page]");
  567.  
  568.    while (!TimeUp (t) && (!response.ok)) {
  569.       get_faxline (buf, 100, 500);
  570.       if (buf[0] != '\0')
  571.          Log->Write ("> Response from peer: %s", buf);
  572.       parse_text_response (buf);
  573.  
  574.       if (response.hangup_code != -1)
  575.          return (PAGE_HANGUP);
  576.  
  577.       if (response.error)
  578.          return (PAGE_ERROR);
  579.    }
  580.  
  581.    if (!response.ok)
  582.       return (PAGE_ERROR);
  583.  
  584.    Com->SendBytes ((UCHAR *)"AT+FDR\r", 7);
  585.    Log->Write (">FAX AT+FDR  [faxmodem_receive_page]");
  586.  
  587.    /* We wait until either a string "CONNECT" is seen
  588.     * or a "+FHNG"
  589.     * or until 10 seconds for a response.
  590.     */
  591.  
  592.    t = TimerSet (1000);
  593.  
  594.    Log->Write (">FAX Waiting for CONNECT  [faxmodem_receive_page]");
  595.  
  596.    while (!TimeUp (t)) {
  597.       get_faxline (buf, 100, 500);
  598.       if (buf[0] != '\0')
  599.          Log->Write ("> Response from peer: %s", buf);
  600.       parse_text_response (buf);
  601.  
  602.       if (response.connect == TRUE)
  603.          return (PAGE_GOOD);
  604.  
  605.       if (response.hangup_code != -1)
  606.          return (PAGE_HANGUP);
  607.  
  608.       if (response.error)
  609.          return (PAGE_ERROR);
  610.    }
  611.  
  612.    return (PAGE_ERROR);
  613. }
  614.  
  615.