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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: FaxClient.c++,v 1.16 2009/09/29 10:56:42 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 "config.h"
  27. #include "Sys.h"
  28. #include "FaxClient.h"
  29. #include "Transport.h"
  30. #include "zlib.h"
  31. #include <pwd.h>
  32. #include <ctype.h>
  33. #include <sys/types.h>
  34. #include <arpa/telnet.h>
  35. #include <errno.h>
  36. #if HAS_MMAP
  37. #include <sys/mman.h>
  38. #endif
  39. #define N(a) (sizeof (a) / sizeof (a[0]))
  40. FaxClient::FaxClient()
  41. {
  42.     init();
  43. }
  44. FaxClient::FaxClient(const fxStr& hostarg)
  45. {
  46.     init();
  47.     setupHostModem(hostarg);
  48. }
  49. FaxClient::FaxClient(const char* hostarg)
  50. {
  51.     init();
  52.     setupHostModem(hostarg);
  53. }
  54. void
  55. FaxClient::init()
  56. {
  57.     transport = NULL;
  58.     fdIn = NULL;
  59.     fdOut = NULL;
  60.     fdData = -1;
  61.     state = 0;
  62.     pasv = false;
  63.     setupConfig();
  64. }
  65. void
  66. FaxClient::initServerState(void)
  67. {
  68.     type = TYPE_A;
  69.     mode = MODE_S;
  70.     stru = STRU_F;
  71.     format = FORM_UNKNOWN;
  72.     curjob = "DEFAULT";
  73.     tzone = TZ_GMT;
  74.     jobFmt = "";
  75.     recvFmt = "";
  76.     state &= ~(FS_TZPEND|FS_JFMTPEND|FS_RFMTPEND|FS_MFMTPEND|FS_FFMTPEND);
  77. }
  78. FaxClient::~FaxClient()
  79. {
  80.     (void) hangupServer();
  81. }
  82. void
  83. FaxClient::printError(const char* fmt ...)
  84. {
  85.     va_list ap;
  86.     va_start(ap, fmt);
  87.     vprintError(fmt, ap);
  88.     va_end(ap);
  89. }
  90. void
  91. FaxClient::vprintError(const char* fmt, va_list ap)
  92. {
  93.     vfprintf(stderr, fmt, ap);
  94.     fputs("n", stderr);
  95. }
  96. void
  97. FaxClient::printWarning(const char* fmt ...)
  98. {
  99.     va_list ap;
  100.     va_start(ap, fmt);
  101.     vprintWarning(fmt, ap);
  102.     va_end(ap);
  103. }
  104. void
  105. FaxClient::vprintWarning(const char* fmt, va_list ap)
  106. {
  107.     fprintf(stderr, "Warning, ");
  108.     vfprintf(stderr, fmt, ap);
  109.     fputs("n", stderr);
  110. }
  111. void
  112. FaxClient::traceServer(const char* fmt ...)
  113. {
  114.     va_list ap;
  115.     va_start(ap, fmt);
  116.     vtraceServer(fmt, ap);
  117.     va_end(ap);
  118. }
  119. void
  120. FaxClient::vtraceServer(const char* fmt, va_list ap)
  121. {
  122.     vfprintf(stdout, fmt, ap);
  123.     fputs("n", stdout);
  124. }
  125. /*
  126.  * Host, port, and modem can be specified using
  127.  *
  128.  *     modem@host:port
  129.  *
  130.  * e.g. ttyf2@flake.asd:9999.  Alternate forms
  131.  * are: modem@, modem@host, host, host:port.
  132.  * IPv6 IP addresses (many :) are supported in [xx:xx::x]:port
  133.  */
  134. void
  135. FaxClient::setupHostModem(const fxStr& s)
  136. {
  137.     u_int pos = s.next(0, '@');
  138.     if (pos != s.length()) {
  139. modem = s.head(pos);
  140. host = s.tail(s.length() - (pos+1));
  141.     } else
  142. host = s;
  143.     if (host.length() && host[0] == '[')
  144.     {
  145. host.remove(0,1);
  146. pos = host.next(0,']');
  147. if (pos == host.length())
  148.     printWarning("Couldn't parse IPv6 ip address string: "%s"", (const char*)s);
  149. else
  150. host.remove(pos,1);
  151.     pos = host.next(pos, ':');
  152.     } else
  153. pos = host.next(0, ':');
  154.     if (pos != host.length()) {
  155. port = atoi(host.tail(host.length() - (pos+1)));
  156. host.resize(pos);
  157.     }
  158. }
  159. void FaxClient::setupHostModem(const char* cp) { setupHostModem(fxStr(cp)); }
  160. void FaxClient::setHost(const fxStr& hostarg) { setupHostModem(hostarg); }
  161. void FaxClient::setHost(const char* hostarg) { setupHostModem(hostarg); }
  162. void FaxClient::setPort(int p) { port = p; }
  163. void FaxClient::setProtoName(const char* s) { proto = s; }
  164. void FaxClient::setModem(const fxStr& modemarg) { modem = modemarg; }
  165. void FaxClient::setModem(const char* modemarg) { modem = modemarg; }
  166. void
  167. FaxClient::setVerbose(bool v)
  168. {
  169.     if (v)
  170. state |= FS_VERBOSE;
  171.     else
  172. state &= ~FS_VERBOSE;
  173. }
  174. bool
  175. FaxClient::setupUserIdentity(fxStr& emsg)
  176. {
  177.     struct passwd* pwd = NULL;
  178.     const char* name = getenv("FAXUSER");
  179.     if (name)
  180. pwd = getpwnam(name);
  181.     else
  182. pwd = getpwuid(getuid());
  183.     if (!pwd) {
  184. if (name)
  185.     userName = name;
  186. else {
  187.     emsg = fxStr::format("Can not locate your password entry "
  188. "(uid %lu): %s", (u_long) getuid(), strerror(errno));
  189.     return (false);
  190. }
  191.     }
  192.     else
  193. userName = pwd->pw_name;
  194.     if (pwd && pwd->pw_gecos && pwd->pw_gecos[0] != '') {
  195. senderName = pwd->pw_gecos;
  196. senderName.resize(senderName.next(0, '(')); // strip SysV junk
  197. u_int l = senderName.next(0, '&');
  198. if (l < senderName.length()) {
  199.     /*
  200.      * Do the '&' substitution and raise the
  201.      * case of the first letter of the inserted
  202.      * string (the usual convention...)
  203.      */
  204.     senderName.remove(l);
  205.     senderName.insert(userName, l);
  206.     if (islower(senderName[l]))
  207. senderName[l] = toupper(senderName[l]);
  208. }
  209. senderName.resize(senderName.next(0,','));
  210.     } else
  211. senderName = userName;
  212.     if (senderName.length() == 0) {
  213. emsg = "Bad (null) user name; your password file entry"
  214.     " probably has bogus GECOS field information.";
  215. return (false);
  216.     } else
  217. return (true);
  218. }
  219. /*
  220.  * Configuration file support.
  221.  */
  222. FaxClient::F_stringtag FaxClient::strings[] = {
  223. { "protocol", &FaxClient::proto, FAX_PROTONAME },
  224. { "host", &FaxClient::host, NULL },
  225. { "modem", &FaxClient::modem, NULL },
  226. };
  227. FaxClient::F_numbertag FaxClient::numbers[] = {
  228. { "port", &FaxClient::port, (u_int) -1 },
  229. };
  230. void
  231. FaxClient::setupConfig()
  232. {
  233.     int i;
  234.     for (i = N(strings)-1; i >= 0; i--)
  235. (*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
  236.     for (i = N(numbers)-1; i >= 0; i--)
  237. (*this).*numbers[i].p = numbers[i].def;
  238.     initServerState();
  239. }
  240. void
  241. FaxClient::resetConfig()
  242. {
  243.     setupConfig();
  244. }
  245. void
  246. FaxClient::configError(const char* fmt ...)
  247. {
  248.     va_list ap;
  249.     va_start(ap, fmt);
  250.     vprintError(fmt, ap);
  251.     va_end(ap);
  252. }
  253. void
  254. FaxClient::configTrace(const char* fmt ...)
  255. {
  256.     if (getVerbose()) {
  257. va_list ap;
  258. va_start(ap, fmt);
  259. vprintWarning(fmt, ap);
  260. va_end(ap);
  261.     }
  262. }
  263. bool
  264. FaxClient::setConfigItem(const char* tag, const char* value)
  265. {
  266.     u_int ix;
  267.     if (findTag(tag, (const tags*)strings, N(strings), ix)) {
  268. (*this).*strings[ix].p = value;
  269.     } else if (findTag(tag, (const tags*)numbers, N(numbers), ix)) {
  270. (*this).*numbers[ix].p = atoi(value);
  271.     } else if (streq(tag, "verbose")) {
  272. if (getBoolean(value))
  273.     state |= FS_VERBOSE;
  274. else
  275.     state &= ~FS_VERBOSE;
  276.     } else if (streq(tag, "timezone") || streq(tag, "tzone")) {
  277. setTimeZone(streq(value, "local") ? TZ_LOCAL : TZ_GMT);
  278.     } else if (streq(tag, "jobfmt")) {
  279. setJobStatusFormat(value);
  280.     } else if (streq(tag, "rcvfmt")) {
  281. setRecvStatusFormat(value);
  282.     } else if (streq(tag, "modemfmt")) {
  283. setModemStatusFormat(value);
  284.     } else if (streq(tag, "filefmt")) {
  285. setFileStatusFormat(value);
  286.     } else if (streq(tag, "passivemode")) {
  287. pasv = getBoolean(value);
  288.     } else
  289. return (false);
  290.     return (true);
  291. }
  292. bool
  293. FaxClient::callServer(fxStr& emsg)
  294. {
  295.     if (host.length() == 0) { // if host not specified by -h
  296. const char* cp = getenv("FAXSERVER");
  297. if (cp && *cp != '') {
  298.     if (modem != "") { // don't clobber specified modem
  299. fxStr m(modem);
  300. setupHostModem(cp);
  301. modem = m;
  302.     } else
  303. setupHostModem(cp);
  304. }
  305.     }
  306.     transport = &Transport::getTransport(*this, host);
  307.     if (transport->callServer(emsg)) {
  308. signal(SIGPIPE, fxSIGHANDLER(SIG_IGN));
  309. /*
  310.  * Transport code is expected to call back through
  311.  * setCtrlFds so fdIn should be properly setup...
  312.  */
  313. if (fdIn == NULL)
  314.     return (false);
  315. int rep = PRELIM;
  316. for (int i = 0; rep == PRELIM && i < 100; i++)
  317.     rep = getReply(false);
  318. return (rep == COMPLETE);
  319.     } else
  320. return (false);
  321. }
  322. bool
  323. FaxClient::hangupServer(void)
  324. {
  325.     if (fdIn != NULL) {
  326. if (transport) {
  327.     closeDataConn();
  328.     (void) transport->hangupServer();
  329. }
  330. fclose(fdIn), fdIn = NULL;
  331.     }
  332.     if (fdOut != NULL)
  333. fclose(fdOut), fdOut = NULL;
  334.     /*
  335.      * Reset state in case another call is placed.
  336.      */
  337.     delete transport, transport = NULL;
  338.     initServerState();
  339.     return (true);
  340. }
  341. void
  342. FaxClient::setCtrlFds(int in, int out)
  343. {
  344.     if (fdIn != NULL)
  345. fclose(fdIn);
  346.     fdIn = fdopen(in, "r");
  347.     if (fdOut != NULL)
  348. fclose(fdOut);
  349.     fdOut = fdopen(out, "w");
  350. }
  351. void
  352. FaxClient::setDataFd(int fd)
  353. {
  354.     if (fdData >= 0)
  355. Sys::close(fdData);
  356.     fdData = fd;
  357. }
  358. /*
  359.  * Do login procedure.
  360.  */
  361. bool
  362. FaxClient::login(const char* user, const char* pass, fxStr& emsg)
  363. {
  364.     if (user == NULL) {
  365. setupUserIdentity(emsg);
  366. user = userName;
  367.     }
  368.     if (*user == '') {
  369. emsg = "Malformed (null) username";
  370. return (false);
  371.     }
  372.     int n = command("USER %s", user);
  373.     if (n == CONTINUE)
  374. n = command("PASS %s", pass ? pass : getPasswd("Password:"));
  375.     if (n == CONTINUE) // XXX not used
  376. n = command("ACCT %s", getPasswd("Account:"));
  377.     if (n == COMPLETE)
  378. state |= FS_LOGGEDIN;
  379.     else
  380. state &= ~FS_LOGGEDIN;
  381.     if (isLoggedIn()) {
  382. if (state&FS_TZPEND) {
  383.     u_int tz = tzone;
  384.     tzone = 0;
  385.     (void) setTimeZone(tz);
  386.     state &= ~FS_TZPEND;
  387. }
  388. return (true);
  389.     } else {
  390. emsg = "Login failed: " | lastResponse;
  391. return (false);
  392.     }
  393. }
  394. /*
  395.  * Prompt for a password.
  396.  */
  397. const char*
  398. FaxClient::getPasswd(const char* prompt)
  399. {
  400.     return (getpass(prompt));
  401. }
  402. /*
  403.  * Do admin login procedure.
  404.  */
  405. bool
  406. FaxClient::admin(const char* pass, fxStr& emsg)
  407. {
  408.     if (command("ADMIN %s", pass ? pass : getpass("Password:")) != COMPLETE) {
  409. emsg = "Admin failed: " | lastResponse;
  410. return (false);
  411.     } else
  412. return (true);
  413. }
  414. bool
  415. FaxClient::setCommon(FaxParam& parm, u_int v)
  416. {
  417.     if (v != this->*parm.pv) {
  418. if (0 < v && v < parm.NparmNames) {
  419.     if (command("%s %s", parm.cmd, parm.parmNames[v]) != COMPLETE) {
  420. printError("%s", (const char*) lastResponse);
  421. return (false);
  422.     }
  423. } else {
  424.     printError("Bad %s parameter value %u.", parm.cmd, v);
  425.     return (false);
  426. }
  427. this->*parm.pv = v;
  428.     }
  429.     return (true);
  430. }
  431. static const char* typeNames[] = { "", "A", "E", "I", "L" };
  432. FaxClient::FaxParam FaxClient::typeParam =
  433.     { "TYPE", typeNames, N(typeNames), &FaxClient::type };
  434. bool FaxClient::setType(u_int v) { return setCommon(typeParam, v); }
  435. static const char* modeNames[] = { "", "S", "B", "C", "Z" };
  436. FaxClient::FaxParam FaxClient::modeParam =
  437.     { "MODE", modeNames, N(modeNames), &FaxClient::mode };
  438. bool FaxClient::setMode(u_int v) { return setCommon(modeParam, v); }
  439. static const char* struNames[] = { "", "F", "R", "P", "T" };
  440. FaxClient::FaxParam FaxClient::struParam =
  441.     { "STRU", struNames, N(struNames), &FaxClient::stru };
  442. bool FaxClient::setStruct(u_int v) { return setCommon(struParam, v); }
  443. static const char* formNames[] = { "", "PS", "PS2", "TIFF", "PCL", "PDF" };
  444. FaxClient::FaxParam FaxClient::formParam =
  445.     { "FORM", formNames, N(formNames), &FaxClient::format };
  446. bool FaxClient::setFormat(u_int v) { return setCommon(formParam, v); }
  447. static const char* tzoneNames[] = { "", "GMT", "LOCAL" };
  448. FaxClient::FaxParam FaxClient::tzoneParam =
  449.     { "TZONE", tzoneNames, N(tzoneNames), &FaxClient::tzone };
  450. bool
  451. FaxClient::setTimeZone(u_int v)
  452. {
  453.     if (!isLoggedIn()) { // set and mark pending accordingly
  454.         if (0 < v && v < N(tzoneNames)) {
  455.             tzone = v;
  456.             if (v == TZ_GMT) state &= ~FS_TZPEND;
  457.             else state |= FS_TZPEND;
  458.         } else {
  459.             printError("Bad time zone parameter value %u.", v);
  460.             return (false);
  461.         }
  462.         return (true);
  463.     } else { // pass directly to server
  464.         return setCommon(tzoneParam, v);
  465.     }
  466. }
  467. /*
  468.  * Data connection support.
  469.  *
  470.  * Separate connections are used for data transfers.
  471.  * The transport classes handle the work since it is
  472.  * inherently transport-specific.  Connections are 
  473.  * setup in a 2-step process because of the need (in
  474.  * the TCP world) to setup a listening socket prior to
  475.  * issuing a server command that causes the data connection
  476.  * to be established.  Thus the expected protocol is to
  477.  * initialize (initDataConn), issue a server command,
  478.  * then open (openDataConn); after which data can be
  479.  * transfered over the connection.  When completed the
  480.  * connection should be closed (closeDataConn).
  481.  *
  482.  * Outbound connections can be terminated simply by
  483.  * closing the data connection.  Inbound connections
  484.  * must be aborted with ABOR command that is sent in
  485.  * a transport-specific way (e.g. for TCP the message
  486.  * is sent as urgent data).  The abortDataConn interface
  487.  * is provided for this use.
  488.  */
  489. bool
  490. FaxClient::initDataConn(fxStr& emsg)
  491. {
  492.     closeDataConn();
  493.     if (transport) {
  494.         if (!transport->initDataConn(emsg)) {
  495.             if (emsg == "") {
  496.                 emsg = "Unable to initialize data connection to server";
  497.             }
  498.             return (false);
  499.         }
  500.     }
  501.     return (true);
  502. }
  503. bool
  504. FaxClient::openDataConn(fxStr& emsg)
  505. {
  506.     if (transport) {
  507.         if (!transport->openDataConn(emsg)) {
  508.             if (emsg == "") {
  509.              emsg = "Unable to open data connection to server";
  510.             }
  511.         return (false);
  512.         }
  513.     }
  514.     return (true);
  515. }
  516. void
  517. FaxClient::closeDataConn(void)
  518. {
  519.     if (fdData >= 0) {
  520.         transport->closeDataConn(fdData);
  521.         fdData = -1;
  522.     }
  523. }
  524. bool
  525. FaxClient::abortDataConn(fxStr& emsg)
  526. {
  527.     if (fdData >= 0 && transport) {
  528.         fflush(fdOut);
  529.         if (!transport->abortCmd(emsg)) {
  530.             if (emsg == "") {
  531.              emsg = "Unable to abort data connection to server";
  532.             }
  533.         return (false);
  534.         }
  535. #ifdef notdef
  536.         /*
  537.          * Flush data from data connection.
  538.          */
  539.         int flags = fcntl(fdData, F_GETFL, 0);
  540.         fcntl(fdData, F_SETFL, flags | FNONBLK);
  541.         while (Sys::read(fdData, buf, sizeof (buf)) > 0);
  542.         fcntl(fdData, F_SETFL, flags);
  543. #endif
  544.         /*
  545.          * Server should send a reply that acknowledges the
  546.          * existing operation is aborted followed by an ack
  547.          * of the ABOR command itself.
  548.          */
  549.         if (getReply(false) != TRANSIENT || // 4xx operation aborted
  550.                 getReply(false) != COMPLETE) { // 2xx abort successful
  551.             unexpectedResponse(emsg);
  552.             return (false);
  553.         }
  554.     }
  555.     return (true);
  556. }
  557. void
  558. FaxClient::lostServer(void)
  559. {
  560.     printError("Service not available, remote server closed connection");
  561.     hangupServer();
  562. }
  563. void
  564. FaxClient::unexpectedResponse(fxStr& emsg)
  565. {
  566.     emsg = "Unexpected server response: " | lastResponse;
  567. }
  568. void
  569. FaxClient::protocolBotch(fxStr& emsg, const char* fmt ...)
  570. {
  571.     va_list ap;
  572.     va_start(ap, fmt);
  573.     emsg = "Protocol botch" | fxStr::vformat(fmt, ap);
  574.     va_end(ap);
  575. }
  576. /*
  577.  * Send a command and wait for a response.
  578.  * The primary response code is returned.
  579.  */
  580. int
  581. FaxClient::command(const char* fmt ...)
  582. {
  583.     va_list ap1, ap2;
  584.     va_start(ap1, fmt);
  585.     va_start(ap2, fmt);
  586.     int r = vcommand(fmt, ap1, ap2);
  587.     va_end(ap1);
  588.     va_end(ap2);
  589.     return (r);
  590. }
  591. /*
  592.  * Send a command and wait for a response.
  593.  * The primary response code is returned.
  594.  */
  595. int
  596. FaxClient::vcommand(const char* fmt, va_list ap1, va_list ap2)
  597. {
  598.     if (getVerbose()) {
  599.         if (strncasecmp("PASS ", fmt, 5) == 0) {
  600.             traceServer("-> PASS XXXX");
  601.         } else if (strncasecmp("ADMIN ", fmt, 6) == 0) {
  602.             traceServer("-> ADMIN XXXX");
  603.         } else {
  604.     char* line = (char *)malloc(1024);
  605.     if (line == NULL)
  606. printError("Memory allocation failed");
  607.     else {
  608. vsnprintf(line, 1024, fmt, ap1);
  609. traceServer("-> %s", line);
  610.     }
  611.     free(line);
  612.         }
  613.     }
  614.     if (fdOut == NULL) {
  615.         printError("No control connection for command");
  616.         code = -1;
  617.         return (0);
  618.     }
  619.     vfprintf(fdOut, fmt, ap2);
  620.     fputs("rn", fdOut);
  621.     (void) fflush(fdOut);
  622.     return (getReply(strncmp(fmt, "QUIT", 4) == 0));
  623. }
  624. /*
  625.  * Extract a valid reply code from a string.
  626.  * The code must be 3 numeric digits followed
  627.  * by a space or ``-'' (the latter indicates
  628.  * the server reponse is to be continued with
  629.  * one or more lines).  If no valid reply code
  630.  * is recognized zero is returned--this is
  631.  * assumed to be an invalid reply code.
  632.  */
  633. static int
  634. getReplyCode(const char* cp)
  635. {
  636.     if (!isdigit(cp[0])) return (0);
  637.     int c = (cp[0] - '0');
  638.     if (!isdigit(cp[1])) return (0);
  639.     c = 10 * c + (cp[1] - '0');
  640.     if (!isdigit(cp[2])) return (0);
  641.     c = 10 * c + (cp[2] - '0');
  642.     return ((cp[3] == ' ' || cp[3] == '-') ? c : 0);
  643. }
  644. /*
  645.  * Read from the control channel until a valid reply is
  646.  * received or the connection is dropped.  The last line
  647.  * of the received response is left in FaxClient::lastResponse
  648.  * and the reply code is left in FaxClient::code.  The
  649.  * primary response (the first digit of the reply code)
  650.  * is returned to the caller.  Continuation lines are
  651.  * collected separately.
  652.  */
  653. int
  654. FaxClient::getReply(bool expecteof)
  655. {
  656.     int firstCode = 0;
  657.     bool continuation = false;
  658.     lastContinuation.resize(0);
  659.     do {
  660.         lastResponse.resize(0);
  661.         int c;
  662.         while ((c = getc(fdIn)) != 'n') {
  663.             if (c == IAC) {     // handle telnet commands
  664.              switch (c = getc(fdIn)) {
  665.              case WILL:
  666.                 case WONT:
  667.                  c = getc(fdIn);
  668.                  fprintf(fdOut, "%c%c%c", IAC, DONT, c);
  669.                     (void) fflush(fdOut);
  670.                     break;
  671.                 case DO:
  672.                 case DONT:
  673.                     c = getc(fdIn);
  674.                     fprintf(fdOut, "%c%c%c", IAC, WONT, c);
  675.                     (void) fflush(fdOut);
  676.                     break;
  677.                 default:
  678.                     break;
  679.                 }
  680.                 continue;
  681.             }
  682.             if (c == EOF) {
  683.              if (expecteof) {
  684.                     code = 221;
  685.                     return (0);
  686.                 } else {
  687.                     lostServer();
  688.                     code = 421;
  689.                     return (4);
  690.                 }
  691.             }
  692.             if (c != 'r') lastResponse.append(c);
  693.         }
  694.         if (getVerbose()) {
  695.             traceServer("%s", (const char*) lastResponse);
  696.         }
  697.         code = getReplyCode(lastResponse);
  698.         if (code != 0) { // found valid reply code
  699.             if (lastResponse[3] == '-') { // continuation line
  700.                 if (firstCode == 0) // first line of reponse
  701.                     firstCode = code;
  702.                 continuation = true;
  703.             } else if (code == firstCode) // end of continued reply
  704.                 continuation = false;
  705.         }
  706. if (continuation) {
  707.     lastContinuation.append(&lastResponse[4]);
  708.     lastContinuation.append("n");
  709. }
  710.     } while (continuation || code == 0);
  711.     if (code == 421) { // server closed connection
  712.         lostServer();
  713.     }
  714.     return code / 100;
  715. }
  716. /*
  717.  * Extract a string from a reply message.  The
  718.  * string that is extracted is expected to follow
  719.  * a pattern string.  The pattern is tried both
  720.  * in the initial case and then the inverse case
  721.  * (upper or lower depending on what the original
  722.  * case was).  The resulting string is checked to
  723.  * make sure that it is not null.
  724.  */
  725. bool
  726. FaxClient::extract(u_int& pos, const char* pattern, fxStr& result,
  727.     const char* cmd, fxStr& emsg)
  728. {
  729.     fxStr pat(pattern);
  730.     u_int l = lastResponse.find(pos, pat);
  731.     if (l == lastResponse.length()) { // try inverse-case version
  732.         if (isupper(pattern[0])) {
  733.             pat.lowercase();
  734.         } else {
  735.             pat.raisecase();
  736.         }
  737.         l = lastResponse.find(pos, pat);
  738.     }
  739.     if (l == lastResponse.length()) {
  740.         protocolBotch(emsg, ": No "%s" in %s response: %s",
  741.             pattern, cmd, (const char*) lastResponse);
  742.         return false;
  743.     }
  744.     l = lastResponse.skip(l+pat.length(), ' ');// skip white space
  745.     result = lastResponse.extract(l, lastResponse.next(l, ' ')-l);
  746.     if (result == "") {
  747.         protocolBotch(emsg, ": Null %s in %s response: %s",
  748.             pattern, cmd, (const char*) lastResponse);
  749.         return false;
  750.     }
  751.     pos = l; // update position
  752.     return true;
  753. }
  754. /*
  755.  * Create a new job and return its job-id
  756.  * and group-id, parsed from the reply.
  757.  */
  758. bool
  759. FaxClient::newJob(fxStr& jobid, fxStr& groupid, fxStr& emsg)
  760. {
  761.     if (command("JNEW") == COMPLETE) {
  762.         if (code == 200) {
  763.             /*
  764.              * The response should be of the form:
  765.              *
  766.              * 200 ... jobid: xxxx groupid: yyyy.
  767.              *
  768.              * where xxxx is the ID for the new job and yyyy is the
  769.              * ID of the new job's group.
  770.              */
  771.             u_int l = 0;
  772.             if (extract(l, "jobid:", jobid, "JNEW", emsg) &&
  773.                     extract(l, "groupid:", groupid, "JNEW", emsg)) {
  774.                 /*
  775.                  * Force job and groupd IDs to be numeric;
  776.                  * this deals with servers that want to append
  777.                  * punctuation such as ``,'' or ``.''.
  778.                  */
  779.                 jobid.resize(jobid.skip(0, "0123456789"));
  780.                 groupid.resize(groupid.skip(0, "0123456789"));
  781.                 curjob = jobid;
  782.                 return true;
  783.             }
  784.         } else {
  785.             unexpectedResponse(emsg);
  786.         }
  787.     } else {
  788.         emsg = lastResponse;
  789.     }
  790.     return false;
  791. }
  792. /*
  793.  * Set the current job on the server.
  794.  */
  795. bool
  796. FaxClient::setCurrentJob(const char* jobid)
  797. {
  798.     if (strcasecmp(jobid, curjob) != 0) {
  799.         if (command("JOB %s", jobid) != COMPLETE) {
  800.             return false;
  801.         }
  802.         curjob = jobid;
  803.     }
  804.     return true;
  805. }
  806. bool
  807. FaxClient::jobParm(const char* name, const fxStr& value)
  808. {
  809.     /*
  810.      * We need to quote any " marks in the string before
  811.      * we pass it on to the raw jobParm(... const char*)
  812.      */
  813.     if (value.next(0,'"'))
  814.     {
  815. fxStr tmp(value);
  816. int r = tmp.length();
  817. while (r > 0)
  818. {
  819.     if ( (r = tmp.nextR(r-1, '"') ) > 0 )
  820. tmp.insert('\', r-1);
  821. }
  822. return jobParm(name, (const char*)tmp);
  823.     }
  824.     return jobParm(name, (const char*) value);
  825. }
  826. bool
  827. FaxClient::jobParm(const char* name, const char* value)
  828. {
  829.     /*
  830.      * if they're passing us a wrong char*, we expect
  831.      * them to have handled any quoting requried.
  832.      */
  833.     return (command("JPARM %s "%s"", name, value) == COMPLETE);
  834. }
  835. bool
  836. FaxClient::jobParm(const char* name, bool b)
  837. {
  838.     return (command("JPARM %s %s", name, b ? "YES" : "NO") == COMPLETE);
  839. }
  840. bool
  841. FaxClient::jobParm(const char* name, u_int v)
  842. {
  843.     return (command("JPARM %s %u", name, v) == COMPLETE);
  844. }
  845. bool
  846. FaxClient::jobParm(const char* name, float v)
  847. {
  848.     return (command("JPARM %s %g", name, v) == COMPLETE);
  849. }
  850. bool
  851. FaxClient::jobSendTime(const struct tm tm)
  852. {
  853.     return (command("JPARM SENDTIME %d%02d%02d%02d%02d"
  854.         , tm.tm_year+1900
  855.         , tm.tm_mon+1
  856.         , tm.tm_mday
  857.         , tm.tm_hour
  858.         , tm.tm_min
  859.         ) == COMPLETE);
  860. }
  861. bool
  862. FaxClient::jobLastTime(u_long tv)
  863. {
  864.     return (command("JPARM LASTTIME %02d%02d%02d",
  865.         tv/(24*60*60), (tv/(60*60))%24, (tv/60)%60) == COMPLETE);
  866. }
  867. bool
  868. FaxClient::jobRetryTime(u_long tv)
  869. {
  870.     return (command("JPARM RETRYTIME %02d%02d", tv/60, tv%60) == COMPLETE);
  871. }
  872. bool
  873. FaxClient::jobCover(const char* docname)
  874. {
  875.     return (command("JPARM COVER %s", docname) == COMPLETE);
  876. }
  877. bool
  878. FaxClient::jobDocument(const char* docname)
  879. {
  880.     return (command("JPARM DOCUMENT %s", docname) == COMPLETE);
  881. }
  882. bool
  883. FaxClient::jobPollRequest(const char* sep, const char* pwd)
  884. {
  885.     return (command("JPARM POLL "%s" "%s"", sep, pwd) == COMPLETE);
  886. }
  887. bool
  888. FaxClient::jobOp(const char* op, const char* jobid)
  889. {
  890.     return (command(jobid == curjob ? "%s" : "%s %s", op, jobid) == COMPLETE);
  891. }
  892. bool FaxClient::jobSubmit(const char* jobid) { return jobOp("JSUBM",jobid); }
  893. bool FaxClient::jobSuspend(const char* jobid) { return jobOp("JSUSP",jobid); }
  894. bool FaxClient::jobKill(const char* jobid) { return jobOp("JKILL",jobid); }
  895. bool FaxClient::jobDelete(const char* jobid) { return jobOp("JDELE",jobid); }
  896. bool FaxClient::jobWait(const char* jobid) { return jobOp("JWAIT",jobid); }
  897. bool FaxClient::jgrpSubmit(const char* jgrpid)
  898.     { return (command("JGSUBM %s", jgrpid) == COMPLETE); }
  899. bool FaxClient::jgrpSuspend(const char* jgrpid)
  900.     { return (command("JGSUSP %s", jgrpid) == COMPLETE); }
  901. bool FaxClient::jgrpKill(const char* jgrpid)
  902.     { return (command("JGKILL %s", jgrpid) == COMPLETE); }
  903. bool FaxClient::jgrpWait(const char* jgrpid)
  904.     { return (command("JGWAIT %s", jgrpid) == COMPLETE); }
  905. bool
  906. FaxClient::runScript(const char* filename, fxStr& emsg)
  907. {
  908.     bool ok = false;
  909.     FILE* fd = fopen(filename, "r");
  910.     if (fd != NULL) {
  911. ok = runScript(fd, filename, emsg);
  912. fclose(fd);
  913.     } else
  914. emsg = fxStr::format("Unable to open script file "%s".", filename);
  915.     return (ok);
  916. }
  917. bool
  918. FaxClient::runScript(FILE* fp, const char* filename, fxStr& emsg)
  919. {
  920.     bool ok = false;
  921.     struct stat sb;
  922.     (void) Sys::fstat(fileno(fp), sb);
  923.     char* addr;
  924. #if HAS_MMAP
  925.     addr = (char*) mmap(NULL, (size_t) sb.st_size, PROT_READ, MAP_SHARED, fileno(fp), 0);
  926.     if (addr == (char*) -1) { // revert to file reads
  927. #endif
  928. addr = new char[sb.st_size];
  929. if (Sys::read(fileno(fp), addr, (u_int) sb.st_size) == sb.st_size)
  930.     ok = runScript(addr, sb.st_size, filename, emsg);
  931. else
  932.     emsg = fxStr::format("%s: Read error: %s",
  933. filename, strerror(errno));
  934. delete [] addr;
  935. #if HAS_MMAP
  936.     } else { // send mmap'd file data
  937. ok = runScript(addr, sb.st_size, filename, emsg);
  938. munmap(addr, (size_t) sb.st_size);
  939.     }
  940. #endif
  941.     return (ok);
  942. }
  943. bool
  944. FaxClient::runScript(const char* script, u_long scriptLen,
  945.     const char* filename, fxStr& emsg)
  946. {
  947.     u_int lineno = 0;
  948.     while (scriptLen > 0) {
  949. lineno++;
  950. const char* ep = strchr(script, 'n');
  951. if (!ep)
  952.     ep = script+scriptLen;
  953. u_int cmdLen = ep-script;
  954. if (cmdLen > 1) {
  955.     if (command("%.*s", cmdLen, script) != COMPLETE) {
  956. emsg = fxStr::format("%s: line %u: %s",
  957.     filename, lineno, (const char*) lastResponse);
  958. return (false);
  959.     }
  960. }
  961. if (*ep == 'n')
  962.     ep++;
  963. scriptLen -= ep - script;
  964. script = ep;
  965.     }
  966.     return (true);
  967. }
  968. /*
  969.  * Create a uniquely named document on the server
  970.  * that is not removed when the server exits.
  971.  */
  972. bool
  973. FaxClient::storeUnique(fxStr& docname, fxStr& emsg)
  974. {
  975.     return storeUnique("STOU", docname, emsg);
  976. }
  977. /*
  978.  * Create a uniquely named document on the server
  979.  * that is automatically removed when the server exits.
  980.  */
  981. bool
  982. FaxClient::storeTemp(fxStr& docname, fxStr& emsg)
  983. {
  984.     return storeUnique("STOT", docname, emsg);
  985. }
  986. /*
  987.  * Send a STOU/STOT command and parse the
  988.  * response to get the resulting filename.
  989.  */
  990. bool
  991. FaxClient::storeUnique(const char* cmd, fxStr& docname, fxStr& emsg)
  992. {
  993.     if (command(cmd) == PRELIM) {
  994. if (code == 150) {
  995.     /*
  996.      * According to RFC 1123, the response must be of the form:
  997.      *
  998.      * 150 FILE: pppp[ anything]
  999.      *
  1000.      * where pppp is the unique document name chosen by the server.
  1001.      */
  1002.     u_int l = 0;
  1003.     return (extract(l, "FILE:", docname, cmd, emsg));
  1004. } else
  1005.     unexpectedResponse(emsg);
  1006.     } else
  1007. emsg = lastResponse;
  1008.     return (false);
  1009. }
  1010. /*
  1011.  * Create/overwrite a file on the server.
  1012.  */
  1013. bool
  1014. FaxClient::storeFile(fxStr& docname, fxStr& emsg)
  1015. {
  1016.     if (command("STOR " | docname) != PRELIM) {
  1017. emsg = lastResponse;
  1018. return (false);
  1019.     }
  1020.     if (code != 150) {
  1021. unexpectedResponse(emsg);
  1022. return (false);
  1023.     }
  1024.     return (true);
  1025. }
  1026. /*
  1027.  * Send a block of raw data on the data
  1028.  * conenction, interpreting write errors.
  1029.  */
  1030. bool
  1031. FaxClient::sendRawData(void* buf, int cc, fxStr& emsg)
  1032. {
  1033. #ifdef __linux__
  1034.     /*
  1035.      * Linux kernel bug: can get short writes on
  1036.      * stream sockets when setup for blocking i/o.
  1037.      */
  1038.     u_char* bp = (u_char*) buf;
  1039.     for (int cnt, sent = 0; cc; sent += cnt, cc -= cnt) 
  1040. if ((cnt = write(fdData, bp + sent, cc)) <= 0) {
  1041.     protocolBotch(emsg, errno == EPIPE ?
  1042. " (server closed connection)" : " (server write error: %s).",
  1043. strerror(errno));
  1044.     return (false);
  1045. }
  1046. #else
  1047.     if (write(fdData, buf, cc) != cc) {
  1048. protocolBotch(emsg, errno == EPIPE ?
  1049.     " (server closed connection)" : " (server write error: %s).",
  1050.     strerror(errno));
  1051. return (false);
  1052.     }
  1053. #endif
  1054.     return (true);
  1055. }
  1056. /*
  1057.  * Send a document file using stream (uncompressed) mode
  1058.  * and the current transfer parameters.  The server-side
  1059.  * document name where data gets placed is returned.
  1060.  */
  1061. bool
  1062. FaxClient::sendData(int fd,
  1063.     bool (FaxClient::*store)(fxStr&, fxStr&), fxStr& docname, fxStr& emsg)
  1064. {
  1065.     char* addr = (char*) -1;
  1066.     struct stat sb;
  1067.     size_t cc;
  1068.     (void) Sys::fstat(fd, sb);
  1069.     if (getVerbose())
  1070. traceServer("SEND data, %lu bytes", (u_long) sb.st_size);
  1071.     if (!initDataConn(emsg))
  1072. goto bad;
  1073.     if (!setMode(MODE_S))
  1074. goto bad;
  1075.     if (!(this->*store)(docname, emsg))
  1076. goto bad;
  1077.     if (!openDataConn(emsg))
  1078. goto bad;
  1079. #if HAS_MMAP
  1080.     addr = (char*) mmap(NULL, (size_t) sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
  1081.     if (addr == (char*) -1) { // revert to file reads
  1082. #endif
  1083. cc = (size_t) sb.st_size;
  1084. while (cc > 0) {
  1085.     char buf[32*1024]; // XXX better if page-aligned
  1086.     size_t n = fxmin(cc, sizeof (buf));
  1087.     if (read(fd, buf, n) != (ssize_t)n) {
  1088. protocolBotch(emsg, " (data read: %s).", strerror(errno));
  1089. goto bad;
  1090.     }
  1091.     if (!sendRawData(buf, n, emsg))
  1092. goto bad;
  1093.     cc -= n;
  1094. }
  1095. #if HAS_MMAP
  1096.     } else if (!sendRawData(addr, (int) sb.st_size, emsg)) // send mmap'd file data
  1097. goto bad;
  1098. #endif
  1099.     closeDataConn();
  1100. #if HAS_MMAP
  1101.     if (addr != (char*) -1)
  1102. munmap(addr, (size_t) sb.st_size);
  1103. #endif
  1104.     return (getReply(false) == 2);
  1105. bad:
  1106.     closeDataConn();
  1107. #if HAS_MMAP
  1108.     if (addr != (char*) -1)
  1109. munmap(addr, (size_t) sb.st_size);
  1110. #endif
  1111.     return (false);
  1112. }
  1113. /*
  1114.  * Send a document file using zip-compressed mode
  1115.  * and the current transfer parameters.  The server-side
  1116.  * document name where data gets placed is returned.
  1117.  */
  1118. bool
  1119. FaxClient::sendZData(int fd,
  1120.     bool (FaxClient::*store)(fxStr&, fxStr&), fxStr& docname, fxStr& emsg)
  1121. {
  1122.     z_stream zstream;
  1123.     zstream.zalloc = NULL;
  1124.     zstream.zfree = NULL;
  1125.     zstream.opaque = NULL;
  1126.     zstream.data_type = Z_BINARY;
  1127.     if (deflateInit(&zstream, Z_DEFAULT_COMPRESSION) == Z_OK) {
  1128. #if HAS_MMAP
  1129. char* addr = (char*) -1; // mmap'd file
  1130. #endif
  1131. char obuf[32*1024]; // XXX better if page-aligned
  1132. zstream.next_out = (Bytef*) obuf;
  1133. zstream.avail_out = sizeof (obuf);
  1134. struct stat sb;
  1135. size_t cc;
  1136. Sys::fstat(fd, sb);
  1137. if (getVerbose())
  1138.     traceServer("SEND compressed data, %lu bytes", (u_long) sb.st_size);
  1139. if (!initDataConn(emsg))
  1140.     goto bad;
  1141. if (!setMode(MODE_Z))
  1142.     goto bad;
  1143. if (!(this->*store)(docname, emsg))
  1144.     goto bad;
  1145. if (!openDataConn(emsg))
  1146.     goto bad;
  1147. #if HAS_MMAP
  1148. addr = (char*) mmap(NULL, (size_t) sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
  1149. if (addr == (char*) -1) { // revert to file reads
  1150. #endif
  1151.     cc = (size_t) sb.st_size;
  1152.     while (cc > 0) {
  1153. char buf[32*1024];
  1154. int n = fxmin((size_t) cc, sizeof (buf));
  1155. if (read(fd, buf, n) != n) {
  1156.     protocolBotch(emsg, " (data read: %s)", strerror(errno));
  1157.     goto bad;
  1158. }
  1159. zstream.next_in = (Bytef*) buf;
  1160. zstream.avail_in = n;
  1161. do {
  1162.     if (deflate(&zstream, Z_NO_FLUSH) != Z_OK) {
  1163. emsg = fxStr::format("zlib compressor error: %s",
  1164.     zstream.msg);
  1165. goto bad;
  1166.     }
  1167.     if (zstream.avail_out == 0) {
  1168. if (!sendRawData(obuf, sizeof (obuf), emsg))
  1169.     goto bad2;
  1170. zstream.next_out = (Bytef*) obuf;
  1171. zstream.avail_out = sizeof (obuf);
  1172.     }
  1173. } while (zstream.avail_in > 0);
  1174. cc -= n;
  1175.     }
  1176.     zstream.avail_in = 0;
  1177. #if HAS_MMAP
  1178. } else {
  1179.     zstream.next_in = (Bytef*) addr;
  1180.     zstream.avail_in = (u_int) sb.st_size;
  1181.     do {
  1182. if (deflate(&zstream, Z_NO_FLUSH) != Z_OK) {
  1183.     emsg = fxStr::format("zlib compressor error: %s",
  1184. zstream.msg);
  1185.     goto bad;
  1186. }
  1187. if (zstream.avail_out == 0) {
  1188.     if (!sendRawData(obuf, sizeof (obuf), emsg))
  1189. goto bad2;
  1190.     zstream.next_out = (Bytef*) obuf;
  1191.     zstream.avail_out = sizeof (obuf);
  1192. }
  1193.     } while (zstream.avail_in > 0);
  1194. }
  1195. #endif
  1196. int dstate;
  1197. do {
  1198.     switch (dstate = deflate(&zstream, Z_FINISH)) {
  1199.     case Z_STREAM_END:
  1200.     case Z_OK:
  1201. if (zstream.avail_out != sizeof (obuf)) {
  1202.     if (!sendRawData(obuf, sizeof (obuf) - zstream.avail_out, emsg))
  1203. goto bad2;
  1204.     zstream.next_out = (Bytef*) obuf;
  1205.     zstream.avail_out = sizeof (obuf);
  1206. }
  1207. break;
  1208.     default:
  1209. emsg = fxStr::format("zlib compressor error: %s",
  1210.     zstream.msg);
  1211. goto bad;
  1212.     }
  1213. } while (dstate != Z_STREAM_END);
  1214. if (getVerbose())
  1215.     traceServer("SEND %lu bytes transmitted (%.1fx compression)",
  1216. #define NZ(x) ((x)?(x):1)
  1217. zstream.total_out, float(sb.st_size) / NZ(zstream.total_out));
  1218. closeDataConn();
  1219. #if HAS_MMAP
  1220. if (addr != (char*) -1)
  1221.     munmap(addr, (size_t) sb.st_size);
  1222. #endif
  1223. deflateEnd(&zstream);
  1224. return (getReply(false) == COMPLETE);
  1225. bad2:
  1226. (void) getReply(false);
  1227. /* fall thru... */
  1228. bad:
  1229. closeDataConn();
  1230. #if HAS_MMAP
  1231. if (addr != (char*) -1)
  1232.     munmap(addr, (size_t) sb.st_size);
  1233. #endif
  1234. deflateEnd(&zstream);
  1235.     } else
  1236. emsg = fxStr::format("Can not initialize compression library: %s",
  1237.     zstream.msg);
  1238.     return (false);
  1239. }
  1240. /*
  1241.  * Receive data using stream mode and the current
  1242.  * transfer parameters.  The supplied arguments are
  1243.  * passed to command to initiate the transfers once
  1244.  * a data connection has been setup.  These commands
  1245.  * can intiate a file retrieval (RETR), directory
  1246.  * listing (LIST), trigger event trace log (SITE TRIGGER)
  1247.  * or other data connection-based transfer.
  1248.  */
  1249. bool
  1250. FaxClient::recvData(bool (*f)(void*, const char*, int, fxStr&),
  1251.     void* arg, fxStr& emsg, u_long restart, const char* fmt, ...)
  1252. {
  1253.     if ((!setMode(MODE_S)) ||
  1254. (!initDataConn(emsg)) ||
  1255. (restart && command("REST %lu", restart) != CONTINUE)) {
  1256. // cannot "goto bad" because it is outside the scope of a va_arg
  1257. closeDataConn();
  1258. return (false);
  1259.     }
  1260.     va_list ap1, ap2;
  1261.     va_start(ap1, fmt);
  1262.     va_start(ap2, fmt);
  1263.     int r; r = vcommand(fmt, ap1, ap2);
  1264.     va_end(ap1);
  1265.     va_end(ap2);
  1266.     if (r != PRELIM)
  1267. goto bad;
  1268.     if (!openDataConn(emsg))
  1269. goto bad;
  1270.     u_long byte_count; byte_count = 0; // XXX for __GNUC__
  1271.     for (;;) {
  1272. char buf[16*1024];
  1273. int cc = read(fdData, buf, sizeof (buf));
  1274. if (cc == 0) {
  1275.     closeDataConn();
  1276.     return (getReply(false) == COMPLETE);
  1277. }
  1278. if (cc < 0) {
  1279.     emsg = fxStr::format("Data Connection: %s", strerror(errno));
  1280.     (void) getReply(false);
  1281.     break;
  1282. }
  1283. byte_count += cc;
  1284. if (!(*f)(arg, buf, cc, emsg))
  1285.     break;
  1286.     }
  1287. bad:
  1288.     closeDataConn();
  1289.     return (false);
  1290. }
  1291. /*
  1292.  * Receive data using zip-compressed mode and the current
  1293.  * transfer parameters.  The supplied arguments are
  1294.  * passed to command to initiate the transfers once
  1295.  * a data connection has been setup.  These commands
  1296.  * can intiate a file retrieval (RETR), directory
  1297.  * listing (LIST), trigger event trace log (SITE TRIGGER)
  1298.  * or other data connection-based transfer.
  1299.  */
  1300. bool
  1301. FaxClient::recvZData(bool (*f)(void*, const char*, int, fxStr&),
  1302.     void* arg, fxStr& emsg, u_long restart, const char* fmt, ...)
  1303. {
  1304.     z_stream zstream;
  1305.     zstream.zalloc = NULL;
  1306.     zstream.zfree = NULL;
  1307.     zstream.opaque = NULL;
  1308.     zstream.data_type = Z_BINARY;
  1309.     if (inflateInit(&zstream) == Z_OK) {
  1310. if ((!setMode(MODE_Z)) ||
  1311.     (!initDataConn(emsg)) ||
  1312.     (restart && command("REST %lu", restart) != CONTINUE)) {
  1313.     // cannot "goto bad" because it is outside the scope of a va_arg
  1314.     closeDataConn();
  1315.     inflateEnd(&zstream);
  1316.     return (false);
  1317. }
  1318. va_list ap1, ap2;
  1319. va_start(ap1, fmt);
  1320. va_start(ap2, fmt);
  1321. int r; r = vcommand(fmt, ap1, ap2); // XXX for __GNUC__
  1322. va_end(ap1);
  1323. va_end(ap2);
  1324. if (r != PRELIM)
  1325.     goto bad;
  1326. if (!openDataConn(emsg))
  1327.     goto bad;
  1328. char obuf[16*1024];
  1329. zstream.next_out = (Bytef*) obuf;
  1330. zstream.avail_out = sizeof (obuf);
  1331. for (;;) {
  1332.     char buf[16*1024];
  1333.     int cc = read(fdData, buf, sizeof (buf));
  1334.     if (cc == 0) {
  1335. size_t occ = sizeof (obuf) - zstream.avail_out;
  1336. if (occ > 0 && !(*f)(arg, obuf, occ, emsg))
  1337.     goto bad;
  1338. closeDataConn();
  1339. (void) inflateEnd(&zstream);
  1340. return (getReply(false) == COMPLETE);
  1341.     }
  1342.     if (cc < 0) {
  1343. emsg = fxStr::format("Data Connection: %s", strerror(errno));
  1344. (void) getReply(false);
  1345. goto bad;
  1346.     }
  1347.     zstream.next_in = (Bytef*) buf;
  1348.     zstream.avail_in = cc;
  1349.     do {
  1350. int dstate = inflate(&zstream, Z_PARTIAL_FLUSH);
  1351. if (dstate == Z_STREAM_END)
  1352.     break;
  1353. if (dstate != Z_OK) {
  1354.     emsg = fxStr::format("Decoding error: %s", zstream.msg);
  1355.     goto bad;
  1356. }
  1357. size_t occ = sizeof (obuf) - zstream.avail_out;
  1358. if (!(*f)(arg, obuf, occ, emsg))
  1359.     goto bad;
  1360. zstream.next_out = (Bytef*) obuf;
  1361. zstream.avail_out = sizeof (obuf);
  1362.     } while (zstream.avail_in > 0);
  1363. }
  1364. bad:
  1365. closeDataConn();
  1366. inflateEnd(&zstream);
  1367.     } else
  1368. emsg = fxStr::format("Can not initialize decoder: %s", zstream.msg);
  1369.     return (false);
  1370. }
  1371. /*
  1372.  * Return the current value for the specified
  1373.  * status format string.  If we have not set a
  1374.  * value locally, ask the server for the default
  1375.  * setting.
  1376.  */
  1377. const fxStr&
  1378. FaxClient::getStatusFormat(u_int flag, const char* cmd, fxStr& fmt)
  1379. {
  1380.     if (isLoggedIn()) {
  1381. if (state&flag) { // set pending; do it
  1382.     if (command("%s "%s"", cmd, (const char*) fmt) == COMPLETE)
  1383. state &= ~flag;
  1384.     else
  1385. printError("%s", (const char*) lastResponse);
  1386. } else if (fmt == "") { // must query server
  1387.     if (command(cmd) == COMPLETE)
  1388. fmt = lastResponse.tail(lastResponse.length()-4);
  1389.     else
  1390. printError("%s", (const char*) lastResponse);
  1391. }
  1392.     }
  1393.     return (fmt);
  1394. }
  1395. /*
  1396.  * Set the specified status format string
  1397.  *  in the client and the server.
  1398.  */
  1399. bool
  1400. FaxClient::setStatusFormat(const char* cmd, u_int flag,
  1401.     fxStr& fmt, const char* value)
  1402. {
  1403.     if (isLoggedIn()) {
  1404. if (command("%s "%s"", cmd, value) != COMPLETE) {
  1405.     printError("%s", (const char*) lastResponse);
  1406.     return (false);
  1407. }
  1408. state &= ~flag;
  1409.     } else
  1410. state |= flag;
  1411.     fmt = value;
  1412.     return (true);
  1413. }
  1414. /*
  1415.  * Set the job status format string in the
  1416.  * client and the server.
  1417.  */
  1418. bool
  1419. FaxClient::setJobStatusFormat(const char* cp)
  1420. {
  1421.     return setStatusFormat("JOBFMT", FS_JFMTPEND, jobFmt, cp);
  1422. }
  1423. /*
  1424.  * Return the current job status format string.
  1425.  * If we have not set a value locally, ask the
  1426.  * server for the default setting.
  1427.  */
  1428. const fxStr&
  1429. FaxClient::getJobStatusFormat(void)
  1430. {
  1431.     return getStatusFormat(FS_JFMTPEND, "JOBFMT", jobFmt);
  1432. }
  1433. /*
  1434.  * Set the receive queue status format
  1435.  * string in the client and the server.
  1436.  */
  1437. bool
  1438. FaxClient::setRecvStatusFormat(const char* cp)
  1439. {
  1440.     return setStatusFormat("RCVFMT", FS_RFMTPEND, recvFmt, cp);
  1441. }
  1442. /*
  1443.  * Return the current recv queue status format
  1444.  * string.  If we have not set a value locally,
  1445.  * ask the server for the default setting.
  1446.  */
  1447. const fxStr&
  1448. FaxClient::getRecvStatusFormat(void)
  1449. {
  1450.     return getStatusFormat(FS_RFMTPEND, "RCVFMT", recvFmt);
  1451. }
  1452. /*
  1453.  * Set the modem status format
  1454.  * string in the client and the server.
  1455.  */
  1456. bool
  1457. FaxClient::setModemStatusFormat(const char* cp)
  1458. {
  1459.     return setStatusFormat("MDMFMT", FS_MFMTPEND, modemFmt, cp);
  1460. }
  1461. /*
  1462.  * Return the current modem status format
  1463.  * string.  If we have not set a value locally,
  1464.  * ask the server for the default setting.
  1465.  */
  1466. const fxStr&
  1467. FaxClient::getModemStatusFormat(void)
  1468. {
  1469.     return getStatusFormat(FS_MFMTPEND, "MDMFMT", modemFmt);
  1470. }
  1471. /*
  1472.  * Set the file status format
  1473.  * string in the client and the server.
  1474.  */
  1475. bool
  1476. FaxClient::setFileStatusFormat(const char* cp)
  1477. {
  1478.     return setStatusFormat("FILEFMT", FS_FFMTPEND, fileFmt, cp);
  1479. }
  1480. /*
  1481.  * Return the current file status format
  1482.  * string.  If we have not set a value locally,
  1483.  * ask the server for the default setting.
  1484.  */
  1485. const fxStr&
  1486. FaxClient::getFileStatusFormat(void)
  1487. {
  1488.     return getStatusFormat(FS_FFMTPEND, "FILEFMT", fileFmt);
  1489. }
  1490. /*
  1491.  * Convert a format string to a header using a table
  1492.  * that maps format character to field header.
  1493.  */
  1494. void
  1495. FaxClient::makeHeader(const char* fmt, const FaxFmtHeader fmts[], fxStr& header)
  1496. {
  1497.     for (const char* cp = fmt; *cp; cp++) {
  1498. if (*cp == '%') {
  1499.     u_int width = 0; // field width
  1500.     u_int prec = 0; // field precision
  1501. #define MAXSPEC 20
  1502.     char fspec[MAXSPEC];
  1503.     char* fp = fspec;
  1504.     *fp++ = '%';
  1505.     char c = *++cp;
  1506.     if (c == '')
  1507. break;
  1508.     if (c == '-')
  1509. *fp++ = c, c = *++cp;
  1510.     if (isdigit(c)) {
  1511. do {
  1512.     *fp++ = c;
  1513.     width = 10*width + (c-'0');
  1514. } while (isdigit(c = *++cp) && fp < &fspec[MAXSPEC-3]);
  1515.     }
  1516.     if (c == '.') {
  1517. do {
  1518.     *fp++ = c;
  1519.     prec = 10*prec + (c-'0');
  1520. } while (isdigit(c = *++cp) && fp < &fspec[MAXSPEC-2]);
  1521.     }
  1522.     if (c == '%') { // %% -> %
  1523. header.append(c);
  1524. continue;
  1525.     }
  1526.     const FaxFmtHeader* hp;
  1527.     for (hp = fmts; hp->fmt != '' && hp->fmt != c; hp++)
  1528. ;
  1529.     if (hp->fmt == c) {
  1530. if (prec == 0) // constrain header to field width
  1531.     prec = width;
  1532. if (fspec[1] == '-') // left justify
  1533.     width = -width;
  1534. if (width == 0 && prec == 0)
  1535.     header.append(hp->title);
  1536. else
  1537.     header.append(fxStr::format("%*.*s", width, prec, hp->title));
  1538.     } else {
  1539. *fp++ = c;
  1540. header.append(fxStr(fspec, fp-fspec));
  1541.     }
  1542. } else
  1543.     header.append(*cp);
  1544.     }
  1545. }
  1546. /*
  1547.  * Table of known format strings for the job
  1548.  * queue status listings returned by the server.
  1549.  */
  1550. const FaxClient::FaxFmtHeader FaxClient::jobFormats[] = {
  1551.     { 'A', "SUB" }, // A (subaddr)
  1552.     { 'B', "PWD" }, // B (passwd)
  1553.     { 'C', "Company" }, // C (company)
  1554.     { 'D', "Dials" }, // D (totdials & maxdials)
  1555.     { 'E', "BR" }, // E (desiredbr)
  1556.     { 'F', "Tagline" }, // F (tagline)
  1557.     { 'G', "ST" }, // G (desiredst)
  1558.     { 'H', "DF" }, // H (desireddf)
  1559.     { 'I', "UsrPri" }, // I (usrpri)
  1560.     { 'J', "JobTag" }, // J (jobtag)
  1561.     { 'K', "EC" }, // K (desiredec as symbol)
  1562.     { 'L', "Location" }, // L (location)
  1563.     { 'M', "MailAddr" }, // M (mailaddr)
  1564.     { 'N', "DT" }, // N (desiredtl as symbol)
  1565.     { 'O', "CC" }, // O (useccover as symbol)
  1566.     { 'P', "Pages" }, // P (npages & totpages)
  1567.     { 'Q', "MinSP" }, // Q (minsp)
  1568.     { 'R', "Receiver" }, // R (receiver)
  1569.     { 'S', "Sender" }, // S (sender)
  1570.     { 'T', "Tries" }, // T (tottries & maxtries)
  1571.     { 'U', "ChopThreshold" },// U (chopthreshold)
  1572.     { 'V', "DoneOp" }, // V (doneop)
  1573.     { 'W', "CommID" }, // W (commid)
  1574.     { 'X', "JobType" }, // X (jobtype)
  1575.     { 'Y', "Date       Time" }, // Y (date & time)
  1576.     { 'Z', "UNIX Time" }, // Z (seconds since the UNIX epoch)
  1577.     { 'a', "State" }, // a (job state as symbol)
  1578.     { 'b', "NTries" }, // b (ntries)
  1579.     { 'c', "Client" }, // c (client)
  1580.     { 'd', "TotDials" }, // d (totdials)
  1581.     { 'e', "Number" }, // e (external)
  1582.     { 'f', "NDials" }, // f (ndials)
  1583.     { 'g', "GID" }, // g (groupid)
  1584.     { 'h', "Chop" }, // h (pagechop as symbol)
  1585.     { 'i', "Priority" }, // i (pri)
  1586.     { 'j', "JID" }, // j (jobid)
  1587.     { 'k', "LastTime" }, // k (killtime)
  1588.     { 'l', "PageLength" }, // l (pagelength)
  1589.     { 'm', "Modem" }, // m (modem)
  1590.     { 'n', "Notify" }, // n (notify as symbol)
  1591.     { 'o', "Owner" }, // o (owner)
  1592.     { 'p', "Pages" }, // p (npages)
  1593.     { 'q', "RetryTime" }, // q (retrytime as MM:SS)
  1594.     { 'r', "Resolution" }, // r (resolution)
  1595.     { 's', "Status" }, // s (notice a.k.a. status)
  1596.     { 't', "TotTries" }, // t (tottries)
  1597.     { 'u', "MaxTries" }, // u (maxtries)
  1598.     { 'v', "DialString" }, // v (number a.ka. dialstring)
  1599.     { 'w', "PageWidth" }, // w (pagewidth)
  1600.     { 'x', "MaxDials" }, // x (maxdials)
  1601.     { 'y', "TotPages" }, // y (totpages)
  1602.     { 'z', "TTS" }, // z (tts)
  1603.     { '0', "UseXVres" }, // 0 (usexvres as symbol)
  1604.     { '' },
  1605. };
  1606. void FaxClient::getJobStatusHeader(fxStr& header)
  1607.     { makeHeader(getJobStatusFormat(), jobFormats, header); }
  1608. /*
  1609.  * Table of known format strings for the receive
  1610.  * queue status listings returned by the server.
  1611.  */
  1612. const FaxClient::FaxFmtHeader FaxClient::recvFormats[] = {
  1613.     { 'X', "RI" }, // X (reception indicator)
  1614.     { 'Y', "Date       Time" }, // Y (date & time)
  1615.     { 'Z', "UNIX Time" }, // Z (seconds since the UNIX epoch)
  1616.     { 'a', "SUB" }, // a (subaddress)
  1617.     { 'b', "BR" }, // b (bitrate)
  1618.     { 'd', "DF" }, // d (data format)
  1619.     { 'e', "Error" }, // e (error description)
  1620.     { 'f', "Filename" }, // f (filename)
  1621.     { 'h', "Time" }, // h (time spent receiving)
  1622.     { 'i', "CIDName" }, // i (caller id name)
  1623.     { 'j', "CIDNumber" }, // j (caller id number)
  1624.     { 'l', "Length" }, // l (pagelength)
  1625.     { 'm', "Protect" }, // m (fax-style protection mode, no group bits)
  1626.     { 'n', "Size" }, // n (file size)
  1627.     { 'o', "Owner" }, // o (owner)
  1628.     { 'p', "Pages" }, // p (npages)
  1629.     { 'q', "Protect" }, // m (UNIX-style protection mode)
  1630.     { 'r', "Resolution" }, // r (resolution)
  1631.     { 's', "Sender/TSI" }, // s (sender TSI)
  1632.     { 't', "Recvd@" }, // t (time received)
  1633.     { 'w', "Width" }, // w (pagewidth)
  1634.     { 'z', " " }, // z (``*'' if being received)
  1635.     { '' },
  1636. };
  1637. void FaxClient::getRecvStatusHeader(fxStr& header)
  1638.     { makeHeader(getRecvStatusFormat(), recvFormats, header); }
  1639. /*
  1640.  * Table of known format strings for the modem
  1641.  * status listings returned by the server.
  1642.  */
  1643. const FaxClient::FaxFmtHeader FaxClient::modemFormats[] = {
  1644.     { 'h', "Host" }, // h (hostname)
  1645.     { 'l', "LocalID" }, // l (local identifier)
  1646.     { 'm', "Modem" }, // m (canonical modem name)
  1647.     { 'n', "Number" }, // n (fax phone number)
  1648.     { 'r', "MaxRecv" }, // r (max recv pages)
  1649.     { 's', "Status" }, // s (status information)
  1650.     { 't', "Tracing" }, // t (server:session tracing level)
  1651.     { 'v', "Speaker" }, // v (speaker volume as symbol)
  1652.     { 'z', " " }, // z (``*'' if faxgetty is running)
  1653.     { '' },
  1654. };
  1655. void FaxClient::getModemStatusHeader(fxStr& header)
  1656.     { makeHeader(getModemStatusFormat(), modemFormats, header); }
  1657. /*
  1658.  * Table of known format strings for the file
  1659.  * status listings returned by the server.
  1660.  */
  1661. const FaxClient::FaxFmtHeader FaxClient::fileFormats[] = {
  1662.     { 'a', "LastAcc" }, // a (last access time)
  1663.     { 'c', "Created" }, // c (create time)
  1664.     { 'd', "Device" }, // d (device)
  1665.     { 'f', "Filename" }, // f (filename)
  1666.     { 'g', "GID" }, // g (GID of file)
  1667.     { 'l', "Links" }, // l (link count)
  1668.     { 'm', "LastMod" }, // m (last modification time)
  1669.     { 'o', "Owner" }, // o (owner based on file GID)
  1670.     { 'p', "Protect" }, // p (fax-style protection flags, no group bits)
  1671.     { 'q', "Protect" }, // q (UNIX-style protection flags)
  1672.     { 'r', "RootDev" }, // r (root device)
  1673.     { 's', "Size" }, // s (file size in bytes)
  1674.     { 'u', "UID" }, // u (UID of file)
  1675.     { '' },
  1676. };
  1677. void FaxClient::getFileStatusHeader(fxStr& header)
  1678.     { makeHeader(getFileStatusFormat(), fileFormats, header); }