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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: SNPPClient.c++,v 1.6 2008/03/12 05:43:09 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1995-1996 Sam Leffler
  4.  * Copyright (c) 1995-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 "SNPPClient.h"
  29. #include <pwd.h>
  30. #include <ctype.h>
  31. #include <sys/types.h>
  32. #include <errno.h>
  33. #define N(a) (sizeof (a) / sizeof (a[0]))
  34. SNPPClient::SNPPClient()
  35. {
  36.     init();
  37. }
  38. SNPPClient::SNPPClient(const fxStr& hostarg)
  39. {
  40.     init();
  41.     setupHostModem(hostarg);
  42. }
  43. SNPPClient::SNPPClient(const char* hostarg)
  44. {
  45.     init();
  46.     setupHostModem(hostarg);
  47. }
  48. void
  49. SNPPClient::init()
  50. {
  51.     jobs = new SNPPJobArray;
  52.     fdIn = NULL;
  53.     fdOut = NULL;
  54.     state = 0;
  55.     msg = NULL;
  56.     pasv = false;
  57.     setupConfig();
  58. }
  59. void
  60. SNPPClient::initServerState(void)
  61. {
  62. }
  63. SNPPClient::~SNPPClient()
  64. {
  65.     (void) hangupServer();
  66.     delete jobs;
  67.     delete msg;
  68. }
  69. void
  70. SNPPClient::printError(const char* fmt ...)
  71. {
  72.     va_list ap;
  73.     va_start(ap, fmt);
  74.     vprintError(fmt, ap);
  75.     va_end(ap);
  76. }
  77. void
  78. SNPPClient::vprintError(const char* fmt, va_list ap)
  79. {
  80.     vfprintf(stderr, fmt, ap);
  81.     fputs("n", stderr);
  82. }
  83. void
  84. SNPPClient::printWarning(const char* fmt ...)
  85. {
  86.     va_list ap;
  87.     va_start(ap, fmt);
  88.     vprintWarning(fmt, ap);
  89.     va_end(ap);
  90. }
  91. void
  92. SNPPClient::vprintWarning(const char* fmt, va_list ap)
  93. {
  94.     fprintf(stderr, "Warning, ");
  95.     vfprintf(stderr, fmt, ap);
  96.     fputs("n", stderr);
  97. }
  98. void
  99. SNPPClient::traceServer(const char* fmt ...)
  100. {
  101.     va_list ap;
  102.     va_start(ap, fmt);
  103.     vtraceServer(fmt, ap);
  104.     va_end(ap);
  105. }
  106. void
  107. SNPPClient::vtraceServer(const char* fmt, va_list ap)
  108. {
  109.     vfprintf(stdout, fmt, ap);
  110.     fputs("n", stdout);
  111. }
  112. /*
  113.  * Host, port, and modem can be specified using
  114.  *
  115.  *     modem@host:port
  116.  *
  117.  * e.g. ttyf2@flake.asd:9999.  Alternate forms
  118.  * are: modem@, modem@host, host, host:port.
  119.  */
  120. void
  121. SNPPClient::setupHostModem(const fxStr& s)
  122. {
  123.     u_int pos = s.next(0, '@');
  124.     if (pos != s.length()) {
  125. modem = s.head(pos);
  126. host = s.tail(s.length() - (pos+1));
  127.     } else
  128. host = s;
  129.     pos = host.next(0, ':');
  130.     if (pos != host.length()) {
  131. port = atoi(host.tail(host.length() - (pos+1)));
  132. host.resize(pos);
  133.     }
  134. }
  135. void SNPPClient::setupHostModem(const char* cp) { setupHostModem(fxStr(cp)); }
  136. void SNPPClient::setHost(const fxStr& hostarg) { setupHostModem(hostarg); }
  137. void SNPPClient::setHost(const char* hostarg) { setupHostModem(hostarg); }
  138. void SNPPClient::setPort(int p) { port = p; }
  139. void SNPPClient::setProtoName(const char* s) { proto = s; }
  140. void SNPPClient::setModem(const fxStr& modemarg){ modem = modemarg; }
  141. void SNPPClient::setModem(const char* modemarg) { modem = modemarg; }
  142. void
  143. SNPPClient::setVerbose(bool v)
  144. {
  145.     if (v)
  146. state |= SS_VERBOSE;
  147.     else
  148. state &= ~SS_VERBOSE;
  149. }
  150. /*
  151.  * Setup the sender's identity.
  152.  */
  153. bool
  154. SNPPClient::setupSenderIdentity(fxStr& emsg)
  155. {
  156.     setupUserIdentity(emsg); // client identity
  157.     if (from != "") {
  158. u_int l = from.next(0, '<');
  159. if (l == from.length()) {
  160.     l = from.next(0, '(');
  161.     if (l != from.length()) { // joe@foobar (Joe Schmo)
  162. setBlankMailboxes(from.head(l));
  163. l++, senderName = from.token(l, ')');
  164.     } else { // joe
  165. setBlankMailboxes(from);
  166. if (from != getUserName())
  167.     senderName = "";
  168.     }
  169. } else { // Joe Schmo <joe@foobar>
  170.     senderName = from.head(l);
  171.     l++, setBlankMailboxes(from.token(l, '>'));
  172. }
  173. if (senderName == "" && getNonBlankMailbox(senderName)) {
  174.     /*
  175.      * Mail address, but no "real name"; construct one from
  176.      * the account name.  Do this by first stripping anything
  177.      * to the right of an '@' and then stripping any leading
  178.      * uucp patch (host!host!...!user).
  179.      */
  180.     senderName.resize(senderName.next(0, '@'));
  181.     senderName.remove(0, senderName.nextR(senderName.length(), '!'));
  182. }
  183. // strip and leading&trailing white space
  184. senderName.remove(0, senderName.skip(0, " t"));
  185. senderName.resize(senderName.skipR(senderName.length(), " t"));
  186.     } else {
  187. setBlankMailboxes(getUserName());
  188.     }
  189.     fxStr mbox;
  190.     if (senderName == "" || !getNonBlankMailbox(mbox)) {
  191. emsg = "Malformed (null) sender name or mail address";
  192. return (false);
  193.     } else
  194. return (true);
  195. }
  196. void SNPPClient::setFromIdentity(const char* s) { from = s; }
  197. /*
  198.  * Assign the specified string to any unspecified email
  199.  * addresses used for notification mail.
  200.  */
  201. void
  202. SNPPClient::setBlankMailboxes(const fxStr& s)
  203. {
  204.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  205. SNPPJob& job = (*jobs)[i];
  206. if (job.getMailbox() == "")
  207.     job.setMailbox(s);
  208.     }
  209. }
  210. /*
  211.  * Return the first non-null mailbox string
  212.  * in the set of jobs.
  213.  */
  214. bool
  215. SNPPClient::getNonBlankMailbox(fxStr& s)
  216. {
  217.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  218. SNPPJob& job = (*jobs)[i];
  219. if (job.getMailbox() != "") {
  220.     s = job.getMailbox();
  221.     return (true);
  222. }
  223.     }
  224.     return (false);
  225. }
  226. bool
  227. SNPPClient::setupUserIdentity(fxStr& emsg)
  228. {
  229.     struct passwd* pwd;
  230.     pwd = getpwuid(getuid());
  231.     if (!pwd) {
  232. emsg = fxStr::format(
  233.     "Can not locate your password entry (uid %lu): %s.",
  234. (u_long) getuid(), strerror(errno));
  235. return (false);
  236.     }
  237.     userName = pwd->pw_name;
  238.     if (pwd->pw_gecos && pwd->pw_gecos[0] != '') {
  239. senderName = pwd->pw_gecos;
  240. senderName.resize(senderName.next(0, '(')); // strip SysV junk
  241. u_int l = senderName.next(0, '&');
  242. if (l < senderName.length()) {
  243.     /*
  244.      * Do the '&' substitution and raise the
  245.      * case of the first letter of the inserted
  246.      * string (the usual convention...)
  247.      */
  248.     senderName.remove(l);
  249.     senderName.insert(userName, l);
  250.     if (islower(senderName[l]))
  251. senderName[l] = toupper(senderName[l]);
  252. }
  253. senderName.resize(senderName.next(0,','));
  254.     } else
  255. senderName = userName;
  256.     if (senderName.length() == 0) {
  257. emsg = "Bad (null) user name; your password file entry"
  258.     " probably has bogus GECOS field information.";
  259. return (false);
  260.     } else
  261. return (true);
  262. }
  263. void
  264. SNPPClient::setPagerMsg(const char* v)
  265. {
  266.     delete msg;
  267.     msg = new fxStr(v);
  268.     msgFile ="";
  269. }
  270. void
  271. SNPPClient::setPagerMsgFile(const char* v)
  272. {
  273.     msgFile = v;
  274.     delete msg;
  275. }
  276. /*
  277.  * Configuration file support.
  278.  */
  279. SNPPClient::S_stringtag SNPPClient::strings[] = {
  280. { "protocol", &SNPPClient::proto, SNPP_PROTONAME },
  281. { "host", &SNPPClient::host, NULL },
  282. { "modem", &SNPPClient::modem, NULL },
  283. };
  284. SNPPClient::S_numbertag SNPPClient::numbers[] = {
  285. { "port", &SNPPClient::port, (u_int) -1 },
  286. };
  287. /*
  288.  * Configuration file support.
  289.  */
  290. #define N(a) (sizeof (a) / sizeof (a[0]))
  291. void
  292. SNPPClient::setupConfig()
  293. {
  294.     int i;
  295.     for (i = N(strings)-1; i >= 0; i--)
  296. (*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
  297.     for (i = N(numbers)-1; i >= 0; i--)
  298. (*this).*numbers[i].p = numbers[i].def;
  299.     initServerState();
  300.     jproto.setQueued(SNPP_DEFQUEUE);
  301.     jproto.setNotification(SNPP_DEFNOTIFY);
  302.     jproto.setHoldTime(0); // immediate delivery
  303.     jproto.setRetryTime((u_int) -1);
  304.     jproto.setMaxTries(SNPP_DEFRETRIES);
  305.     jproto.setMaxDials(SNPP_DEFREDIALS);
  306.     jproto.setServiceLevel(SNPP_DEFLEVEL);
  307.     jproto.setMailbox("");
  308. }
  309. void
  310. SNPPClient::resetConfig()
  311. {
  312.     setupConfig();
  313. }
  314. void
  315. SNPPClient::configError(const char* fmt ...)
  316. {
  317.     va_list ap;
  318.     va_start(ap, fmt);
  319.     vprintError(fmt, ap);
  320.     va_end(ap);
  321. }
  322. void
  323. SNPPClient::configTrace(const char* fmt ...)
  324. {
  325.     if (getVerbose()) {
  326. va_list ap;
  327. va_start(ap, fmt);
  328. vprintWarning(fmt, ap);
  329. va_end(ap);
  330.     }
  331. }
  332. bool
  333. SNPPClient::setConfigItem(const char* tag, const char* value)
  334. {
  335.     u_int ix;
  336.     if (findTag(tag, (const tags*) strings, N(strings), ix)) {
  337. (*this).*strings[ix].p = value;
  338.     } else if (findTag(tag, (const tags*) numbers, N(numbers), ix)) {
  339. (*this).*numbers[ix].p = getNumber(value);
  340.     } else if (streq(tag, "verbose")) {
  341. if (getBoolean(value))
  342.     state |= SS_VERBOSE;
  343. else
  344.     state &= ~SS_VERBOSE;
  345.     } else if (streq(tag, "queuesend")) {
  346. jproto.setQueued(getBoolean(value));
  347.     } else if (streq(tag, "notify") || streq(tag, "notification")) {
  348. jproto.setNotification(value);
  349.     } else if (streq(tag, "holdtime")) {
  350. fxStr emsg;
  351. if (!jproto.setHoldTime(tag, emsg))
  352.     configError("Invalid hold time "%s": %s",
  353. value, (const char*) emsg);
  354.     } else if (streq(tag, "retrytime")) {
  355. jproto.setRetryTime(value);
  356.     } else if (streq(tag, "maxtries")) {
  357. jproto.setMaxTries(getNumber(value));
  358.     } else if (streq(tag, "maxdials")) {
  359. jproto.setMaxDials(getNumber(value));
  360.     } else if (streq(tag, "servicelevel")) {
  361. jproto.setServiceLevel(getNumber(value));
  362.     } else if (streq(tag, "mailaddr")) {
  363. jproto.setMailbox(value);
  364.     } else if (streq(tag, "passivemode")) {
  365. pasv = getBoolean(value);
  366.     } else
  367. return (false);
  368.     return (true);
  369. }
  370. bool
  371. SNPPClient::callServer(fxStr& emsg)
  372. {
  373.     if (host.length() == 0) { // if host not specified by -h
  374. const char* cp = getenv("SNPPSERVER");
  375. if (cp && *cp != '') {
  376.     if (modem != "") { // don't clobber specified modem
  377. fxStr m(modem);
  378. setupHostModem(cp);
  379. modem = m;
  380.     } else
  381. setupHostModem(cp);
  382. } else // use default host
  383.     host = SNPP_DEFHOST;
  384.     }
  385.     if (callInetServer(emsg)) {
  386. signal(SIGPIPE, fxSIGHANDLER(SIG_IGN));
  387. /*
  388.  * Transport code is expected to call back through
  389.  * setCtrlFds so fdIn should be properly setup...
  390.  */
  391. return (fdIn != NULL && getReply(false) == COMPLETE);
  392.     } else
  393. return (false);
  394. }
  395. #if CONFIG_INETTRANSPORT
  396. #include "Socket.h"
  397. extern "C" {
  398. #include <arpa/inet.h>
  399. #include <arpa/telnet.h>
  400. #include <netinet/in.h>
  401. #include <netinet/in_systm.h>
  402. #include <netinet/ip.h>
  403. #include <netdb.h>
  404. }
  405. bool
  406. SNPPClient::callInetServer(fxStr& emsg)
  407. {
  408.     fxStr proto(getProtoName());
  409.     char* cp;
  410.     if ((cp = getenv("SNPPSERVICE")) && *cp != '') {
  411. fxStr s(cp);
  412. u_int l = s.next(0,'/');
  413. port = (u_int) (int) s.head(l);
  414. if (l < s.length())
  415.     proto = s.tail(s.length()-(l+1));
  416.     }
  417.     struct hostent* hp = Socket::gethostbyname(getHost());
  418.     if (!hp) {
  419. emsg = getHost() | ": Unknown host";
  420. return (false);
  421.     }
  422.     int protocol;
  423.     const char* cproto = proto; // XXX for busted include files
  424.     struct protoent* pp = getprotobyname(cproto);
  425.     if (!pp) {
  426. printWarning("%s: No protocol definition, using default.", cproto);
  427. protocol = 0;
  428.     } else
  429. protocol = pp->p_proto;
  430.     int fd = socket(hp->h_addrtype, SOCK_STREAM, protocol);
  431.     if (fd < 0) {
  432. emsg = "Can not create socket to connect to server.";
  433. return (false);
  434.     }
  435.     struct sockaddr_in sin;
  436.     memset(&sin, 0, sizeof (sin));
  437.     sin.sin_family = hp->h_addrtype;
  438.     if (port == (u_int) -1) {
  439. struct servent* sp = getservbyname(SNPP_SERVICE, cproto);
  440. if (!sp) {
  441.     if (!isdigit(cproto[0])) {
  442. printWarning(
  443.     "No "%s" service definition, using default %u/%s.",
  444.     SNPP_SERVICE, SNPP_DEFPORT, cproto);
  445. sin.sin_port = htons(SNPP_DEFPORT);
  446.     } else
  447. sin.sin_port = htons(atoi(cproto));
  448. } else
  449.     sin.sin_port = sp->s_port;
  450.     } else
  451. sin.sin_port = htons(port);
  452.     for (char** cpp = hp->h_addr_list; *cpp; cpp++) {
  453. memcpy(&sin.sin_addr, *cpp, hp->h_length);
  454. if (getVerbose())
  455.     traceServer("Trying %s (%s) at port %u...",
  456. (const char*) getHost(),
  457. inet_ntoa(sin.sin_addr),
  458. ntohs(sin.sin_port));
  459. if (Socket::connect(fd, &sin, sizeof (sin)) >= 0) {
  460.     if (getVerbose())
  461. traceServer("Connected to %s.", hp->h_name);
  462. #if defined(IP_TOS) && defined(IPTOS_LOWDELAY)
  463.     int tos = IPTOS_LOWDELAY;
  464.     if (Socket::setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0)
  465. printWarning("setsockopt(TOS): %s (ignored)",
  466.     strerror(errno));
  467. #endif
  468. #ifdef SO_OOBINLINE
  469.     int on = 1;
  470.     if (Socket::setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof (on)) < 0)
  471. printWarning("setsockopt(OOBLINE): %s (ignored)",
  472.     strerror(errno));
  473. #endif
  474.     setCtrlFds(fd, fd);
  475.     return (true);
  476. }
  477.     }
  478.     emsg = fxStr::format("Can not reach server at host "%s", port %u.",
  479. (const char*) getHost(), ntohs(sin.sin_port));
  480.     close(fd), fd = -1;
  481.     return (false);
  482. }
  483. #else
  484. bool
  485. SNPPServer::callInetServer(fxStr& emsg)
  486. {
  487.     emsg = "Sorry, no TCP/IP communication support was configured.";
  488.     return (false);
  489. }
  490. #endif
  491. bool
  492. SNPPClient::hangupServer(void)
  493. {
  494.     if (fdIn != NULL)
  495. fclose(fdIn), fdIn = NULL;
  496.     if (fdOut != NULL)
  497. fclose(fdOut), fdOut = NULL;
  498.     initServerState();
  499.     return (true);
  500. }
  501. void
  502. SNPPClient::setCtrlFds(int in, int out)
  503. {
  504.     if (fdIn != NULL)
  505. fclose(fdIn);
  506.     fdIn = fdopen(in, "r");
  507.     if (fdOut != NULL)
  508. fclose(fdOut);
  509.     fdOut = fdopen(out, "w");
  510. }
  511. /*
  512.  * Do login procedure.
  513.  */
  514. bool
  515. SNPPClient::login(const char* user, const char* pass, fxStr& emsg)
  516. {
  517.     if (user == NULL) {
  518. setupSenderIdentity(emsg); // invokes setupUserIdentity
  519. user = userName;
  520.     }
  521.     int n = command("LOGI %s", user);
  522.     if (code == 550)
  523. n = command("LOGI %s %s", user, pass ? pass : getPasswd("Password:"));
  524.     if (n == COMPLETE)
  525. state |= SS_LOGGEDIN;
  526.     else
  527. state &= ~SS_LOGGEDIN;
  528.     if (isLoggedIn()) {
  529. if (command("SITE HELP NOTIFY") == COMPLETE)
  530.     state |= SS_HASSITE;
  531. else
  532.     state &= ~SS_HASSITE;
  533. return (true);
  534.     } else {
  535. emsg = "Login failed: " | lastResponse;
  536. return (false);
  537.     }
  538. }
  539. /*
  540.  * Prompt for a password.
  541.  */
  542. const char*
  543. SNPPClient::getPasswd(const char* prompt)
  544. {
  545.     return (getpass(prompt));
  546. }
  547. void
  548. SNPPClient::lostServer(void)
  549. {
  550.     printError("Service not available, remote server closed connection");
  551.     hangupServer();
  552. }
  553. void
  554. SNPPClient::unexpectedResponse(fxStr& emsg)
  555. {
  556.     emsg = "Unexpected server response: " | lastResponse;
  557. }
  558. void
  559. SNPPClient::protocolBotch(fxStr& emsg, const char* fmt ...)
  560. {
  561.     va_list ap;
  562.     va_start(ap, fmt);
  563.     emsg = "Protocol botch" | fxStr::vformat(fmt, ap);
  564.     va_end(ap);
  565. }
  566. /*
  567.  * Send a command and wait for a response.
  568.  * The primary response code is returned.
  569.  */
  570. int
  571. SNPPClient::command(const char* fmt ...)
  572. {
  573.     va_list ap1, ap2;
  574.     va_start(ap1, fmt);
  575.     va_start(ap2, fmt);
  576.     int r = vcommand(fmt, ap1, ap2);
  577.     va_end(ap1);
  578.     va_end(ap2);
  579.     return (r);
  580. }
  581. /*
  582.  * Send a command and wait for a response.
  583.  * The primary response code is returned.
  584.  */
  585. int
  586. SNPPClient::vcommand(const char* fmt, va_list ap1, va_list ap2)
  587. {
  588.     if (getVerbose()) {
  589. if (strncasecmp("LOGI", fmt, 4) == 0)
  590.     traceServer("-> LOGI XXXX");
  591. else {
  592.         fxStr f("-> ");
  593.         f.append(fmt);
  594.     vtraceServer(f, ap1);
  595. }
  596.     }
  597.     if (fdOut == NULL) {
  598. printError("No control connection for command");
  599. code = -1;
  600. return (0);
  601.     }
  602.     vfprintf(fdOut, fmt, ap2);
  603.     fputs("rn", fdOut);
  604.     (void) fflush(fdOut);
  605.     int r = getReply(strncmp(fmt, "QUIT", 4) == 0);
  606.     return (r);
  607. }
  608. /*
  609.  * Extract a valid reply code from a string.
  610.  * The code must be 3 numeric digits followed
  611.  * by a space or ``-'' (the latter indicates
  612.  * the server reponse is to be continued with
  613.  * one or more lines).  If no valid reply code
  614.  * is recognized zero is returned--this is
  615.  * assumed to be an invalid reply code.
  616.  */
  617. static int
  618. getReplyCode(const char* cp)
  619. {
  620.     if (!isdigit(cp[0]))
  621. return (0);
  622.     int c = (cp[0] - '0');
  623.     if (!isdigit(cp[1]))
  624. return (0);
  625.     c = 10*c + (cp[1]-'0');
  626.     if (!isdigit(cp[2]))
  627. return (0);
  628.     c = 10*c + (cp[2]-'0');
  629.     return ((cp[3] == ' ' || cp[3] == '-') ? c : 0);
  630. }
  631. /*
  632.  * Read from the control channel until a valid reply is
  633.  * received or the connection is dropped.  The last line
  634.  * of the received response is left in SNPPClient::lastResponse
  635.  * and the reply code is left in SNPPClient::code.  The
  636.  * primary response (the first digit of the reply code)
  637.  * is returned to the caller.  Continuation lines are
  638.  * handled but not collected.
  639.  */
  640. int
  641. SNPPClient::getReply(bool expecteof)
  642. {
  643.     int firstCode = 0;
  644.     bool continuation = false;
  645.     do {
  646. lastResponse.resize(0);
  647. int c;
  648. while ((c = getc(fdIn)) != 'n') {
  649.     if (c == IAC) {     // handle telnet commands
  650. switch (c = getc(fdIn)) {
  651. case WILL:
  652. case WONT:
  653.     c = getc(fdIn);
  654.     fprintf(fdOut, "%c%c%c", IAC, DONT, c);
  655.     (void) fflush(fdOut);
  656.     break;
  657. case DO:
  658. case DONT:
  659.     c = getc(fdIn);
  660.     fprintf(fdOut, "%c%c%c", IAC, WONT, c);
  661.     (void) fflush(fdOut);
  662.     break;
  663. default:
  664.     break;
  665. }
  666. continue;
  667.     }
  668.     if (c == EOF) {
  669. if (expecteof) {
  670.     code = 221;
  671.     return (0);
  672. } else {
  673.     lostServer();
  674.     code = 421;
  675.     return (4);
  676. }
  677.     }
  678.     if (c != 'r')
  679. lastResponse.append(c);
  680. }
  681. if (getVerbose())
  682.     traceServer("%s", (const char*) lastResponse);
  683. code = getReplyCode(lastResponse);
  684. if (code != 0) { // found valid reply code
  685.     if (lastResponse[3] == '-') { // continuation line
  686. if (firstCode == 0) // first line of reponse
  687.     firstCode = code;
  688. continuation = true;
  689.     } else if (code == firstCode) // end of continued reply
  690. continuation = false;
  691. }
  692.     } while (continuation || code == 0);
  693.     if (code == 421) // server closed connection
  694. lostServer();
  695.     return (code/100);
  696. }
  697. /*
  698.  * Extract a string from a reply message.  The
  699.  * string that is extracted is expected to follow
  700.  * a pattern string.  The pattern is tried both
  701.  * in the initial case and then the inverse case
  702.  * (upper or lower depending on what the original
  703.  * case was).  The resulting string is checked to
  704.  * make sure that it is not null.
  705.  */
  706. bool
  707. SNPPClient::extract(u_int& pos, const char* pattern, fxStr& result)
  708. {
  709.     fxStr pat(pattern);
  710.     u_int l = lastResponse.find(pos, pat);
  711.     if (l == lastResponse.length()) { // try inverse-case version
  712. if (isupper(pattern[0]))
  713.     pat.lowercase();
  714. else
  715.     pat.raisecase();
  716. l = lastResponse.find(pos, pat);
  717.     }
  718.     if (l == lastResponse.length())
  719. return (false);
  720.     l = lastResponse.skip(l+pat.length(), ' ');// skip white space
  721.     result = lastResponse.extract(l, lastResponse.next(l, ' ')-l);
  722.     if (result == "")
  723. return (false);
  724.     pos = l; // update position
  725.     return (true);
  726. }
  727. /*
  728.  * Create a new job and return its job-id if
  729.  * parsed from the reply (this is for HylaFAX,
  730.  * RFC 1861 says nothing about this).
  731.  */
  732. bool
  733. SNPPClient::newPage(const fxStr& pin, const fxStr& passwd, fxStr& jobid, fxStr& emsg)
  734. {
  735.     int result;
  736.     if (passwd != "")
  737. result = command("PAGE %s %s", (const char*) pin, (const char*) passwd);
  738.     else
  739. result = command("PAGE %s", (const char*) pin);
  740.     if (result == COMPLETE) {
  741. if (code == 250) {
  742.     /*
  743.      * If the server is hfaxd, then the response should be
  744.      * of the form:
  745.      *
  746.      * 250 ... jobid: xxxx.
  747.      *
  748.      * where xxxx is the ID for the new job.
  749.      */
  750.     u_int l = 0;
  751.     if (extract(l, "jobid:", jobid)) {
  752. /*
  753.  * Force job IDs to be numeric;
  754.  * this deals with servers that want to append
  755.  * punctuation such as ``,'' or ``.''.
  756.  */
  757. jobid.resize(jobid.skip(0, "0123456789"));
  758.     } else
  759. jobid = "unknown";
  760.     return (true);
  761. } else
  762.     unexpectedResponse(emsg);
  763.     } else
  764. emsg = lastResponse;
  765.     return (false);
  766. }
  767. SNPPJob&
  768. SNPPClient::addJob(void)
  769. {
  770.     u_int ix = jobs->length();
  771.     jobs->resize(ix+1);
  772.     (*jobs)[ix] = jproto;
  773.     return ((*jobs)[ix]);
  774. }
  775. u_int SNPPClient::getNumberOfJobs() const { return jobs->length(); }
  776. SNPPJob*
  777. SNPPClient::findJob(const fxStr& pin)
  778. {
  779.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  780. SNPPJob& job = (*jobs)[i];
  781. if (job.getPIN() == pin)
  782.     return (&job);
  783.     }
  784.     return (NULL);
  785. }
  786. void
  787. SNPPClient::removeJob(const SNPPJob& job)
  788. {
  789.     u_int ix = jobs->find(job);
  790.     if (ix != fx_invalidArrayIndex)
  791. jobs->remove(ix);
  792. }
  793. bool
  794. SNPPClient::prepareForJobSubmissions(fxStr&)
  795. {
  796.     // XXX nothing to do right now
  797.     return (true);
  798. }
  799. bool
  800. SNPPClient::submitJobs(fxStr& emsg)
  801. {
  802.     if (!isLoggedIn()) {
  803. emsg = "Not logged in to server";
  804. return (false);
  805.     }
  806.     /*
  807.      * Construct jobs and submit them.
  808.      */
  809.     for (u_int i = 0, n = jobs->length(); i < n; i++) {
  810. SNPPJob& job = (*jobs)[i];
  811. if (!job.createJob(*this, emsg))
  812.     return (false);
  813. notifyNewJob(job); // notify client
  814.     }
  815.     if (msgFile != "") {
  816. if (!sendData(msgFile, emsg))
  817.     return (false);
  818.     } else if (msg) {
  819. if (!sendMsg(*msg, emsg))
  820.     return (false);
  821.     }
  822.     if (command("SEND") != COMPLETE) {
  823. emsg = lastResponse;
  824. return (false);
  825.     } else
  826. return (true);
  827. }
  828. /*
  829.  * Default notification handler for when a new job is created.
  830.  */
  831. void
  832. SNPPClient::notifyNewJob(const SNPPJob& job)
  833. {
  834.     printf("destination pin %s: request id is %s for host %sn"
  835. , (const char*) job.getPIN()
  836. , (const char*) job.getJobID()
  837. , (const char*) getHost()
  838.     );
  839. }
  840. /*
  841.  * Send a block of raw data on the data
  842.  * conenction, interpreting write errors.
  843.  */
  844. bool
  845. SNPPClient::sendRawData(void* buf, int cc, fxStr& emsg)
  846. {
  847. #ifdef __linux__
  848.     /*
  849.      * Linux kernel bug: can get short writes on
  850.      * stream sockets when setup for blocking i/o.
  851.      */
  852.     u_char* bp = (u_char*) buf;
  853.     for (int cnt, sent = 0; cc; sent += cnt, cc -= cnt) 
  854. if ((cnt = write(fileno(fdOut), bp + sent, cc)) <= 0) {
  855.     protocolBotch(emsg, errno == EPIPE ?
  856. " (server closed connection)" : " (server write error: %s).",
  857. strerror(errno));
  858.     return (false);
  859. }
  860. #else
  861.     if (write(fileno(fdOut), buf, cc) != cc) {
  862. protocolBotch(emsg, errno == EPIPE ?
  863.     " (server closed connection)" : " (server write error: %s).",
  864.     strerror(errno));
  865. return (false);
  866.     }
  867. #endif
  868.     return (true);
  869. }
  870. bool
  871. SNPPClient::sendData(int fd, fxStr& emsg)
  872. {
  873.     struct stat sb;
  874.     (void) Sys::fstat(fd, sb);
  875.     if (getVerbose())
  876. traceServer("SEND message data, %lu bytes", (u_long) sb.st_size);
  877.     if (command("DATA") == CONTINUE) {
  878. size_t cc = (size_t) sb.st_size;
  879. while (cc > 0) {
  880.     char buf[32*1024];
  881.     size_t n = fxmin(cc, sizeof (buf));
  882.     if (read(fd, buf, n) != (ssize_t) n) {
  883. protocolBotch(emsg, " (data read: %s).", strerror(errno));
  884. return (false);
  885.     }
  886.     if (!sendRawData(buf, n, emsg))
  887. return (false);
  888.     cc -= n;
  889. }
  890. if (command(".") == COMPLETE)
  891.     return (true);
  892.     }
  893.     emsg = getLastResponse();
  894.     return (false);
  895. }
  896. bool
  897. SNPPClient::sendData(const fxStr& filename, fxStr& emsg)
  898. {
  899.     bool ok = false;
  900.     int fd = Sys::open(filename, O_RDONLY);
  901.     if (fd >= 0) {
  902. ok = sendData(fd, emsg);
  903. Sys::close(fd);
  904.     } else
  905. emsg = fxStr::format("Unable to open message file "%s".",
  906.     (const char*) filename);
  907.     return (ok);
  908. }
  909. bool
  910. SNPPClient::sendMsg(const char* msg, fxStr& emsg)
  911. {
  912.     if (command("MESS %s", msg) != COMPLETE) {
  913. emsg = getLastResponse();
  914. return (false);
  915.     } else
  916. return (true);
  917. }
  918. bool
  919. SNPPClient::siteParm(const char* name, const fxStr& value)
  920. {
  921.     if (!hasSiteCmd()) {
  922. printWarning("no SITE %s support; ignoring set request.", name);
  923. return (true);
  924.     } else
  925. return (command("SITE %s %s", name, (const char*) value) == COMPLETE);
  926. }
  927. bool
  928. SNPPClient::siteParm(const char* name, u_int value)
  929. {
  930.     if (!hasSiteCmd()) {
  931. printWarning("no SITE %s support; ignoring set request.", name);
  932. return (true);
  933.     } else
  934. return (command("SITE %s %u", name, value) == COMPLETE);
  935. }
  936. bool
  937. SNPPClient::setHoldTime(u_int t)
  938. {
  939.     time_t tv = t;
  940.     struct tm* tm = gmtime(&tv);
  941.     return (command("HOLD %02d%02d%02d%02d%02d"
  942. , (tm->tm_year) % 100
  943. , tm->tm_mon+1
  944. , tm->tm_mday
  945. , tm->tm_hour
  946. , tm->tm_min) == COMPLETE);
  947. }
  948. bool
  949. SNPPClient::setRetryTime(u_int t)
  950. {
  951.     return siteParm("RETRYTIME", fxStr::format("%02d%02d", t/60, t%60));
  952. }