Class2Recv.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:11k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: Class2Recv.c++,v 1.12 2008/05/03 00:22:23 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1990-1996 Sam Leffler
  4.  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. #include <stdio.h>
  27. #include "Class2.h"
  28. #include "ModemConfig.h"
  29. #include "Sys.h"
  30. #include "t.30.h"
  31. /*
  32.  * Recv Protocol for Class-2-style modems.
  33.  */
  34. static const AnswerMsg answerMsgs[] = {
  35. { "+FCO", 4,
  36.   FaxModem::AT_NOTHING, FaxModem::OK,     FaxModem::CALLTYPE_FAX },
  37. { "+FDM", 4,
  38.   FaxModem::AT_NOTHING, FaxModem::OK,     FaxModem::CALLTYPE_DATA },
  39. { "VCON", 4,
  40.   FaxModem::AT_NOTHING, FaxModem::OK,     FaxModem::CALLTYPE_VOICE },
  41. };
  42. #define NANSWERS (sizeof (answerMsgs) / sizeof (answerMsgs[0]))
  43. const AnswerMsg*
  44. Class2Modem::findAnswer(const char* s)
  45. {
  46.     for (u_int i = 0; i < NANSWERS; i++)
  47. if (strneq(s, answerMsgs[i].msg, answerMsgs[i].len))
  48.     return (&answerMsgs[i]);
  49.     return FaxModem::findAnswer(s);
  50. }
  51. /*
  52.  * Begin a fax receive session.
  53.  */
  54. bool
  55. Class2Modem::recvBegin(FaxSetup* clientinfo, fxStr& emsg)
  56. {
  57.     bool status = false;
  58.     hangupCode[0] = '';
  59.     hadHangup = false;
  60.     ATResponse r;
  61.     do {
  62. switch (r = atResponse(rbuf, 3*60*1000)) {
  63. case AT_NOANSWER:
  64. case AT_NOCARRIER:
  65. case AT_NODIALTONE:
  66. case AT_ERROR:
  67. case AT_TIMEOUT:
  68. case AT_EMPTYLINE:
  69.     processHangup("70");
  70.     emsg = fxStr::format("%s {%s}", hangupCause(hangupCode), hangupCause(hangupCode, true));
  71.     return (false);
  72. case AT_FNSS:
  73.     // XXX parse and pass on to server
  74.     break;
  75. case AT_FSA:
  76.     recvSUB(stripQuotes(skipStatus(rbuf)));
  77.     break;
  78. #ifdef notdef
  79. case AT_FPA:
  80.     recvSEP(stripQuotes(skipStatus(rbuf)));
  81.     break;
  82. #endif
  83. case AT_FPW:
  84.     recvPWD(stripQuotes(skipStatus(rbuf)));
  85.     break;
  86. case AT_FTSI:
  87.     recvTSI(stripQuotes(skipStatus(rbuf)));
  88.     break;
  89. case AT_FDCS:
  90.     status = recvDCS(rbuf);
  91.     break;
  92. case AT_FHNG:
  93.     status = false;
  94.     break;
  95. }
  96.     } while (r != AT_OK);
  97.     if (!status)
  98. emsg = fxStr::format("%s {%s}", hangupCause(hangupCode), hangupCause(hangupCode, true));
  99.     return (status);
  100. }
  101. /*
  102.  * Begin a fax receive session after EOM.
  103.  */
  104. bool
  105. Class2Modem::recvEOMBegin(FaxSetup* infosetup, fxStr& emsg)
  106. {
  107.     /*
  108.      * There's nothing to do because the modem
  109.      * does all of the protocol work.
  110.      */
  111.     return (true);
  112. }
  113. /*
  114.  * Process a received DCS.
  115.  */
  116. bool
  117. Class2Modem::recvDCS(const char* cp)
  118. {
  119.     if (parseClass2Capabilities(skipStatus(cp), params, false)) {
  120. params.update(false);
  121. setDataTimeout(60, params.br);
  122. FaxModem::recvDCS(params); // announce session params
  123. return (true);
  124.     } else { // protocol botch
  125. processHangup("72"); // XXX "COMREC error"
  126. return (false);
  127.     }
  128. }
  129. /*
  130.  * Signal that we're ready to receive a page
  131.  * and then collect the data.  Return the
  132.  * received post-page-message.
  133.  */
  134. bool
  135. Class2Modem::recvPage(TIFF* tif, u_int& ppm, fxStr& emsg, const fxStr& id)
  136. {
  137.     int ppr;
  138.     bool prevPage = false;
  139.     bool pageGood = false; 
  140.     pageStarted = false;
  141.     do {
  142. ppm = PPM_EOP;
  143. hangupCode[0] = 0;
  144. if (!atCmd("AT+FDR", AT_NOTHING))
  145.     goto bad;
  146. /*
  147.  * The spec says the modem is supposed to return CONNECT
  148.  * in response, but some modems such as the PPI PM14400FXMT
  149.  * PM28800FXMT return OK instead in between documents
  150.  * (i.e. when the previous page was punctuated with EOM).
  151.  */
  152. ATResponse r;
  153. do {
  154.     switch (r = atResponse(rbuf, conf.pageStartTimeout)) {
  155.     case AT_FDCS: // inter-page DCS
  156. if (!pageGood) recvResetPage(tif);
  157. (void) recvDCS(rbuf);
  158. break;
  159.     case AT_FTSI:
  160. if (!pageGood) recvResetPage(tif);
  161. recvTSI(stripQuotes(skipStatus(rbuf)));
  162. break;
  163. case AT_FSA:
  164. if (!pageGood) recvResetPage(tif);
  165. recvSUB(stripQuotes(skipStatus(rbuf)));
  166. break;
  167. #ifdef notdef
  168. case AT_FPA:
  169. if (!pageGood) recvResetPage(tif);
  170. recvSEP(stripQuotes(skipStatus(rbuf)));
  171. break;
  172. #endif
  173. case AT_FPW:
  174. if (!pageGood) recvResetPage(tif);
  175. recvPWD(stripQuotes(skipStatus(rbuf)));
  176. break;
  177.     case AT_TIMEOUT:
  178.     case AT_EMPTYLINE:
  179.     case AT_ERROR:
  180.     case AT_NOCARRIER:
  181.     case AT_NODIALTONE:
  182.     case AT_NOANSWER:
  183. goto bad;
  184.     case AT_FHNG: // remote hangup
  185. waitFor(AT_OK);
  186. goto bad;
  187.     }
  188. } while (r != AT_CONNECT && r != AT_OK);
  189. protoTrace("RECV: begin page");
  190. /*
  191.  * NB: always write data in LSB->MSB for folks that
  192.  *     don't understand the FillOrder tag!
  193.  */
  194. recvSetupTIFF(tif, group3opts, FILLORDER_LSB2MSB, id);
  195. if (!recvPageData(tif, emsg)) {
  196.     prevPage = false;
  197.     goto bad;
  198. }
  199. else {
  200.     prevPage = true;
  201.     if (!recvPPM(tif, ppr))
  202. goto bad;
  203. }
  204. if (!waitFor(AT_FET)) // post-page message status
  205.     goto bad;
  206. ppm = atoi(skipStatus(rbuf));
  207. tracePPM("RECV recv", ppm);
  208. // synchronization from modem
  209. /*
  210.  * Class 2 modems should always respond with OK.
  211.  * Class 2.0 modems may respond with ERROR if copy
  212.  * quality checking indicates the page is bad.
  213.  */
  214. if (!(waitFor(AT_OK) || lastResponse == AT_ERROR))
  215.     goto bad;
  216. /*
  217.  * T.30 says to process operator intervention requests
  218.  * here rather than before the page data is received.
  219.  * This has the benefit of not recording the page as
  220.  * received when the post-page response might need to
  221.  * be retransmited.
  222.  */
  223. if (abortRequested()) {
  224.     // XXX no way to purge TIFF directory
  225.     emsg = "Receive aborted due to operator intervention {E301}";
  226.     return (false);
  227. }
  228. // XXX deal with PRI interrupts
  229. /*
  230.  * If the host did the copy quality checking,
  231.  * then override the modem-specified post-page
  232.  * response according to the quality of the
  233.  * received page.
  234.  */
  235. if (hostDidCQ)
  236.     ppr = isQualityOK(params) ? PPR_MCF : PPR_RTN;
  237. #if 0
  238.         /*
  239.          * RTN debug code: always respond with RTN to sending facsimile
  240.          */
  241.         ppr = PPR_RTN;
  242. #endif
  243. if (ppr & 1) {
  244.     pageGood = true;
  245.     TIFFWriteDirectory(tif); // complete page write
  246. } else
  247.     pageGood = false;
  248. tracePPR("RECV send", ppr);
  249. if (ppr & 1) // page good, work complete
  250.     return (true);
  251.     } while (!hostDidCQ || class2Cmd(ptsCmd, ppr));
  252. bad:
  253.     if (hangupCode[0] == 0)
  254. processHangup("90"); // "Unspecified Phase C error"
  255.     emsg = fxStr::format("%s {%s}", hangupCause(hangupCode), hangupCause(hangupCode, true));
  256.     if (prevPage && conf.saveUnconfirmedPages) {
  257. TIFFWriteDirectory(tif);
  258. protoTrace("RECV keeping unconfirmed page");
  259. return (true);
  260.     }
  261.     return (false);
  262. }
  263. void
  264. Class2Modem::abortPageRecv()
  265. {
  266.     char c = CAN;
  267.     putModem(&c, 1, 1);
  268. }
  269. /*
  270.  * Receive Phase C data using the Class 2 ``stream interface''.
  271.  */
  272. bool
  273. Class2Modem::recvPageData(TIFF* tif, fxStr& emsg)
  274. {
  275.     // be careful about flushing here -- otherwise we can lose +FDB messages
  276.     if (flowControl == FLOW_XONXOFF)
  277. (void) setXONXOFF(FLOW_NONE, FLOW_XONXOFF, ACT_DRAIN);
  278.     protoTrace("RECV: send trigger 0%o", recvDataTrigger&0377);
  279.     (void) putModem(&recvDataTrigger, 1); // initiate data transfer
  280.     /*
  281.      * Have host do copy quality checking if the modem does not
  282.      * support checking for this data format and if the configuration
  283.      * parameters indicate CQ checking is to be done.
  284.      *
  285.      * If the modem is performing copy quality correction then
  286.      * the host cannot perform copy quality checking.
  287.      */
  288.     if (serviceType == SERVICE_CLASS2)
  289. hostDidCQ = (modemCQ & BIT(params.df)) == 0 && checkQuality();
  290.     else
  291. hostDidCQ = modemCQ == 0 && checkQuality();
  292.     protoTrace("Copy quality checking performed by %s", hostDidCQ ? "host" : "modem");
  293.     // JBIG needs the data bit-reversed
  294.     if (params.df == DF_JBIG) {
  295. if (recvFillOrder == FILLORDER_LSB2MSB)
  296.     recvFillOrder = FILLORDER_MSB2LSB;
  297. else
  298.     recvFillOrder = FILLORDER_LSB2MSB;
  299.     }
  300.     bool pageRecvd = recvPageDLEData(tif, hostDidCQ, params, emsg);
  301.     // Return recvFillOrder to its configured setting
  302.     if (params.df == DF_JBIG) {
  303. if (recvFillOrder == FILLORDER_LSB2MSB)
  304.     recvFillOrder = FILLORDER_MSB2LSB;
  305. else
  306.     recvFillOrder = FILLORDER_LSB2MSB;
  307.     }
  308.     // be careful about flushing here -- otherwise we lose +FPTS codes
  309.     if (flowControl == FLOW_XONXOFF)
  310. (void) setXONXOFF(FLOW_XONXOFF, getInputFlow(), ACT_DRAIN);
  311.     if (!pageRecvd)
  312. processHangup("91"); // "Missing EOL after 5 seconds"
  313.     return (pageRecvd);
  314. }
  315. bool
  316. Class2Modem::recvPPM(TIFF* tif, int& ppr)
  317. {
  318.     for (;;) {
  319. switch (atResponse(rbuf, conf.pageDoneTimeout)) {
  320. case AT_OK:
  321.     protoTrace("MODEM protocol botch: OK without +FPTS:");
  322.     /* fall thru... */
  323. case AT_TIMEOUT:
  324. case AT_EMPTYLINE:
  325. case AT_NOCARRIER:
  326. case AT_NODIALTONE:
  327. case AT_NOANSWER:
  328. case AT_ERROR:
  329.     processHangup("50");
  330.     return (false);
  331. case AT_FPTS:
  332.     return parseFPTS(tif, skipStatus(rbuf), ppr);
  333. case AT_FET:
  334.     protoTrace("MODEM protocol botch: +FET: without +FPTS:");
  335.     processHangup("100"); // "Unspecified Phase D error"
  336.     return (false);
  337. case AT_FHNG:
  338.     waitFor(AT_OK); // resynchronize modem
  339.     return (false);
  340. }
  341.     }
  342. }
  343. bool
  344. Class2Modem::parseFPTS(TIFF* tif, const char* cp, int& ppr)
  345. {
  346.     u_long lc = 0;
  347.     int blc = 0;
  348.     int cblc = 0;
  349.     ppr = 0;
  350.     if (sscanf(cp, "%d,%ld,%d,%d", &ppr, &lc, &blc, &cblc) > 0) {
  351. /*
  352.  * In practice we cannot trust the modem line count when we're 
  353.  * not using ECM due to transmission errors and also due to
  354.  * bugs in the modems' own decoders (like MMR).
  355.  *
  356.  * Furthermore, there exists a discrepancy between many modem's
  357.  * behaviors and the specification.  Some give lc in hex and 
  358.  * others in decimal, and so this would further complicate things.
  359.  */
  360. if (!conf.class2UseLineCount) {
  361.     lc = getRecvEOLCount();
  362. }
  363. TIFFSetField(tif, TIFFTAG_IMAGELENGTH, lc);
  364. TIFFSetField(tif, TIFFTAG_CLEANFAXDATA, blc ?
  365.     CLEANFAXDATA_REGENERATED : CLEANFAXDATA_CLEAN);
  366. if (blc) {
  367.     TIFFSetField(tif, TIFFTAG_BADFAXLINES, (u_long) blc);
  368.     TIFFSetField(tif, TIFFTAG_CONSECUTIVEBADFAXLINES, cblc);
  369. }
  370. return (true);
  371.     } else {
  372. protoTrace("MODEM protocol botch: "%s"; can not parse line count",
  373.     cp);
  374. processHangup("100"); // "Unspecified Phase D error"
  375. return (false);
  376.     }
  377. }
  378. /*
  379.  * Complete a receive session.
  380.  */
  381. bool
  382. Class2Modem::recvEnd(FaxSetup*, fxStr&)
  383. {
  384.     if (!hadHangup) {
  385. if (isNormalHangup()) {
  386.     if (atCmd("AT+FDR", AT_FHNG)) // wait for DCN
  387. waitFor(AT_OK);
  388. } else
  389.     (void) atCmd(abortCmd); // abort session
  390.     }
  391.     return (true);
  392. }
  393. /*
  394.  * Abort an active receive session.
  395.  */
  396. void
  397. Class2Modem::recvAbort()
  398. {
  399.     strcpy(hangupCode, "50"); // force abort in recvEnd
  400. }