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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: ClassModem.c++,v 1.35 2009/02/17 01:42:42 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1994-1996 Sam Leffler
  4.  * Copyright (c) 1994-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 <ctype.h>
  27. #include <stdlib.h>
  28. #include "ModemServer.h"
  29. #include "FaxTrace.h"
  30. #include "Sys.h"
  31. /*
  32.  * Call status description strings.
  33.  */
  34. const char* ClassModem::callStatus[10] = {
  35.     "Call successful {E000}", // OK
  36.     "Busy signal detected {E001}", // BUSY
  37.     "No carrier detected {E002}", // NOCARRIER
  38.     "No answer from remote {E003}", // NOANSWER
  39.     "No local dialtone {E004}", // NODIALTONE
  40.     "Invalid dialing command {E005}", // ERROR
  41.     "Unknown problem {E006}", // FAILURE
  42.     "Carrier established, but Phase A failure {E007}", // NOFCON
  43.     "Data connection established (wanted fax) {E008}", // DATACONN
  44.     "Glare - RING detected {E009}", // RING
  45. };
  46. /*
  47.  * Service class descriptions.  The first three
  48.  * correspond to the EIA/TIA definitions.  The
  49.  * voice class is for ZyXEL modems.
  50.  */
  51. const char* ClassModem::serviceNames[9] = {
  52.     ""Data"", // SERVICE_DATA
  53.     ""Class 1"", // SERVICE_CLASS1
  54.     ""Class 2"", // SERVICE_CLASS2
  55.     ""Class 2.0"", // SERVICE_CLASS20 (XXX 3)
  56.     ""Class 1.0"", // SERVICE_CLASS10 (XXX 4)
  57.     ""Class 2.1"", // SERVICE_CLASS21 (XXX 5)
  58.     "", // 6
  59.     "", // 7
  60.     ""Voice"", // SERVICE_VOICE
  61. };
  62. const char* ClassModem::ATresponses[19] = {
  63.     "Nothing", // AT_NOTHING
  64.     "OK", // AT_OK
  65.     "Connection established", // AT_CONNECT
  66.     "No answer or ring back", // AT_NOANSWER
  67.     "No carrier", // AT_NOCARRIER
  68.     "No dial tone", // AT_NODIALTONE
  69.     "Busy", // AT_BUSY
  70.     "Phone off-hook", // AT_OFFHOOK
  71.     "Ring", // AT_RING
  72.     "Command error", // AT_ERROR
  73.     "Hang up", // AT_FHNG
  74.     "<Empty line>", // AT_EMPTYLINE
  75.     "<Timeout>", // AT_TIMEOUT
  76.     "<dle+etx>", // AT_DLEETX
  77.     "End of transmission", // AT_DLEEOT
  78.     "<xon>", // AT_XON
  79.     "DTMF detection", // AT_DTMF
  80.     "Voice connection", // AT_VCON
  81.     "<Unknown response>" // AT_OTHER
  82. };
  83. const char* ClassModem::callTypes[6] = {
  84.     "unknown",
  85.     "data",
  86.     "fax",
  87.     "voice",
  88.     "error",
  89.     "done"
  90. };
  91. const char* ClassModem::answerTypes[6] = {
  92.     "any",
  93.     "data",
  94.     "fax",
  95.     "voice",
  96.     "dial",
  97.     "external"
  98. };
  99. ClassModem::ClassModem(ModemServer& s, const ModemConfig& c)
  100.     : server(s)
  101.     , conf(c)
  102.     , mfrQueryCmd(c.mfrQueryCmd)
  103.     , modelQueryCmd(c.modelQueryCmd)
  104.     , revQueryCmd(c.revQueryCmd)
  105. {
  106.     modemServices = 0;
  107.     rate = BR0;
  108.     flowControl = conf.flowControl;
  109.     iFlow = FLOW_NONE;
  110.     oFlow = FLOW_NONE;
  111.     setupDefault(mfrQueryCmd,   conf.mfrQueryCmd,       "ATI3");
  112.     setupDefault(modelQueryCmd, conf.modelQueryCmd,     "ATI0");
  113.     setupDefault(revQueryCmd,   conf.revQueryCmd,       ""); // No "standard" way? -- dbely
  114. }
  115. ClassModem::~ClassModem()
  116. {
  117. }
  118. /*
  119.  * Default methods for modem driver interface.
  120.  */
  121. bool
  122. ClassModem::dataService()
  123. {
  124.     return atCmd(conf.class0Cmd);
  125. }
  126. CallStatus
  127. ClassModem::dial(const char* number, const char* origin, fxStr& emsg)
  128. {
  129.     dialedNumber = fxStr(number);
  130.     protoTrace("DIAL %s", number);
  131.     fxStr dialcmd = conf.dialCmd;
  132.     u_int destpos = dialcmd.find(0, "%s");
  133.     u_int origpos = dialcmd.find(0, "%d");
  134.     if (destpos == dialcmd.length() && origpos == dialcmd.length()) {
  135. // neither %d nor %s appear in the cmd, use dialcmd as-is
  136.     } else if (origpos == dialcmd.length()) {
  137. // just %s appears in the cmd
  138. dialcmd = fxStr::format((const char*) dialcmd, number);
  139.     } else if (destpos == dialcmd.length()) {
  140. // just %d appears in the cmd
  141. dialcmd[origpos+1] = 's';  // change %d to %s
  142. dialcmd = fxStr::format((const char*) dialcmd, origin);
  143.     } else {
  144. // both %d and %s appear in the cmd
  145. dialcmd[origpos+1] = 's';  // change %d to %s
  146. if (origpos < destpos) {
  147.     // %d appears before %s
  148.     dialcmd = fxStr::format((const char*) dialcmd, origin, number);
  149. } else {
  150.     // %s appears before %d
  151.     dialcmd = fxStr::format((const char*) dialcmd, number, origin);
  152. }
  153.     }
  154.     emsg = "";
  155.     CallStatus cs = (atCmd(dialcmd, AT_NOTHING) ? dialResponse(emsg) : FAILURE);
  156.     if (cs != OK && emsg == "") {
  157.         emsg = callStatus[cs];
  158.     }
  159.     return (cs);
  160. }
  161. /*
  162.  * Status messages to ignore when dialing.
  163.  */
  164. bool
  165. ClassModem::isNoise(const char* s)
  166. {
  167.     static const char* noiseMsgs[] = {
  168. "CED", // RC32ACL-based modems send this before +FCON
  169. "DIALING",
  170. "RRING", // Telebit
  171. "RINGING", // ZyXEL
  172. "+FHR:", // Intel 144e
  173. "+A8", // Class 1.0 V.8 report
  174. "+F34:", // Class 1.0 V.34 report
  175. "+FDB:", // DCE debugging
  176. "MESSAGE-WAITING", // voice-mail waiting, Conexant
  177. "2003", // DLE+ETX ???
  178.     };
  179. #define NNOISE (sizeof (noiseMsgs) / sizeof (noiseMsgs[0]))
  180.     for (u_int i = 0; i < NNOISE; i++)
  181. if (strneq(s, noiseMsgs[i], strlen(noiseMsgs[i])))
  182.     return (true);
  183.     /*
  184.      * Some modems echoes the DTMF of the dialed number, and this should be
  185.      * ignored too.
  186.      * Instead of simply checking if "dialedNumber" is equal to "s" we must
  187.      * consider the possibility of having more numbers at "conf.dialCmd".
  188.      * Eg.: conf.dialCmd = atdt0%s (zero will be echoed)
  189.      * The easiest way is to find if "dialedNumber" is a sub-string of "s".
  190.      */
  191.     if (strstr(s, (const char *) dialedNumber) != NULL) return (true);
  192.     return (false);
  193. }
  194. #undef NNOISE
  195. /*
  196.  * Set of status codes we expect to receive
  197.  * from a modem in response to an A (answer
  198.  * the phone) command.
  199.  */
  200. static const AnswerMsg answerMsgs[] = {
  201. { "CONNECT FAX",11,
  202.    ClassModem::AT_NOTHING, ClassModem::OK,   ClassModem::CALLTYPE_FAX },
  203. { "CONNECT",     7,
  204.    ClassModem::AT_NOTHING, ClassModem::OK,   ClassModem::CALLTYPE_DATA },
  205. { "NO ANSWER",   9,
  206.    ClassModem::AT_NOTHING, ClassModem::NOANSWER,  ClassModem::CALLTYPE_ERROR },
  207. { "NO CARRIER", 10,
  208.    ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
  209. { "NO DIALTONE",11,
  210.    ClassModem::AT_NOTHING, ClassModem::NODIALTONE,ClassModem::CALLTYPE_ERROR },
  211. { "ERROR",       5,
  212.    ClassModem::AT_NOTHING, ClassModem::ERROR,     ClassModem::CALLTYPE_ERROR },
  213. { "+FHNG:", 6,
  214.    ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
  215. { "+FHS:", 5,
  216.    ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
  217. { "OK", 2,
  218.    ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
  219. { "BUSY", 4,
  220.    ClassModem::AT_NOTHING, ClassModem::NOCARRIER, ClassModem::CALLTYPE_ERROR },
  221. { "FAX",  3,
  222.    ClassModem::AT_CONNECT, ClassModem::OK,   ClassModem::CALLTYPE_FAX },
  223. { "DATA",  4,
  224.    ClassModem::AT_CONNECT, ClassModem::OK,   ClassModem::CALLTYPE_DATA },
  225. };
  226. #define NANSWERS (sizeof (answerMsgs) / sizeof (answerMsgs[0]))
  227. const AnswerMsg*
  228. ClassModem::findAnswer(const char* s)
  229. {
  230.     for (u_int i = 0; i < NANSWERS; i++)
  231. if (strneq(s, answerMsgs[i].msg, answerMsgs[i].len))
  232.     return (&answerMsgs[i]);
  233.     return (NULL);
  234. }
  235. #undef NANSWERS
  236. /*
  237.  * Deduce connection kind: fax, data, or voice.
  238.  */
  239. CallType
  240. ClassModem::answerResponse(fxStr answerCmd, fxStr& emsg)
  241. {
  242.     CallStatus cs = FAILURE;
  243.     ATResponse r;
  244.     time_t start = Sys::now();
  245.     u_short morerings = 0;
  246.     do {
  247. r = atResponse(rbuf, conf.answerResponseTimeout);
  248. again:
  249. if (r == AT_TIMEOUT || r == AT_DLEEOT || r == AT_NOCARRIER)
  250.     break;
  251. if (r == AT_RING && ++morerings > 1) {
  252.     /*
  253.      * We answered already.  If we see RING once it could
  254.      * be glare.  If we see it yet again, then the modem
  255.      * apparently did not see or respond to our first 
  256.      * answerCmd and we've got to try it again.
  257.      *
  258.      * In some cases this may happen because our ATA comes
  259.      * too soon following the RING for the modem's liking.
  260.      * This may have to do with the presence of Caller*ID
  261.      * reporting and in such cases is probably best-resolved
  262.      * by changing the RingsBeforeAnswer setting.  However,
  263.      * as a fail-safe we try to shake up the timing by
  264.      * using a small delay in our ATA here.
  265.      */
  266.     pause(500);
  267.     atCmd(answerCmd, AT_NOTHING);
  268.     morerings = 0;
  269. }
  270. const AnswerMsg* am = findAnswer(rbuf);
  271. if (am != NULL) {
  272.     if (am->expect != AT_NOTHING && conf.waitForConnect) {
  273. /*
  274.  * Response string is an intermediate result that
  275.  * is only meaningful if followed by AT response
  276.  * am->next.  Read the next response from the modem
  277.  * and if it's the expected one, use the message
  278.  * to intuit the call type.  Otherwise, discard
  279.  * the intermediate response string and process the
  280.  * call according to the newly read response.
  281.  * This is intended to deal with modems that send
  282.  *   <something>
  283.  *   CONNECT
  284.  * (such as the Boca 14.4).
  285.  */
  286. r = atResponse(rbuf, conf.answerResponseTimeout);
  287. if (r != am->expect)
  288.     goto again;
  289.     }
  290.     if (am->status == OK) // success
  291. return (am->type);
  292.     cs = am->status;
  293.     break;
  294. }
  295. if (r == AT_EMPTYLINE) {
  296.     emsg = callStatus[cs];
  297.     return (CALLTYPE_ERROR);
  298. }
  299.     } while ((unsigned) ((Sys::now()-start)*1000) < conf.answerResponseTimeout);
  300.     emsg = "Ring detected without successful handshake {E012}";
  301.     return (CALLTYPE_ERROR);
  302. }
  303. CallType
  304. ClassModem::answerCall(AnswerType atype, fxStr& emsg, const char* number)
  305. {
  306.     CallType ctype = CALLTYPE_ERROR;
  307.     /*
  308.      * If the request has no type-specific commands
  309.      * to use, then just use the normal commands
  310.      * intended for answering any type of call.
  311.      */
  312.     fxStr answerCmd;
  313.     switch (atype) {
  314.     case ANSTYPE_FAX: answerCmd = conf.answerFaxCmd; break;
  315.     case ANSTYPE_DATA: answerCmd = conf.answerDataCmd; break;
  316.     case ANSTYPE_VOICE: answerCmd = conf.answerVoiceCmd; break;
  317.     case ANSTYPE_DIAL:
  318. answerCmd = conf.answerDialCmd;
  319. dial(number, NULL, emsg); // no error-checking
  320. break;
  321.     }
  322.     if (answerCmd == "")
  323. answerCmd = conf.answerAnyCmd;
  324.     if (atCmd(answerCmd, AT_NOTHING)) {
  325. ctype = answerResponse(answerCmd, emsg);
  326. if (atype == ANSTYPE_DIAL) ctype = CALLTYPE_FAX; // force as fax
  327. if (ctype == CALLTYPE_UNKNOWN) {
  328.     /*
  329.      * The response does not uniquely identify the type
  330.      * of call; assume the type corresponds to the type
  331.      * of the answer request.
  332.      */
  333.     static CallType unknownCall[] = {
  334. CALLTYPE_FAX, // ANSTYPE_ANY (default)
  335. CALLTYPE_DATA, // ANSTYPE_DATA
  336. CALLTYPE_FAX, // ANSTYPE_FAX
  337. CALLTYPE_VOICE, // ANSTYPE_VOICE
  338. CALLTYPE_UNKNOWN, // ANSTYPE_EXTERN
  339.     };
  340.     ctype = unknownCall[atype];
  341. }
  342. answerCallCmd(ctype);
  343.     }
  344.     return (ctype);
  345. /*
  346.  * Send any configured commands to the modem once the
  347.  * type of the call has been established.  These commands
  348.  * normally configure flow control and buad rate for
  349.  * modems that, for example, require a fixed baud rate
  350.  * and flow control scheme when receiving fax.
  351.  */ 
  352. void
  353. ClassModem::answerCallCmd(CallType ctype)
  354. {
  355.     fxStr beginCmd;
  356.     switch (ctype) {
  357.     case CALLTYPE_FAX: beginCmd = conf.answerFaxBeginCmd; break;
  358.     case CALLTYPE_DATA: beginCmd = conf.answerDataBeginCmd; break;
  359.     case CALLTYPE_VOICE:beginCmd = conf.answerVoiceBeginCmd; break;
  360.     }
  361.     if (beginCmd != "")
  362. (void) atCmd(beginCmd);
  363. }
  364. /*
  365.  * Set data transfer timeout and adjust according
  366.  * to the negotiated bit rate.
  367.  */
  368. void
  369. ClassModem::setDataTimeout(long secs, u_int br)
  370. {
  371.     dataTimeout = secs*1000; // 9600 baud timeout/data write (ms)
  372.     switch (br) {
  373.     case BR_2400: dataTimeout *= 4; break;
  374.     case BR_4800: dataTimeout *= 2; break;
  375.     case BR_9600: dataTimeout = (4*dataTimeout)/3; break;
  376.     // could shrink timeout for br > 9600
  377.     }
  378. }
  379. fxStr
  380. ClassModem::getCapabilities() const
  381. {
  382.     return fxStr("");
  383. }
  384. /*
  385.  * Tracing support.
  386.  */
  387. /*
  388.  * Trace a MODEM-communication-related activity.
  389.  */
  390. void
  391. ClassModem::modemTrace(const char* fmt ...)
  392. {
  393.     va_list ap;
  394.     va_start(ap, fmt);
  395.     server.vtraceStatus(FAXTRACE_MODEMCOM, fmt, ap);
  396.     va_end(ap);
  397. }
  398. /*
  399.  * Trace a modem capability.
  400.  */
  401. void
  402. ClassModem::modemCapability(const char* fmt ...)
  403. {
  404.     va_list ap;
  405.     va_start(ap, fmt);
  406.     static const fxStr modem("MODEM: ");
  407.     server.vtraceStatus(FAXTRACE_MODEMCAP, modem | fmt, ap);
  408.     va_end(ap);
  409. }
  410. /*
  411.  * Indicate a modem supports some capability.
  412.  */
  413. void
  414. ClassModem::modemSupports(const char* fmt ...)
  415. {
  416.     va_list ap;
  417.     va_start(ap, fmt);
  418.     static const fxStr supports("MODEM Supports ");
  419.     server.vtraceStatus(FAXTRACE_MODEMCAP, supports | fmt, ap);
  420.     va_end(ap);
  421. }
  422. /*
  423.  * Trace a protocol-related activity.
  424.  */
  425. void
  426. ClassModem::protoTrace(const char* fmt ...)
  427. {
  428.     va_list ap;
  429.     va_start(ap, fmt);
  430.     server.vtraceStatus(FAXTRACE_PROTOCOL, fmt, ap);
  431.     va_end(ap);
  432. }
  433. /*
  434.  * Trace a server-level activity.
  435.  */
  436. void
  437. ClassModem::serverTrace(const char* fmt ...)
  438. {
  439.     va_list ap;
  440.     va_start(ap, fmt);
  441.     server.vtraceStatus(FAXTRACE_SERVER, fmt, ap);
  442.     va_end(ap);
  443. }
  444. /*
  445.  * Trace a modem capability bit mask.
  446.  */
  447. void
  448. ClassModem::traceBits(u_int bits, const char* bitNames[])
  449. {
  450.     for (u_int i = 0; bits; i++)
  451. if (BIT(i) & bits) {
  452.     modemSupports(bitNames[i]);
  453.     bits &= ~BIT(i);
  454. }
  455. }
  456. /*
  457.  * Trace a modem capability true bit mask (VR, BF, JP).
  458.  */
  459. void
  460. ClassModem::traceBitMask(u_int bits, const char* bitNames[])
  461. {
  462.     u_int i = 0;
  463.     do {
  464. if ((bits & i) == i) {
  465.     modemSupports(bitNames[i]);
  466.     bits -= i;
  467. }
  468. i++;
  469.     } while (bits);
  470. }
  471. /*
  472.  * Modem i/o support.
  473.  */
  474. int
  475. ClassModem::getModemLine(char buf[], u_int bufSize, long ms)
  476. {
  477.     int n = server.getModemLine(buf, bufSize, ms);
  478.     if (n > 0)
  479. trimModemLine(buf, n);
  480.     return (n);
  481. }
  482. int ClassModem::getModemBit(long ms)  { return server.getModemBit(ms); }
  483. int ClassModem::getModemChar(long ms, bool doquery) { return server.getModemChar(ms, doquery); }
  484. int ClassModem::getModemDataChar()    { return server.getModemChar(dataTimeout); }
  485. int ClassModem::getLastByte()         { return server.getLastByte(); }
  486. bool ClassModem::didBlockEnd()        { return server.didBlockEnd(); }
  487. void ClassModem::resetBlock()         { server.resetBlock(); }
  488. bool
  489. ClassModem::putModemDLEData(const u_char* data, u_int cc, const u_char* bitrev, long ms, bool doquery)
  490. {
  491.     u_char dlebuf[2*1024];
  492.     while (cc > 0) {
  493. if (wasTimeout() || abortRequested())
  494.     return (false);
  495. /*
  496.  * Copy to temp buffer, doubling DLE's.
  497.  */
  498. u_int i, j;
  499. u_int n = fxmin((size_t) cc, sizeof (dlebuf)/2);
  500. for (i = 0, j = 0; i < n; i++, j++) {
  501.     dlebuf[j] = bitrev[data[i]];
  502.     if (dlebuf[j] == DLE)
  503. dlebuf[++j] = DLE;
  504. }
  505. if (!putModem(dlebuf, j, ms))
  506.     return (false);
  507. data += n;
  508. cc -= n;
  509. /*
  510.  * Fax is half-duplex.  Thus, we shouldn't typically need to read
  511.  * data from the modem while we are transmitting.  However, Phase C
  512.  * can be long, and things can go wrong, and it will be nice to
  513.  * see the timing of any messages that will come from the modem,
  514.  * instead of letting them buffer.  Furthermore, advanced Class 2 
  515.  * debugging will send us messages during Phase C.  So in those
  516.  * cases when it will be useful, we query the modem during transmits.
  517.  *
  518.  * In the cases where things do go wrong, we'll need to begin
  519.  * building-in the intelligence to handle that here.  But we have
  520.  * to wait and see what turns up in order to do that.
  521.  */
  522. if (doquery) {
  523.     int c;
  524.     fxStr dbdata;
  525.     do {
  526.         c = getModemChar(0, true);
  527. if (c != EOF && c != '' && c != 'r' && c != 'n')
  528.     dbdata.append(c);
  529. else if (dbdata.length()) {
  530.     protoTrace("DCE DEBUG: %s", (const char*) dbdata);
  531.     dbdata = "";
  532. }
  533.     } while (c != EOF);
  534. }
  535.     }
  536.     return (true);
  537. }
  538. void ClassModem::flushModemInput()
  539.     { server.modemFlushInput(); }
  540. bool ClassModem::putModem(void* d, int n, long ms)
  541.     { return server.putModem(d, n, ms); }
  542. bool ClassModem::putModemData(void* d, int n, long ms)
  543.     { return server.putModem(d, n, (ms == -1) ? dataTimeout : ms); }
  544. bool
  545. ClassModem::putModemLine(const char* cp, long ms)
  546. {
  547.     u_int cc = strlen(cp);
  548.     server.traceStatus(FAXTRACE_MODEMCOM, "<-- [%u:%s\r]", cc+1, cp);
  549.     static const char CR = 'r';
  550.     return (server.putModem1(cp, cc, ms) && server.putModem1(&CR, 1, ms));
  551. }
  552. void ClassModem::startTimeout(long ms) { server.startTimeout(ms); }
  553. void ClassModem::stopTimeout(const char* w){ server.stopTimeout(w); }
  554. const u_int MSEC_PER_SEC = 1000;
  555. #include <sys/time.h>
  556. #if HAS_SELECT_H
  557. #include <sys/select.h>
  558. #endif
  559. void
  560. ClassModem::pause(u_int ms)
  561. {
  562.     if (ms == 0)
  563. return;
  564.     protoTrace("DELAY %u ms", ms);
  565.     struct timeval tv;
  566.     tv.tv_sec = ms / MSEC_PER_SEC;
  567.     tv.tv_usec = (ms % MSEC_PER_SEC) * 1000;
  568.     (void) select(0, 0, 0, 0, &tv);
  569. }
  570. void
  571. ClassModem::setupDefault(fxStr& s, const fxStr& configured, const char* def)
  572. {
  573.     if (configured == "")
  574. s = def;
  575.     else
  576. s = configured;
  577. }
  578. /*
  579.  * Reset the modem and set the DTE-DCE rate.
  580.  */
  581. bool
  582. ClassModem::selectBaudRate(BaudRate br, FlowControl i, FlowControl o)
  583. {
  584.     rate = br;
  585.     iFlow = i;
  586.     oFlow = o;
  587.     return (reset(5*1000) || reset(5*1000)); // NB: try at most twice
  588. }
  589. bool ClassModem::sendBreak(bool pause)
  590.     { return server.sendBreak(pause); }
  591. bool
  592. ClassModem::setBaudRate(BaudRate r)
  593. {
  594.     if (server.setBaudRate(r)) {
  595. if (conf.baudRateDelay)
  596.     pause(conf.baudRateDelay);
  597. return (true);
  598.     } else
  599. return (false);
  600. }
  601. bool
  602. ClassModem::setBaudRate(BaudRate r, FlowControl i, FlowControl o)
  603. {
  604.     iFlow = i;
  605.     oFlow = o;
  606.     rate = r;
  607.     if (server.setBaudRate(r,i,o)) {
  608. if (conf.baudRateDelay)
  609.     pause(conf.baudRateDelay);
  610. return (true);
  611.     } else
  612. return (false);
  613. }
  614. bool
  615. ClassModem::setXONXOFF(FlowControl i, FlowControl o, SetAction a)
  616. {
  617.     iFlow = i;
  618.     oFlow = o;
  619.     return server.setXONXOFF(i, o, a);
  620. }
  621. bool ClassModem::setDTR(bool onoff)
  622.     { return server.setDTR(onoff); }
  623. bool ClassModem::setInputBuffering(bool onoff)
  624.     { return server.setInputBuffering(onoff); }
  625. bool ClassModem::modemStopOutput()
  626.     { return server.modemStopOutput(); }
  627. /*
  628.  * Miscellaneous server interfaces hooks.
  629.  */
  630. bool ClassModem::abortRequested()
  631.     { return server.abortRequested(); }
  632. void ClassModem::beginTimedTransfer() { server.timeout = false; }
  633. void ClassModem::endTimedTransfer() {}
  634. bool ClassModem::wasTimeout() { return server.timeout; }
  635. void ClassModem::setTimeout(bool b) { server.timeout = b; }
  636. /*
  637.  * Parsing support routines.
  638.  */
  639. /*
  640.  * Cleanup a response line from the modem.  This removes
  641.  * leading white space and any prefixing "+F<mumble>=" crap
  642.  * that some Class 2 modems put at the front, as well as
  643.  * any trailing white space.
  644.  */
  645. void
  646. ClassModem::trimModemLine(char buf[], int& cc)
  647. {
  648.     // trim trailing white space
  649.     if (cc > 0 && isspace(buf[cc-1])) {
  650. do {
  651.     cc--;
  652. } while (cc > 0 && isspace(buf[cc-1]));
  653. buf[cc] = '';
  654.     }
  655.     if (cc > 0) {
  656. int i = 0;
  657. // leading white space
  658. while (i < cc && isspace(buf[i]))
  659.     i++;
  660. // check for a leading +F<mumble>=
  661. if (i+1 < cc && buf[i] == '+' && buf[i+1] == 'F') {
  662.     u_int j = i;
  663.     for (i += 2; i < cc && buf[i] != '='; i++)
  664.  ;
  665.     if (i < cc) { // trim more white space
  666. for (i++; i < cc && isspace(buf[i]); i++)
  667.     ;
  668.     } else // no '=', back out
  669. i = j;
  670. }
  671. cc -= i;
  672. memmove(buf, buf+i, cc+1);
  673.     }
  674. }
  675.     /*
  676.      * The modem drivers and main server code require:
  677.      *
  678.      * echoOff command echo disabled
  679.      * verboseResults verbose command result strings
  680.      * resultCodes result codes enabled
  681.      * onHook modem initially on hook (hung up)
  682.      * noAutoAnswe no auto-answer (we do it manually)
  683.      *
  684.      * In addition the following configuration is included
  685.      * in the reset command set:
  686.      *
  687.      * flowControl DCE-DTE flow control method
  688.      * setupDTR DTR management technique
  689.      * setupDCD DCD management technique
  690.      * pauseTime time to pause for "," when dialing
  691.      * waitTime time to wait for carrier when dialing
  692.      *
  693.      * Any other modem-specific configuration that needs to
  694.      * be done at reset time should be implemented by overriding
  695.      * the ClassModem::reset method.
  696.      */
  697. bool
  698. ClassModem::reset(long ms)
  699. {
  700.     if (conf.dtrDropDelay) {
  701. setDTR(false);
  702. pause(conf.dtrDropDelay); // required DTR OFF-to-ON delay
  703. setDTR(true);
  704. pause(conf.resetDelay); // pause so modem can do reset
  705.     }
  706. #ifndef CONFIG_NOREOPEN
  707.     /*
  708.      * On some systems lowering and raising DTR is not done
  709.      * properly (DTR is not raised when requested); thus we
  710.      * reopen the device to insure that DTR is reasserted.
  711.      */
  712.     server.reopenDevice();
  713. #endif
  714.     if (!setBaudRate(rate, iFlow, oFlow)) {
  715.         return (false);
  716.     }
  717.     flushModemInput();
  718.     /*
  719.      * Perform a soft reset as well to ensure the modem
  720.      * is in a stable state before sending the additional
  721.      * reset commands.  Depending on the modem and its
  722.      * state, we may wait 30 sec for OK repsonse.
  723.      */
  724.     if ( true != atCmd(conf.softResetCmd, AT_OK, 30*1000) ) {
  725.         return false;
  726.     }
  727.     /*
  728.      * Some modems require a pause after ATZ before they can
  729.      * accept any more commands although they have already
  730.      * replied OK to the ATZ command.
  731.      */
  732.     pause(conf.softResetCmdDelay);
  733.     // some modems result with OK *twice* after ATZ, so flush it
  734.     flushModemInput();
  735.     if ( true != atCmd(conf.resetCmds, AT_OK, ms) ) {
  736.         return false;
  737.     }
  738.     if ( true != atCmd(conf.noAutoAnswerCmd, AT_OK, ms) ) {
  739.         return false;
  740.     }
  741.     if (conf.noAutoAnswerCmdDelay) {
  742. /* Some modems do funny things after ATS0=0. */
  743. pause(conf.noAutoAnswerCmdDelay);
  744. flushModemInput();
  745.     }
  746.     if ( true != atCmd(conf.echoOffCmd, AT_OK, ms) ) {
  747.         return false;
  748.     }
  749.     if ( true != atCmd(conf.verboseResultsCmd, AT_OK, ms) ) {
  750.         return false;
  751.     }
  752.     if ( true != atCmd(conf.resultCodesCmd, AT_OK, ms) ) {
  753.         return false;
  754.     }
  755.     // some modems do not accept standard onHookCmd (ATH0) when
  756.     // they are allready on hook
  757. //    if ( true != atCmd(conf.onHookCmd, AT_OK, ms) ) {
  758. //        return false;
  759. //    }
  760.     if ( true != atCmd(conf.pauseTimeCmd, AT_OK, ms) ) {
  761.         return false;
  762.     }
  763.     if ( true != atCmd(conf.waitTimeCmd, AT_OK, ms) ) {
  764.         return false;
  765.     }
  766.     if ( true != atCmd(conf.getFlowCmd(conf.flowControl), AT_OK, ms) ) {
  767.         return false;
  768.     }
  769.     if ( true != atCmd(conf.setupDTRCmd, AT_OK, ms) ) {
  770.         return false;
  771.     }
  772.     if ( true != atCmd(conf.setupDCDCmd, AT_OK, ms) ) {
  773.         return false;
  774.     }
  775.     return true;
  776. }
  777. /*
  778.  * Some scenarios require a "ready" command sequence to occur
  779.  * to, for example, un-busy a line (DID) or otherwise ready
  780.  * the modem for incoming calls after the rest of the
  781.  * initialization has already occurred.
  782.  */
  783. bool
  784. ClassModem::ready(long ms)
  785. {
  786.     return atCmd(conf.readyCmds, AT_OK, ms);
  787. }
  788. bool
  789. ClassModem::sync(long ms)
  790. {
  791.     return waitFor(AT_OK, ms);
  792. }
  793. ATResponse
  794. ClassModem::atResponse(char* buf, long ms)
  795. {
  796.     bool prevTimeout = wasTimeout();
  797.     int n = getModemLine(buf, sizeof (rbuf), ms);
  798.     if (!prevTimeout && wasTimeout())
  799. lastResponse = AT_TIMEOUT;
  800.     else if (n <= 0)
  801. lastResponse = AT_EMPTYLINE;
  802.     else {
  803. lastResponse = AT_OTHER;
  804. switch (buf[0]) {
  805. case 'B':
  806.     if (strneq(buf, "BUSY", 4))
  807. lastResponse = AT_BUSY;
  808.     break;
  809. case 'C':
  810.     if (strneq(buf, "CONNECT", 7))
  811. lastResponse = AT_CONNECT;
  812.     break;
  813. case 'D':
  814.     if (strneq(buf, "DTMF", 4))
  815. lastResponse = AT_DTMF;
  816.     break;
  817. case 'E':
  818.     if (strneq(buf, "ERROR", 5))
  819. lastResponse = AT_ERROR;
  820.     break;
  821. case 'N':
  822.     if (strneq(buf, "NO CARRIER", 10))
  823. lastResponse = AT_NOCARRIER;
  824.     else if (strneq(buf, "NO DIAL", 7)) // NO DIALTONE or NO DIAL TONE
  825. lastResponse = AT_NODIALTONE;
  826.     else if (strneq(buf, "NO ANSWER", 9))
  827. lastResponse = AT_NOANSWER;
  828.     break;
  829. case 'O':
  830.     if (strneq(buf, "OK", 2))
  831. lastResponse = AT_OK;
  832.     break;
  833. case 'P':
  834.     if (strneq(buf, "PHONE OFF-HOOK", 14))
  835. lastResponse = AT_OFFHOOK;
  836.     break;
  837. case 'R':
  838.     if (streq(buf, "RING")) // NB: avoid match of RINGING
  839. lastResponse = AT_RING;
  840.     break;
  841. case 'V':
  842.     if (streq(buf, "VCON"))
  843. lastResponse = AT_VCON;
  844.     break;
  845. case '20':
  846.     if (streq(buf, "2003")) // DLE/ETX
  847. lastResponse = AT_DLEETX;
  848.     if (streq(buf, "2004"))
  849. lastResponse = AT_DLEEOT; // DLE+EOT
  850.     break;
  851. case '21':
  852.     if (streq(buf, "21")) // DC1 (XON)
  853. lastResponse = AT_XON;
  854.     break;
  855. }
  856.     }
  857.     return lastResponse;
  858. }
  859. #define isLineBreak(c) ((c) == 'n' || (c) == 'r')
  860. #define isEscape(c) ((c) & 0x80)
  861. /*
  862.  * Send an AT command string to the modem and, optionally
  863.  * wait for status responses.  This routine breaks multi-line
  864.  * strings (demarcated by embedded n's) and waits for each
  865.  * intermediate response.  Embedded escape sequences for
  866.  * changing the DCE-DTE communication rate and/or host-modem
  867.  * flow control scheme are also recognized and handled.
  868.  */
  869. bool
  870. ClassModem::atCmd(const fxStr& cmd, ATResponse r, long ms)
  871. {
  872.     u_int cmdlen;
  873.     u_int pos;
  874.     bool respPending;
  875.     if (lastResponse == AT_RING) lastResponse = AT_NOTHING;
  876.     do {
  877. cmdlen = cmd.length();
  878. pos = 0;
  879. respPending = false;
  880. /*
  881.  * Scan string for line breaks and escape codes (byte w/ 0x80 set).
  882.  * A line break causes the current string to be sent to the modem
  883.  * and a return status string parsed (and possibly compared to an
  884.  * expected response).  An escape code terminates scanning,
  885.  * with any pending string flushed to the modem before the
  886.  * associated commands are carried out.  All commands are sent in
  887.  * uppercase.
  888.  */
  889. u_int i = 0;
  890. while (i < cmdlen) {
  891.     if (isLineBreak(cmd[i]) && !(i+1 < cmdlen && isEscape(cmd[i+1]))) {
  892. /*
  893.  * No escape code follows, send partial string
  894.  * to modem and await status string if necessary.
  895.  */
  896. if (conf.atCmdDelay)
  897.     pause(conf.atCmdDelay);
  898. fxStr command = cmd.extract(pos, i-pos);
  899. if (conf.raiseATCmd) command.raiseatcmd();
  900. if (!putModemLine(command, ms))
  901.     return (false);
  902. pos = ++i; // next segment starts after line break
  903. if (r != AT_NOTHING) {
  904.     if (!waitFor(r, ms))
  905. return (false);
  906. } else {
  907.     if (!waitFor(AT_OK, ms))
  908. return (false);
  909. }
  910. respPending = false;
  911.     } else if (isEscape(cmd[i])) {
  912. /*
  913.  * Escape code; flush any partial line, process
  914.  * escape codes and carry out their actions.
  915.  */
  916. ATResponse resp = AT_NOTHING;
  917. if (i > pos) {
  918.     if (conf.atCmdDelay)
  919. pause(conf.atCmdDelay);
  920.     if (isLineBreak(cmd[i-1])) {
  921. /*
  922.  * Send data with a line break and arrange to
  923.  * collect the expected response (possibly
  924.  * specified through a <waitfor> escape processed
  925.  * below).  Note that we use putModemLine, as
  926.  * above, so that the same line break is sent
  927.  * to the modem for all segments (i.e. n is
  928.  * translated to r).
  929.  */
  930. fxStr command = cmd.extract(pos, i-1-pos);
  931. if (conf.raiseATCmd) command.raiseatcmd();
  932. if (!putModemLine(command, ms))
  933.     return (false);
  934. // setup for expected response
  935. resp = (r != AT_NOTHING ? r : AT_OK);
  936.     } else {
  937. /*
  938.  * Flush any data as-is, w/o adding a line
  939.  * break or expecting a response.  This is
  940.  * important for sending, for example, a
  941.  * command escape sequence such as "+++".
  942.  */
  943. u_int cc = i-pos;
  944. const char* cp = &cmd[pos];
  945. server.traceStatus(FAXTRACE_MODEMCOM, "<-- [%u:%s]", cc,cp);
  946. if (!server.putModem1(cp, cc))
  947.     return (false);
  948.     }
  949.     respPending = true;
  950. }
  951. /*
  952.  * Process escape codes.
  953.  */
  954. BaudRate br = rate;
  955. FlowControl flow = flowControl;
  956. u_int delay = 0;
  957. do {
  958.     switch (cmd[i] & 0xff) {
  959.     case ESC_SETBR: // set host baud rate
  960. br = (u_char) cmd[++i];
  961. if (br != rate) {
  962.     setBaudRate(br);
  963.     rate = br;
  964. }
  965. break;
  966.     case ESC_SETFLOW: // set host flow control
  967. flow = (u_char) cmd[++i];
  968. if (flow != flowControl) {
  969.     setBaudRate(br, flow, flow);
  970.     flowControl = flow;
  971. }
  972. break;
  973.     case ESC_DELAY: // host delay
  974. delay = (u_char) cmd[++i];
  975. if (delay != 0)
  976.     pause(delay*10); // 10 ms granularity
  977. break;
  978.     case ESC_WAITFOR: // wait for response
  979. resp = (u_char) cmd[++i];
  980.         if (resp != AT_NOTHING) {
  981.     // XXX check return?
  982.     // The timeout setting here (60*1000) used to be "ms" (which
  983.     // defaults to 30 s), but we find that's too short, especially 
  984.     // for long-running escape sequences.  It really needs to be
  985.     // escape-configurable, but for now we just make it 60 s.
  986.     (void) waitFor(resp, 60*1000);
  987.     respPending = false;
  988. }
  989. break;
  990.     case ESC_FLUSH: // flush input
  991. flushModemInput();
  992. break;
  993.     case ESC_PLAY:
  994. {
  995.     fxStr filename = fxStr("etc/play");
  996.     filename.append(cmd[++i]);
  997.     filename.append(".raw");
  998.     protoTrace("Playing file "%s".", (const char*) filename);
  999.     int fd = open((const char*) filename, O_RDONLY);
  1000.     if (fd > 0) {
  1001. u_char buf[1024];
  1002. int len, pos;
  1003. do {
  1004.     pos = 0;
  1005.     do {
  1006. len = read(fd, &buf[pos], 1);
  1007. if (buf[pos] == 0x10) buf[++pos] = 0x10;
  1008. pos++;
  1009.     } while (len > 0 &&  (u_int) pos < sizeof(buf) - 1);
  1010.     putModem(&buf[0], pos, getDataTimeout());
  1011. } while (len > 0);
  1012. close(fd);
  1013.     } else {
  1014. protoTrace("Unable to open file "%s" for reading.", (const char*) filename);
  1015.     }
  1016.     u_char buf[2];
  1017.     buf[0] = DLE; buf[1] = ETX;
  1018.     putModem(buf, 2, getDataTimeout());
  1019. }
  1020. break;
  1021.     }
  1022. } while (++i < cmdlen && isEscape(cmd[i]));
  1023. pos = i; // next segment starts here
  1024. if (respPending) {
  1025.     /*
  1026.      * If a segment with a line break was flushed
  1027.      * but no explicit <waitfor> escape followed
  1028.      * then collect the response here so that it
  1029.      * does not get lost.
  1030.      */
  1031.     if (resp != AT_NOTHING && !waitFor(resp, ms))
  1032. return (false);
  1033.     respPending = false;
  1034. }
  1035.     } else
  1036. i++;
  1037. }
  1038. /*
  1039.  * Flush any pending string to modem.
  1040.  */
  1041. if (i > pos) {
  1042.     if (conf.atCmdDelay)
  1043. pause(conf.atCmdDelay);
  1044.     fxStr command = cmd.extract(pos, i-pos);
  1045.     if (conf.raiseATCmd) command.raiseatcmd();
  1046.     if (!putModemLine(command, ms))
  1047. return (false);
  1048.     respPending = true;
  1049. }
  1050. /*
  1051.  * Wait for any pending response.
  1052.  */
  1053. if (respPending) {
  1054.     if (r != AT_NOTHING && !waitFor(r, ms)) {
  1055. if (lastResponse != AT_RING) {
  1056.     return (false);
  1057. } else {
  1058.     // wait for result, but some modem's don't result after glare
  1059.     if (r != AT_NOTHING && !waitFor(r, ms)) {
  1060. lastResponse = AT_RING;
  1061.     }
  1062. }
  1063.     }
  1064. }
  1065.     } while (lastResponse == AT_RING);
  1066.     return (true);
  1067. }
  1068. #undef isEscape
  1069. #undef isLineBreak
  1070. /*
  1071.  * Wait (carefully) for some response from the modem.
  1072.  */
  1073. bool
  1074. ClassModem::waitFor(ATResponse wanted, long ms)
  1075. {
  1076.     for (;;) {
  1077. ATResponse response = atResponse(rbuf, ms);
  1078. if (response == wanted)
  1079.     return (true);
  1080. // we need to translate AT responses from faxd/Class2.h
  1081. if (response == 100) response = AT_FHNG;
  1082. switch (response) {
  1083. case AT_TIMEOUT:
  1084. case AT_EMPTYLINE:
  1085. case AT_ERROR:
  1086. case AT_NOCARRIER:
  1087. case AT_NODIALTONE:
  1088. case AT_NOANSWER:
  1089. case AT_BUSY:
  1090. case AT_OFFHOOK:
  1091. case AT_RING:
  1092. case AT_FHNG:
  1093.     modemTrace("MODEM %s", ATresponses[response]);
  1094. case AT_OK:
  1095.     /*
  1096.      * If we get OK and aren't expecting it then we're back in command-mode
  1097.      * and our previous command failed to acheive the desired result.
  1098.      */
  1099.     return (false);
  1100. }
  1101.     }
  1102. }
  1103. /*
  1104.  * Process a manufacturer/model/revision query.
  1105.  */
  1106. bool
  1107. ClassModem::doQuery(const fxStr& queryCmd, fxStr& result, long ms)
  1108. {
  1109.     if (queryCmd == "")
  1110. return (true);
  1111.     if (queryCmd[0] == '!') {
  1112. /*
  1113.  * ``!mumble'' is interpreted as "return mumble";
  1114.  * this means that you can't send ! to the modem.
  1115.  */
  1116. result = queryCmd.tail(queryCmd.length()-1);
  1117. return (true);
  1118.     }
  1119.     return (atQuery(queryCmd, result, ms));
  1120. }
  1121. /*
  1122.  * Return modem manufacturer.
  1123.  */
  1124. bool
  1125. ClassModem::setupManufacturer(fxStr& mfr)
  1126. {
  1127.     return doQuery(mfrQueryCmd, mfr);
  1128. }
  1129. /*
  1130.  * Return modem model identification.
  1131.  */
  1132. bool
  1133. ClassModem::setupModel(fxStr& model)
  1134. {
  1135.     return doQuery(modelQueryCmd, model);
  1136. }
  1137. /*
  1138.  * Return modem firmware revision.
  1139.  */
  1140. bool
  1141. ClassModem::setupRevision(fxStr& rev)
  1142. {
  1143.     return doQuery(revQueryCmd, rev);
  1144. }
  1145. /*
  1146.  * Send AT<what>? and get a string response.
  1147.  */
  1148. bool
  1149. ClassModem::atQuery(const char* what, fxStr& v, long ms)
  1150. {
  1151.     ATResponse r = AT_ERROR;
  1152.     if (atCmd(what, AT_NOTHING)) {
  1153. v.resize(0);
  1154. while ((r = atResponse(rbuf, ms)) != AT_OK) {
  1155.     if (r == AT_ERROR || r == AT_TIMEOUT || r == AT_EMPTYLINE)
  1156. break;
  1157.     if (v.length())
  1158. v.append('n');
  1159.     v.append(rbuf);
  1160. }
  1161.     }
  1162.     return (r == AT_OK);
  1163. }
  1164. /*
  1165.  * Send AT<what>? and get a range response.
  1166.  */
  1167. bool
  1168. ClassModem::atQuery(const char* what, u_int& v, long ms)
  1169. {
  1170.     char response[1024];
  1171.     if (atCmd(what, AT_NOTHING) && atResponse(response) == AT_OTHER) {
  1172. sync(ms);
  1173. return parseRange(response, v);
  1174.     }
  1175.     return (false);
  1176. }
  1177. /*
  1178.  * Parsing support routines.
  1179.  */
  1180. const char OPAREN = '(';
  1181. const char CPAREN = ')';
  1182. const char COMMA = ',';
  1183. const char SPACE = ' ';
  1184. /*
  1185.  * Parse a Class 2 parameter range string.  This is very
  1186.  * forgiving because modem vendors do not exactly follow
  1187.  * the syntax specified in the "standard".  Try looking
  1188.  * at some of the responses given by rev ~4.04 of the
  1189.  * ZyXEL firmware (for example)!
  1190.  *
  1191.  * This can also get complicated in that the parameters may
  1192.  * be in hexadecimal or decimal notation.  If in the future
  1193.  * a standard way to "detect" the presence of future hex values
  1194.  * (i.e. the <low> value is "00" instead of "0") may be found.
  1195.  * For now we just use Class2UseHex.
  1196.  *
  1197.  * NB: We accept alphanumeric items but don't return them
  1198.  *     in the parsed range so that modems like the ZyXEL 2864
  1199.  *     that indicate they support ``Class Z'' are handled.
  1200.  */
  1201. bool
  1202. ClassModem::vparseRange(const char* cp, int masked, int nargs ... )
  1203. {
  1204.     bool b = true;
  1205.     va_list ap;
  1206.     va_start(ap, nargs);
  1207.     while (nargs-- > 0) {
  1208. while (cp[0] == SPACE)
  1209.     cp++;
  1210. char matchc;
  1211. bool acceptList;
  1212. if (cp[0] == OPAREN) { // (<items>)
  1213.     matchc = CPAREN;
  1214.     acceptList = true;
  1215.     cp++;
  1216. } else if (isalnum(cp[0])) { // <item>
  1217.     matchc = COMMA;
  1218.     acceptList = (nargs == 0);
  1219. } else { // skip to comma
  1220.     b = false;
  1221.     break;
  1222. }
  1223. int mask = 0;
  1224. while (cp[0] && cp[0] != matchc) {
  1225.     if (cp[0] == SPACE) { // ignore white space
  1226. cp++;
  1227. continue;
  1228.     }
  1229.     if (!isalnum(cp[0])) {
  1230. b = false;
  1231. goto done;
  1232.     }
  1233.     int v;
  1234.     if (conf.class2UseHex) { // read as hex
  1235. if (isxdigit(cp[0])) {
  1236.     char *endp;
  1237.     v = (int) strtol(cp, &endp, 16);
  1238.     cp = endp;
  1239. } else {
  1240.     v = -1; // XXX skip item below
  1241.     while (isalnum((++cp)[0]));
  1242. }
  1243.     } else { // assume decimal
  1244. if (isdigit(cp[0])) {
  1245.     v = 0;
  1246.     do {
  1247. v = v*10 + (cp[0] - '0');
  1248.     } while (isdigit((++cp)[0]));
  1249. } else {
  1250.     v = -1; // XXX skip item below
  1251.     while (isalnum((++cp)[0]));
  1252. }
  1253.     }
  1254.     int r = v;
  1255.     if (cp[0] == '-') { // <low>-<high>
  1256. cp++;
  1257. if (conf.class2UseHex) { // read as hex
  1258.     if (!isxdigit(cp[0])) {
  1259. b = false;
  1260. goto done;
  1261.     }
  1262.     char *endp;
  1263.     r = (int) strtol(cp, &endp, 16);
  1264.     cp = endp;
  1265. } else { // assume decimal
  1266.     if (!isdigit(cp[0])) {
  1267. b = false;
  1268. goto done;
  1269.     }
  1270.     r = 0;
  1271.     do {
  1272. r = r*10 + (cp[0] - '0');
  1273.     } while (isdigit((++cp)[0]));
  1274. }
  1275.     } else if (cp[0] == '.') { // <d.b>
  1276. cp++;
  1277. if (v == 2) {
  1278.     if (cp[0] == '1') { // 2.1 -> 5
  1279. v = 5;
  1280. r = 5;
  1281.     } else { // 2.0 -> 3
  1282. v = 3;
  1283. r = 3;
  1284.     }
  1285. } else { // 1.0 -> 4
  1286. v = 4;
  1287. r = 4;
  1288. }
  1289. while (isdigit(cp[0])) // XXX
  1290.     cp++;
  1291.     }
  1292.     if (v != -1) { // expand range or list
  1293. if ((BIT(nargs) & masked) == BIT(nargs)) {
  1294.     /*
  1295.      * These are pre-masked values. T.32 Table 21 gives valid
  1296.      * values as: 00, 01, 02, 04, 08, 10, 20, 40 (hex).
  1297.      *
  1298.      * Some modems may say "(00-7F)" when what's meant is
  1299.      * "(00-40)" or simply "(7F)".
  1300.      */
  1301.     if (v == 00 && r == 127)
  1302. v = r = 127;
  1303.     if (v == r)
  1304. mask = v;
  1305.     else {
  1306. r = fxmin(r, 64); // clamp to valid range
  1307. mask = 0;
  1308. for (; v <= r; v++)
  1309.     if (v == 0 || v == 1 || v == 2 || v == 4 || v == 8 || v == 16 || v == 32 || v == 64)
  1310. mask += v;
  1311.     }
  1312. } else {
  1313.     r = fxmin(r, 31); // clamp to valid range
  1314.     for (; v <= r; v++)
  1315. mask |= 1<<v;
  1316. }
  1317.     }
  1318.     if (acceptList && cp[0] == COMMA) // (<item>,<item>...)
  1319. cp++;
  1320. }
  1321. *va_arg(ap, int*) = mask;
  1322. if (cp[0] == matchc)
  1323.     cp++;
  1324. if (matchc == CPAREN && cp[0] == COMMA)
  1325.     cp++;
  1326.     }
  1327. done:
  1328.     va_end(ap);
  1329.     return (b);
  1330. }
  1331. /*
  1332.  * Parse a single Class X range specification
  1333.  * and return the resulting bit mask.
  1334.  */
  1335. bool
  1336. ClassModem::parseRange(const char* cp, u_int& a0)
  1337. {
  1338.     return vparseRange(cp, 0, 1, &a0);
  1339. }
  1340. void
  1341. ClassModem::setSpeakerVolume(SpeakerVolume l)
  1342. {
  1343.     atCmd(conf.setVolumeCmd[l], AT_OK, 5000);
  1344. }
  1345. void
  1346. ClassModem::hangup()
  1347. {
  1348.     atCmd(conf.onHookCmd, AT_OK, 5000);
  1349. }
  1350. bool
  1351. ClassModem::poke()
  1352. {
  1353.     return atCmd("AT", AT_OK, 5000);
  1354. }
  1355. bool
  1356. ClassModem::waitForRings(u_short rings, CallType& type, CallID& callid)
  1357. {
  1358.     bool gotring = false;
  1359.     u_int i = 0, count = 0;
  1360.     int incadence[5] = { 0, 0, 0, 0, 0 };
  1361.     time_t timeout = conf.ringTimeout/1000; // 6 second/ring
  1362.     time_t start = Sys::now();
  1363.     do {
  1364. switch (atResponse(rbuf, conf.ringTimeout)) {
  1365. case AT_DTMF:
  1366. case AT_OTHER: // check distinctive ring
  1367.     if (streq(conf.ringData, rbuf))
  1368. type = CALLTYPE_DATA;
  1369.     else if (streq(conf.ringFax, rbuf))
  1370. type = CALLTYPE_FAX;
  1371.     else if (streq(conf.ringVoice, rbuf))
  1372. type = CALLTYPE_VOICE;
  1373.     else if (conf.dringOff.length() && strneq(conf.dringOff, rbuf, conf.dringOff.length())) {
  1374.         if (count++ == 0) break;         //discard initial DROFF code if present
  1375.         incadence[i++] = -atoi(rbuf + conf.dringOff.length());
  1376.         break;
  1377.     } else if (conf.dringOn.length() && strneq(conf.dringOn, rbuf, conf.dringOn.length())) {
  1378.         ++count;
  1379.         incadence[i++] = atoi(rbuf + conf.dringOn.length());
  1380.         break;
  1381.     } else {
  1382. if (conf.ringExtended.length() && strneq(rbuf, conf.ringExtended, conf.ringExtended.length())) // extended RING
  1383.     gotring = true;
  1384. conf.parseCallID(rbuf, callid);
  1385. /* DID modems may send DID data in lieu of RING */
  1386. for (u_int i = 0; i < conf.idConfig.length(); i++) {
  1387.     if (conf.idConfig[i].answerlength && callid.length(i) >= conf.idConfig[i].answerlength)
  1388. gotring = true;
  1389. }
  1390. break;
  1391.     }
  1392.     /* fall thru... */
  1393. case AT_RING: // normal ring
  1394.     if (conf.ringResponse != "" && rings+1U >= conf.ringsBeforeResponse) {
  1395. // With the MT1932ZDX we must respond ATH1>DT1 in order
  1396. // to hear DTMF tones which are DID data, and we configure
  1397. // RingExtended to be FAXCNG to then trigger ATA.
  1398. atCmd(conf.ringResponse, AT_NOTHING);
  1399. ATResponse r;
  1400. time_t ringstart = Sys::now();
  1401. bool callidwasempty = true, cmddone = false;
  1402. for (u_int i = 0; callidwasempty && i < callid.size(); i++)
  1403.     if (callid.length(i) )
  1404. callidwasempty = false;
  1405. do {
  1406.     r = atResponse(rbuf, 3000);
  1407.     if (r == AT_OTHER && callidwasempty) {
  1408. /*
  1409.  * Perhaps a modem will repeat CID/DID info for us
  1410.  * with AT+VRID if we missed it before.
  1411.  */
  1412. conf.parseCallID(rbuf, callid);
  1413.     }
  1414.     if (r == AT_OK) cmddone = true;
  1415.     else if (r == AT_VCON) cmddone = true; // VCON for modems that require ATA
  1416. } while (!cmddone && (Sys::now()-ringstart < 3));
  1417. for (u_int j = 0 ; j < conf.idConfig.length(); j++) {
  1418.     if (conf.idConfig[j].pattern == "SHIELDED_DTMF") { // retrieve DID, e.g. via voice DTMF
  1419. ringstart = Sys::now();
  1420. bool marked = false, gotdigit = false;
  1421. do {
  1422.     int c = server.getModemChar(10000);
  1423.     if (c == 0x10) c = server.getModemChar(10000);
  1424.     if (c == 0x23 || c == 0x2A || (c >= 0x30 && c <= 0x39)) {
  1425. // a DTMF digit was received...
  1426. if (!marked || (marked && !gotdigit)) {
  1427.     protoTrace("MODEM HEARD DTMF: %c", c);
  1428.     callid[j].append(fxStr::format("%c", c));
  1429.     gotdigit = true;
  1430. }
  1431.     } else if (c == 0x2F) {
  1432. // got IS-101 DTMF lead marker
  1433. marked = true;
  1434. gotdigit = false;
  1435.     } else if (c == 0x7E) {
  1436. // got IS-101 DTMF end marker
  1437. marked = false;
  1438. gotdigit = false;
  1439.     } else if (c == 0x73) {
  1440. // got silence, keep waiting
  1441. protoTrace("MODEM HEARD SILENCE");
  1442.     } else if (c == 0x62) {
  1443. // got busy tone, fail
  1444. protoTrace("MODEM HEARD BUSY");
  1445. return (false);
  1446.     } else if (c == 0x63) {
  1447. // got CNG tone, we're not going to get more DTMF, trigger answering
  1448. protoTrace("MODEM HEARD CNG");
  1449. break;
  1450.     }
  1451. } while (callid.length(j) < conf.idConfig[j].answerlength && (Sys::now()-ringstart < 10));
  1452. /*
  1453.  * If the sender doesn't send enough DTMF then we want to answer anyway.
  1454.  */
  1455. while (callid.length(j) < conf.idConfig[j].answerlength) callid[j].append(" ");
  1456.     }
  1457. }
  1458.     }
  1459.     if (conf.dringOn.length()) {              // Compare with all distinctive ring cadences
  1460. modemTrace("WFR: received cadence = %d, %d, %d, %d, %d", incadence[0], incadence[1], incadence[2], incadence[3], incadence[4]);
  1461. type = findCallType(incadence);
  1462.     }
  1463.     gotring = true;
  1464.     break;
  1465. case AT_NOANSWER:
  1466. case AT_NOCARRIER:
  1467. case AT_NODIALTONE:
  1468. case AT_ERROR:
  1469.     return (false);
  1470. }
  1471.     } while (!gotring && Sys::now()-start < timeout);
  1472.     return (gotring);
  1473. }
  1474. CallType ClassModem::findCallType(int vec[])
  1475. {
  1476. // compare x^2 than x to avoid use of math functions
  1477.     double limit = 0.33*0.33;
  1478.     double dif, sum;
  1479.     u_int n, k;
  1480.     for (n=0; n < conf.NoDRings; ++n) {
  1481. for (k=0, sum=0; k < 5; ++k) {
  1482.             dif = vec[k] - conf.distinctiveRings[n].cadence[k];
  1483.             sum += dif*dif;
  1484.         }
  1485. if (sum/conf.distinctiveRings[n].magsqrd < limit)  
  1486.     return conf.distinctiveRings[n].type;
  1487.     }
  1488.     return CALLTYPE_UNKNOWN;
  1489. }
  1490. bool ClassModem::doCallIDDisplay(int i) const { return conf.idConfig[i].display; }
  1491. bool ClassModem::doCallIDRecord(int i) const { return conf.idConfig[i].record; }
  1492. const fxStr& ClassModem::getCallIDLabel(int i) const { return conf.idConfig[i].label; }
  1493. const fxStr& ClassModem::getCallIDType(int i) const { return conf.idConfig[i].type; }