/*--------------------------------------------------------------------------*/ /* */ /* */ /* ------------ Bit-Bucket Software, Co. */ /* \ 10001101 / Writers and Distributors of */ /* \ 011110 / Freely Available Software. */ /* \ 1011 / */ /* ------ */ /* */ /* (C) Copyright 1987-96, Bit Bucket Software Co. */ /* */ /* This module originally written by Michael Buenter */ /* Original UNIX sources Henry Minsky 11/02/90 hqm@ai.mit.edu */ /* BinkleyTerm FAX file reception module */ /* */ /* */ /* For complete details of the licensing restrictions, please refer */ /* to the License agreement, which is published in its entirety in */ /* the MAKEFILE and BT.C, and also contained in the file LICENSE.260. */ /* */ /* USE OF THIS FILE IS SUBJECT TO THE RESTRICTIONS CONTAINED IN THE */ /* BINKLEYTERM LICENSING AGREEMENT. IF YOU DO NOT FIND THE TEXT OF */ /* THIS AGREEMENT IN ANY OF THE AFOREMENTIONED FILES, OR IF YOU DO */ /* NOT HAVE THESE FILES, YOU SHOULD IMMEDIATELY CONTACT BIT BUCKET */ /* SOFTWARE CO. AT ONE OF THE ADDRESSES LISTED BELOW. IN NO EVENT */ /* SHOULD YOU PROCEED TO USE THIS FILE WITHOUT HAVING ACCEPTED THE */ /* TERMS OF THE BINKLEYTERM LICENSING AGREEMENT, OR SUCH OTHER */ /* AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO. */ /* */ /* */ /* You can contact Bit Bucket Software Co. at any one of the following */ /* addresses: */ /* */ /* Bit Bucket Software Co. FidoNet 1:104/501, 1:343/491 */ /* P.O. Box 460398 AlterNet 7:42/1491 */ /* Aurora, CO 80046 BBS-Net 86:2030/1 */ /* Internet f491.n343.z1.fidonet.org */ /* */ /* Please feel free to contact us at any time to share your comments about */ /* our software and/or licensing policies. */ /* */ /*--------------------------------------------------------------------------*/ /* Include this file before any other includes or defines! */ #include "includes.h" #include "faxproto.h" /*--------------------------------------------------------------------------*/ /* Local routines */ /*--------------------------------------------------------------------------*/ static int LOCALFUNC get_fax_file (int); static int LOCALFUNC read_g3_stream (FILE *, int); static void LOCALFUNC get_faxline (char *, int, unsigned int); static void LOCALFUNC init_swaptable (void); static void LOCALFUNC init_modem_response (void); static void LOCALFUNC get_modem_result_code (void); static void LOCALFUNC parse_text_response (char *); static int LOCALFUNC faxmodem_receive_page (int); static void LOCALFUNC fax_status (char *); /*--------------------------------------------------------------------------*/ /* Private data */ /*--------------------------------------------------------------------------*/ static int gEnd_of_document; static unsigned char swaptable[256]; static int swaptableinit = FALSE; static struct faxmodem_response response; static unsigned long faxsize = 0L; /*--------------------------------------------------------------------------*/ /* FAX RECEIVE Routines */ /*--------------------------------------------------------------------------*/ /* receive fax files into basefilename */ int faxreceive (int zmode) { int result; int page; if (!fax_in) return (0); happy_compiler = zmode; if (!swaptableinit) init_swaptable (); (void) time (&etm); if (fax_baud != -1) { baud = fax_baud; cur_baud = pbtypes[baud]; MDM_ENABLE (cur_baud.rate_mask); (void) sprintf (junk, "%-6lu Com%d", cur_baud.rate_value, port_ptr + 1); sb_move (settingswin, SET_PORT_ROW, SET_COL); sb_puts (settingswin, junk); } do_ready ("FAX Rcv "); (void) init_modem_response (); gEnd_of_document = FALSE; response.fcon = TRUE; /* we already connected */ result = 0; for (page = 0; gEnd_of_document == FALSE; page++) { result = get_fax_file (page); status_line (">FAX get_fax_file returns = %d", result); switch ((int) result) { case PAGE_GOOD: continue; case PAGE_HANGUP: status_line (" FAX Received %d pages", page); result = 1; gEnd_of_document = TRUE; break; default: status_line (" FAX Error during transmission"); result = page; gEnd_of_document = TRUE; break; } } set_baud (max_baud.rate_value, 0); (void) sprintf (junk, "%-6lu", max_baud.rate_value); sb_move (settingswin, SET_PORT_ROW, SET_COL); sb_puts (settingswin, junk); return result; } /* This executes the +FDR receive page command, and looks for * the proper CONNECT response, or, if the document is finished, * looks for the FHNG/FHS code. * * returns: * PAGE_GOOD no error conditions occured during reception * PAGE_HANGUP normal end of transmission * PAGE_ERROR something's wrong */ static int LOCALFUNC get_fax_file (int page) { char buf[256], j[100]; int result; FILE *fp = NULL; int opage = page; status_line (">FAX [get_fax_file]"); do { if (TaskNumber) sprintf (buf, "%sPAGE%02x%02x.FAX", fax_in, (TaskNumber & 0xff), opage++); else sprintf (buf, "%sPAGE%04x.FAX", fax_in, opage++); } while (dexists (buf) && (opage < 256)); if (opage == 256) { status_line ("!FAX Couldn't create output file"); return (PAGE_ERROR); } if ((result = faxmodem_receive_page (page)) == 0) { /* filename to create for this page of document */ if ((fp = fopen (buf, write_binary)) == NULL) { status_line ("!FAX Couldn't create output file %s", buf); return (PAGE_ERROR); } if (!page) status_line (" FAX Connect with %s", response.remote_id); (void) sprintf (j, "%s %s; page %02x", "FAX Rcv", buf, page); if (un_attended && fullscreen) { clear_filetransfer (); sb_move (filewin, 1, 2); sb_puts (filewin, j); (void) sprintf (j, " vr%d br%d wd%d ln%d df%d ec%d bf%d st%d", response.T30.vr, response.T30.br, response.T30.wd, response.T30.ln, response.T30.df, response.T30.ec, response.T30.bf, response.T30.st); sb_move (filewin, 2, 40); sb_puts (filewin, j); status_line (j); elapse_time (); } else { set_xy (j); set_xy (NULL); locate_x += 2; } result = read_g3_stream (fp, page); } if (fp != NULL) { fclose (fp); if (faxsize <= 256L) unlink (buf); else status_line (" FAX File received %s (%lub)", buf, faxsize); } return (result); } /* Reads a data stream from the faxmodem, unstuffing DLE characters. * Returns the +FET value (2 if no more pages) or 4 if hangup. */ static int LOCALFUNC read_g3_stream (FILE * fp, int page) { register short c; char e_input_buf[11]; unsigned char *secbuf, *p; long ltimer = 0L; /* MB 94-01-01 */ int pseudo_carrier; /* MB 94-01-01 */ status_line (">FAX [read_g3_stream]"); happy_compiler = page; /* Make compiler happy */ response.post_page_response_code = -1; /* reset page codes */ response.post_page_message_code = -1; CLEAR_INBOUND (); /* flush echoes or return codes */ if ((secbuf = (unsigned char *) calloc (1, 1024)) == NULL) goto fax_error; p = secbuf; (void) fax_status (ultoa (faxsize, e_input_buf, 10)); pseudo_carrier = !(CARRIER); /* test if modem sets DCD */ if (pseudo_carrier) status_line (">FAX modem doesn't assert DCD [read_g3_stream]"); status_line (">FAX DC2 [read_g3_stream]"); /* Send DC2 to start phase C data stream */ SENDBYTE ((unsigned char) DC2); while (pseudo_carrier || CARRIER) /* data only when carrier high */ { if (!CHAR_AVAIL ()) /* if nothing ready,*/ { if (pseudo_carrier) /* MB 94-01-01 */ { /* process timeout if modem does not */ /* set DCD, this is only a kludge, but */ /* it could prevent an endless loop */ if (!ltimer) ltimer = timerset (1500); /* 15 secs timeout */ else if (timeup(ltimer)) goto fax_error; /* Houston, we lost the downlink */ } time_release (); continue; /* process timeouts */ } else ltimer = 0L; /* reset no char waiting timer */ c = MODEM_IN () & 0xff; /* get a character */ if (c == DLE) /* DLE handling */ { long ltimer2 = 0L; while (!CHAR_AVAIL ()) { if (!ltimer2) ltimer2 = timerset (400); else if (timeup (ltimer2)) { faxsize = 0L; goto fax_error; /* give up */ } } c = TIMED_READ (0); if (c == ETX) /* end of stream */ goto end_page; /* DLE DLE gives DLE. We don't know what to do if it isn't ETX (above) or DLE. So we'll just always treat DLE (not ETX) as (not ETX). Fall out of here into storage. */ } *p++ = swaptable[(unsigned char) c]; faxsize++; if (!(faxsize % 1024)) { (void) fax_status (ultoa (faxsize, e_input_buf, 10)); if (fwrite (secbuf, 1, 1024, fp) != 1024) { goto fax_error; /* hoppala */ } p = secbuf; time_release (); } } end_page: if (faxsize % 1024) { if (fwrite (secbuf, 1, (size_t) (faxsize % 1024), fp) != (size_t) (faxsize % 1024)) goto fax_error; /* hoppala */ (void) fax_status (ultoa (faxsize, e_input_buf, 10)); } free (secbuf); status_line (">FAX Waiting for +FET/+FHNG [read_g3_stream]"); c = 0; while (response.post_page_message_code == -1) /* wait for +FET */ { (void) get_modem_result_code (); c++; if ((!response.post_page_response_code) || (c > 5) || (response.error)) return (PAGE_ERROR); if (response.hangup_code != -1) return (PAGE_HANGUP); } return (PAGE_GOOD); fax_error: if (secbuf != NULL) free (secbuf); status_line ("!FAX Error receiving page"); (void) get_modem_result_code (); return (PAGE_ERROR); } /*--------------------------------------------------------------------------*/ /* Class 2 Faxmodem Protocol Functions */ /* */ /* Taken from EIA Standards Proposal No. 2388: Proposed New Standard */ /* "Asynchronous Facsimile DCE Control Standard" (if approved, */ /* to be published as EIA/TIA-592) */ /*--------------------------------------------------------------------------*/ /* reads a line of characters, terminated by a newline */ static void LOCALFUNC get_faxline (char *p, int nbytes, unsigned int wtime) { short c; /* current modem character */ int count = 1; /* character count (+null) */ long t; char *resp; t = timerset (wtime); resp = p; while ((count < nbytes) /* until we have n bytes, */ && (!timeup (t))) /* or out of time */ { if (!CHAR_AVAIL ()) /* if nothing ready yet, */ { time_release (); continue; /* just process timeouts */ } c = MODEM_IN () & 0xff; /* get a character */ if (c == '\n') continue; if (c == '\r') if (count > 1) break; /* get out */ else continue; /* otherwise just keep going */ *p++ = (char) c; /* store the character */ ++count; /* increment the counter */ } *p = '\0'; /* terminate the new string */ if (debugging_log && (count > 1) && strnicmp (resp, "AT", 2)) status_line (">FAX %s", resp); /* pop it on screen */ } static void LOCALFUNC init_swaptable (void) { int i, j; for (i = 0; i < 256; i++) { /* swap the low order 4 bits with the high order */ j = (((i & 0x01) << 7) | ((i & 0x02) << 5) | ((i & 0x04) << 3) | ((i & 0x08) << 1) | ((i & 0x10) >> 1) | ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | ((i & 0x80) >> 7)); swaptable[i] = (unsigned char) j; } swaptableinit = TRUE; } /**************************************************************** * Initialize a faxmodem_response struct */ static void LOCALFUNC init_modem_response (void) { response.remote_id[0] = '\0'; response.fcon = FALSE; response.connect = FALSE; response.ok = FALSE; response.error = FALSE; response.hangup_code = -1; response.post_page_response_code = -1; response.post_page_message_code = -1; response.T30.ec = response.T30.bf = 0; } /* This function parses numeric responses from the faxmodem. * It fills in any relevant slots of the faxmodem_response structure. */ static void LOCALFUNC get_modem_result_code (void) { char buf[256]; long t; status_line (">FAX [get_modem_result_code]"); t = timerset (400); while (!timeup (t)) { buf[0] = '\0'; (void) get_faxline (buf, 255, 100); if (buf[0]) { (void) parse_text_response (buf); return; } } return; } static void LOCALFUNC fax_status (char *str) { if (fullscreen && un_attended) { sb_move (filewin, 2, 2); sb_puts (filewin, str); elapse_time (); } else { gotoxy (locate_x, locate_y); (void) cputs (str); } } static void LOCALFUNC parse_text_response (char *str) { /* Look for +FCON, +FDCS, +FDIS, +FHNG, +FHS, +FPTS, +FK, +FTSI */ if (!strnicmp ("+FCO", str, 4)) { response.fcon = TRUE; (void) fax_status ("+FCO "); return; } if (!strnicmp (str, "OK", 2)) { response.ok = TRUE; return; } if (!strnicmp (str, "CONNECT", 7)) { response.connect = TRUE; return; } if (!strnicmp (str, "NO CARRIER", 10) || !strnicmp (str, "ERROR", 5)) { response.error = TRUE; response.hangup_code = 0; return; } if (!strnicmp (str, "+FDCS", 5)) { sscanf (str + 6, "%d,%d,%d,%d,%d,%d,%d,%d", &response.T30.vr, &response.T30.br, &response.T30.wd, &response.T30.ln, &response.T30.df, &response.T30.ec, &response.T30.bf, &response.T30.st); (void) fax_status ("+FDCS "); return; } if (!strnicmp (str, "+FHNG", 5)) { sscanf (str + 6, "%d", &response.hangup_code); (void) fax_status ("+FHNG "); return; } if (!strnicmp (str, "+FPTS", 5)) { sscanf (str + 6, "%d", &response.post_page_response_code); (void) fax_status ("+FPTS "); return; } if (!strnicmp (str, "+FTSI", 5)) { (void) strcpy (response.remote_id, str + 6); (void) fax_status ("+FTSI "); return; } if (!strnicmp (str, "+FET", 4)) { sscanf (str + 5, "%d", &response.post_page_message_code); (void) fax_status ("+FET "); return; } if (!strnicmp (str, "+FHS", 4)) /* Class 2.0 */ { sscanf (str + 5, "%d", &response.hangup_code); (void) fax_status ("+FHS "); return; } if (!strnicmp (str, "+FCS", 4)) /* Class 2.0 */ { sscanf (str + 5, "%d,%d,%d,%d,%d,%d,%d,%d", &response.T30.vr, &response.T30.br, &response.T30.wd, &response.T30.ln, &response.T30.df, &response.T30.ec, &response.T30.bf, &response.T30.st); (void) fax_status ("+FCS "); return; } if (!strnicmp (str, "+FPS", 4)) /* Class 2.0 */ { sscanf (str + 5, "%d", &response.post_page_response_code); (void) fax_status ("+FPS "); return; } if (!strnicmp (str, "+FTI", 4)) /* Class 2.0 */ { (void) strcpy (response.remote_id, str + 5); (void) fax_status ("+FTI "); return; } } /**************************************************************** * Action Commands */ /* Receive a page * after receiving OK, * send +FDR * This is somewhat ugly, because the FDR command can return * a couple of possible results; * If the connection is valid, it returns something like * +FCFR * +FDCS: * CONNECT * * If, on the other hand, the other machine has hung up, it returns * +FHNG: or * +FHS: * * and if the connection was never made at all, it returns ERROR (actually numeric * code 4) * * faxmodem_receive_page returns values: * PAGE_GOOD page reception OK, data coming * PAGE_HANGUP normal hangup * PAGE_ERROR page error */ static int LOCALFUNC faxmodem_receive_page (int page) { long t; char buf[100]; faxsize = 0L; response.connect = response.ok = FALSE; /* We wait until a string "OK" is seen * or a "+FHNG" * or a "ERROR" or "NO CARRIER" * or until 10 seconds for a response. */ t = timerset (1000); status_line (">FAX Waiting for OK [faxmodem_receive_page]"); while (!timeup (t) && (!response.ok)) { (void) get_faxline (buf, 100, 100); status_line ("> Response from peer: %s", buf); (void) parse_text_response (buf); if (response.hangup_code != -1) return (PAGE_HANGUP); if (response.error) return (PAGE_ERROR); } if (!response.ok) return (PAGE_ERROR); SENDCHARS ("AT+FDR\r", 7, 1); status_line (">FAX AT+FDR [faxmodem_receive_page]"); /* We wait until either a string "CONNECT" is seen * or a "+FHNG" * or until 10 seconds for a response. */ t = timerset (1000); status_line (">FAX Waiting for CONNECT [faxmodem_receive_page]"); while (!timeup (t)) { (void) get_faxline (buf, 100, 100); status_line ("> Response from peer: %s", buf); (void) parse_text_response (buf); if (response.connect == TRUE) return (PAGE_GOOD); if (response.hangup_code != -1) return (PAGE_HANGUP); if (response.error) return (PAGE_ERROR); } return (PAGE_ERROR); }