1 /*	$Id$ */
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 
29 #include <ctype.h>
30 #include <string.h>
31 #include <errno.h>
32 
33 #if CONFIG_INETTRANSPORT
34 extern "C" {
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <netdb.h>            // XXX
38 }
39 #endif
40 
41 #include "SendFaxClient.h"
42 #include "PageSize.h"
43 #include "FaxConfig.h"
44 
45 #include "NLS.h"
46 
SendFaxJob()47 SendFaxJob::SendFaxJob()
48 {
49 }
SendFaxJob(const SendFaxJob & other)50 SendFaxJob::SendFaxJob(const SendFaxJob& other)
51     : jobtag(other.jobtag)
52     , mailbox(other.mailbox)
53     , number(other.number)
54     , subaddr(other.subaddr)
55     , passwd(other.passwd)
56     , external(other.external)
57     , coverFile(other.coverFile)
58     , coverTemplate(other.coverTemplate)
59     , name(other.name)
60     , voicenumber(other.voicenumber)
61     , location(other.location)
62     , company(other.company)
63     , comments(other.comments)
64     , regarding(other.regarding)
65     , fromlocation(other.fromlocation)
66     , fromfax(other.fromfax)
67     , fromvoice(other.fromvoice)
68     , fromcompany(other.fromcompany)
69     , killTime(other.killTime)
70     , sendTime(other.sendTime)
71     , tagline(other.tagline)
72     , pageSize(other.pageSize)
73     , pageRange(other.pageRange)
74 {
75     notify = other.notify;
76     autoCover = other.autoCover;
77     coverIsTemp = other.coverIsTemp;
78     sendTagLine = other.sendTagLine;
79     useXVRes = other.useXVRes;
80     retryTime = other.retryTime;
81     hres = other.hres;
82     vres = other.vres;
83     pageWidth = other.pageWidth;
84     pageLength = other.pageLength;
85     maxRetries = other.maxRetries;
86     maxDials = other.maxDials;
87     priority = other.priority;
88     minsp = other.minsp;
89     desiredbr = other.desiredbr;
90     desiredst = other.desiredst;
91     desiredec = other.desiredec;
92     desireddf = other.desireddf;
93     pagechop = other.pagechop;
94     chopthreshold = other.chopthreshold;
95     pageRange = other.pageRange;
96 }
~SendFaxJob()97 SendFaxJob::~SendFaxJob()
98 {
99     if (coverFile != "" && coverIsTemp)
100 	Sys::unlink(coverFile);
101 }
102 
103 /*
104  * Configuration file support.
105  */
106 #define	N(a)	(sizeof (a) / sizeof (a[0]))
107 
108 SendFaxJob::SFJ_stringtag SendFaxJob::strings[] = {
109 { "tagline",		&SendFaxJob::tagline,		NULL },
110 { "sendtime",		&SendFaxJob::sendTime,		NULL },
111 { "killtime",		&SendFaxJob::killTime,		FAX_TIMEOUT },
112 { "pagesize",		&SendFaxJob::pageSize,		"default" },
113 { "jobtag",		&SendFaxJob::jobtag,		NULL },
114 { "subaddress",		&SendFaxJob::subaddr,		NULL },
115 { "password",		&SendFaxJob::passwd,		NULL },
116 { "cover-template",	&SendFaxJob::coverTemplate,	NULL },
117 { "cover-comments",	&SendFaxJob::comments,		NULL },
118 { "cover-regarding",	&SendFaxJob::regarding,		NULL },
119 { "cover-company",	&SendFaxJob::company,		NULL },
120 { "cover-location",	&SendFaxJob::location,		NULL },
121 { "cover-voice",	&SendFaxJob::voicenumber,	NULL },
122 { "cover-from-location",	&SendFaxJob::fromlocation,	NULL },
123 { "cover-from-fax",	&SendFaxJob::fromfax,		NULL },
124 { "cover-from-voice",	&SendFaxJob::fromvoice,		NULL },
125 { "cover-from-company",	&SendFaxJob::fromcompany,	NULL },
126 { "pagerange",		&SendFaxJob::pageRange,		NULL },
127 };
128 SendFaxJob::SFJ_numbertag SendFaxJob::numbers[] = {
129 { "maxtries",		&SendFaxJob::maxRetries,	FAX_RETRIES },
130 { "maxdials",		&SendFaxJob::maxDials,		FAX_REDIALS },
131 };
132 SendFaxJob::SFJ_floattag SendFaxJob::floats[] = {
133 { "hres",		&SendFaxJob::hres,		204. },
134 { "vres",		&SendFaxJob::vres,		FAX_DEFVRES },
135 { "pagewidth",		&SendFaxJob::pageWidth,		0. },
136 { "pagelength",		&SendFaxJob::pageLength,	0. },
137 { "chopthreshold",	&SendFaxJob::chopthreshold,	3.0 },
138 };
139 
140 void
setupConfig()141 SendFaxJob::setupConfig()
142 {
143     int i;
144 
145     for (i = N(strings)-1; i >= 0; i--)
146 	(*this).*strings[i].p = (strings[i].def ? strings[i].def : "");
147     for (i = N(numbers)-1; i >= 0; i--)
148 	(*this).*numbers[i].p = numbers[i].def;
149     for (i = N(floats)-1; i >= 0; i--)
150 	(*this).*floats[i].p = floats[i].def;
151 
152     autoCover = true;
153     sendTagLine = false;		// default is to use server config
154     useXVRes = false;			// default is to use normal or fine
155     notify = FAX_DEFNOTIFY;		// default notification
156     mailbox = "";
157     priority = FAX_DEFPRIORITY;		// default transmit priority
158     minsp = (u_int) -1;
159     desiredbr = (u_int) -1;
160     desiredst = (u_int) -1;
161     desiredec = (u_int) -1;
162     desireddf = (u_int) -1;
163     retryTime = (u_int) -1;
164     pagechop = chop_default;
165 }
166 
167 bool
setConfigItem(const char * tag,const char * value)168 SendFaxJob::setConfigItem(const char* tag, const char* value)
169 {
170     u_int ix;
171     if (FaxConfig::findTag(tag, (const FaxConfig::tags*) strings, N(strings), ix)) {
172 	(*this).*strings[ix].p = value;
173 	switch (ix) {
174 	case 0:	sendTagLine = true; break;
175 	}
176     } else if (FaxConfig::findTag(tag, (const FaxConfig::tags*) numbers, N(numbers), ix)) {
177 	(*this).*numbers[ix].p = atoi(value);
178     } else if (FaxConfig::findTag(tag, (const FaxConfig::tags*) floats, N(floats), ix)) {
179 	(*this).*floats[ix].p = atof(value);
180     } else if (streq(tag, "autocoverpage"))
181 	setAutoCoverPage(FaxConfig::getBoolean(value));
182     else if (streq(tag, "notify") || streq(tag, "notification"))
183 	setNotification(value);
184     else if (streq(tag, "mailaddr"))
185 	setMailbox(value);
186     else if (streq(tag, "priority"))
187 	setPriority(value);
188     else if (streq(tag, "minspeed"))
189 	setMinSpeed(value);
190     else if (streq(tag, "desiredspeed"))
191 	setDesiredSpeed(value);
192     else if (streq(tag, "desiredmst"))
193 	setDesiredMST(value);
194     else if (streq(tag, "desiredec"))
195 	setDesiredEC(FaxConfig::getBoolean(value));
196     else if (streq(tag, "usexvres"))
197 	setUseXVRes(FaxConfig::getBoolean(value));
198     else if (streq(tag, "desireddf"))
199 	setDesiredDF(value);
200     else if (streq(tag, "retrytime"))
201 	setRetryTime(value);
202     else if (streq(tag, "pagechop"))
203 	setChopHandling(value);
204     else
205 	return (false);
206     return (true);
207 }
208 #undef N
209 
210 #define	valeq(a,b)	(strcasecmp(a,b)==0)
211 #define	valneq(a,b,n)	(strncasecmp(a,b,n)==0)
212 
setDoneOp(const char * v)213 void SendFaxJob::setDoneOp(const char* v)	{ doneop = v; }
214 
215 bool
setNotification(const char * v0)216 SendFaxJob::setNotification(const char* v0)
217 {
218     const char* v = v0;
219     if (valneq(v, "when", 4)) {
220 	for (v += 4; isspace(*v); v++)
221 	    ;
222     }
223     if (valeq(v, "done"))
224 	notify = when_done;
225     else if (valneq(v, "req", 3))
226 	notify = when_requeued;
227     else if (valeq(v, "none") || valeq(v, "off"))
228 	notify = no_notice;
229     else if (valeq(v, "default"))
230 	notify = FAX_DEFNOTIFY;
231     else
232 	return (false);
233     return (true);
234 }
setNotification(FaxNotify n)235 void SendFaxJob::setNotification(FaxNotify n)		{ notify = n; }
236 /*
237  * Create the mail address for a local user.
238  */
239 void
setMailbox(const char * user)240 SendFaxJob::setMailbox(const char* user)
241 {
242     fxStr acct(user);
243     if (acct != "" && acct.next(0, "@!") == acct.length()) {
244 	static fxStr domainName;
245 	if (domainName == "") {
246 	    char hostname[64];
247 	    (void) gethostname(hostname, sizeof (hostname));
248 #if CONFIG_INETTRANSPORT
249 	    struct hostent* hp = gethostbyname(hostname);
250 	    domainName = (hp ? hp->h_name : hostname);
251 #else
252 	    domainName = hostname;
253 #endif
254 	}
255 	mailbox = acct | "@" | domainName;
256     } else
257 	mailbox = acct;
258     // strip leading & trailing white space
259     mailbox.remove(0, mailbox.skip(0, " \t"));
260     mailbox.resize(mailbox.skipR(mailbox.length(), " \t"));
261 }
setJobTag(const char * s)262 void SendFaxJob::setJobTag(const char* s)		{ jobtag = s; }
263 
264 void
setRetryTime(const char * v)265 SendFaxJob::setRetryTime(const char* v)
266 {
267     char* cp;
268     u_int t = (u_int) strtoul(v, &cp, 10);
269     if (cp) {
270 	while (isspace(*cp))
271 	    ;
272 	if (strncasecmp(cp, "min", 3) == 0)
273 	    t *= 60;
274 	else if (strncasecmp(cp, "hour", 4) == 0)
275 	    t *= 60*60;
276 	else if (strncasecmp(cp, "day", 3) == 0)
277 	    t *= 24*60*60;
278     }
279     retryTime = t;
280 }
setRetryTime(u_int v)281 void SendFaxJob::setRetryTime(u_int v)			{ retryTime = v; }
setKillTime(const char * s)282 void SendFaxJob::setKillTime(const char* s)		{ killTime = s; }
setSendTime(const char * s)283 void SendFaxJob::setSendTime(const char* s)		{ sendTime = s; }
setMaxRetries(u_int n)284 void SendFaxJob::setMaxRetries(u_int n)			{ maxRetries = n; }
setMaxDials(u_int n)285 void SendFaxJob::setMaxDials(u_int n)			{ maxDials = n; }
286 void
setPriority(const char * pri)287 SendFaxJob::setPriority(const char* pri)
288 {
289     if (valeq(pri, "default") || valeq(pri, "normal"))
290 	priority = FAX_DEFPRIORITY;
291     else if (valeq(pri, "bulk") || valeq(pri, "junk"))
292 	priority = FAX_DEFPRIORITY + 4*16;
293     else if (valeq(pri, "low"))
294 	priority = FAX_DEFPRIORITY + 4*16-1;
295     else if (valeq(pri, "high"))
296 	priority = FAX_DEFPRIORITY - 4*16;
297     else
298 	priority = atoi(pri);
299 }
setPriority(int p)300 void SendFaxJob::setPriority(int p)			{ priority = p; }
301 
setTSI(const char * s)302 void SendFaxJob::setTSI(const char* s)			{ tsi = s; }
setDialString(const char * s)303 void SendFaxJob::setDialString(const char* s)		{ number = s; }
setSubAddress(const char * s)304 void SendFaxJob::setSubAddress(const char* s)		{ subaddr = s; }
setPassword(const char * s)305 void SendFaxJob::setPassword(const char* s)		{ passwd = s; }
setExternalNumber(const char * s)306 void SendFaxJob::setExternalNumber(const char* s)	{ external = s; }
307 
setAutoCoverPage(bool b)308 void SendFaxJob::setAutoCoverPage(bool b)		{ autoCover = b; }
309 void
setCoverPageFile(const char * s,bool removeOnExit)310 SendFaxJob::setCoverPageFile(const char* s, bool removeOnExit)
311 {
312     if (coverFile != "" && removeOnExit)
313 	Sys::unlink(coverFile);
314     coverFile = s;
315     coverIsTemp = removeOnExit;
316 }
setCoverTemplate(const char * s)317 void SendFaxJob::setCoverTemplate(const char* s)	{ coverTemplate = s; }
setCoverName(const char * s)318 void SendFaxJob::setCoverName(const char* s)		{ name = s; }
setCoverLocation(const char * s)319 void SendFaxJob::setCoverLocation(const char* s)	{ location = s; }
setCoverCompany(const char * s)320 void SendFaxJob::setCoverCompany(const char* s)		{ company = s; }
setCoverComments(const char * s)321 void SendFaxJob::setCoverComments(const char* s)	{ comments = s; }
setCoverRegarding(const char * s)322 void SendFaxJob::setCoverRegarding(const char* s)	{ regarding = s; }
setCoverVoiceNumber(const char * s)323 void SendFaxJob::setCoverVoiceNumber(const char* s)	{ voicenumber = s; }
setCoverFromLocation(const char * s)324 void SendFaxJob::setCoverFromLocation(const char* s)	{ fromlocation = s; }
setCoverFromFax(const char * s)325 void SendFaxJob::setCoverFromFax(const char* s)		{ fromfax = s; }
setCoverFromVoice(const char * s)326 void SendFaxJob::setCoverFromVoice(const char* s)	{ fromvoice = s; }
setCoverFromCompany(const char * s)327 void SendFaxJob::setCoverFromCompany(const char* s)	{ fromcompany = s; }
328 
329 bool
setPageSize(const char * name)330 SendFaxJob::setPageSize(const char* name)
331 {
332     PageSizeInfo* info = PageSizeInfo::getPageSizeByName(name);
333     if (info) {
334 	pageWidth = info->width();
335 	pageLength = info->height();
336 	pageSize = name;
337 	delete info;
338 	return (true);
339     } else
340 	return (false);
341 }
setVResolution(float r)342 void SendFaxJob::setVResolution(float r)		{ vres = r; }
setHResolution(float r)343 void SendFaxJob::setHResolution(float r)		{ hres = r; }
344 
345 int
getSpeed(const char * value) const346 SendFaxJob::getSpeed(const char* value) const
347 {
348     switch (atoi(value)) {
349     case 2400:	return (0);
350     case 4800:	return (1);
351     case 7200:	return (2);
352     case 9600:	return (3);
353     case 12000:	return (4);
354     case 14400:	return (5);
355     case 16800:	return (6);
356     case 19200:	return (7);
357     case 21600:	return (8);
358     case 24000:	return (9);
359     case 26400:	return (10);
360     case 28800:	return (11);
361     case 31200:	return (12);
362     case 33600:	return (13);
363     }
364     return (-1);
365 }
setMinSpeed(int v)366 void SendFaxJob::setMinSpeed(int v)			{ minsp = v; }
setMinSpeed(const char * v)367 void SendFaxJob::setMinSpeed(const char* v)		{ minsp = getSpeed(v); }
setDesiredSpeed(int v)368 void SendFaxJob::setDesiredSpeed(int v)			{ desiredbr = v; }
setDesiredSpeed(const char * v)369 void SendFaxJob::setDesiredSpeed(const char* v)		{ desiredbr = getSpeed(v); }
370 void
setDesiredMST(const char * v)371 SendFaxJob::setDesiredMST(const char* v)
372 {
373     if (valeq(v, "0ms"))
374 	desiredst = 0;
375     else if (valeq(v, "5ms"))
376 	desiredst = 1;
377     else if (valeq(v, "10ms2"))
378 	desiredst = 2;
379     else if (valeq(v, "10ms"))
380 	desiredst = 3;
381     else if (valeq(v, "20ms2"))
382 	desiredst = 4;
383     else if (valeq(v, "20ms"))
384 	desiredst = 5;
385     else if (valeq(v, "40ms2"))
386 	desiredst = 6;
387     else if (valeq(v, "40ms"))
388 	desiredst = 7;
389     else
390 	desiredst = atoi(v);
391 }
setDesiredMST(int v)392 void SendFaxJob::setDesiredMST(int v)			{ desiredst = v; }
setDesiredEC(bool b)393 void SendFaxJob::setDesiredEC(bool b)			{ desiredec = b; }
setUseXVRes(bool b)394 void SendFaxJob::setUseXVRes(bool b)			{ useXVRes = b; }
395 void
setDesiredDF(const char * v)396 SendFaxJob::setDesiredDF(const char* v)
397 {
398     if (strcasecmp(v, "1d") == 0 || strcasecmp(v, "1dmh") == 0 || strcasecmp(v, "1dmr") == 0)
399 	desireddf = 0;
400     else if (strcasecmp(v, "2d") == 0 || strcasecmp(v, "2dmr") == 0)
401 	desireddf = 1;
402     else if (strcasecmp(v, "2dmruncomp") == 0)
403 	desireddf = 1;				// NB: force 2D w/o uncompressed
404     else if (strcasecmp(v, "2dmmr") == 0)
405 	desireddf = 3;
406     else
407 	desireddf = atoi(v);
408 }
setDesiredDF(int df)409 void SendFaxJob::setDesiredDF(int df)			{ desireddf = df; }
410 
411 void
setTagLineFormat(const char * v)412 SendFaxJob::setTagLineFormat(const char* v)
413 {
414     tagline = v;
415     sendTagLine = true;
416 }
417 
418 void
setChopHandling(const char * v)419 SendFaxJob::setChopHandling(const char* v)
420 {
421     if (strcasecmp(v, "none") == 0)
422 	pagechop = chop_none;
423     else if (strcasecmp(v, "all") == 0)
424 	pagechop = chop_all;
425     else if (strcasecmp(v, "last") == 0)
426 	pagechop = chop_last;
427     else
428 	pagechop = atoi(v);
429 }
setChopHandling(u_int v)430 void SendFaxJob::setChopHandling(u_int v)		{ pagechop = v; }
setChopThreshold(float v)431 void SendFaxJob::setChopThreshold(float v)		{ chopthreshold = v; }
setPageRange(const char * v)432 void SendFaxJob::setPageRange (const char* v)		{ pageRange = v; }
433 
434 extern int
435 parseAtSyntax(const char* s, const struct tm& ref, struct tm& at0, fxStr& emsg);
436 
437 #define	CHECK(x)	{ if (!(x)) goto failure; }
438 #define	CHECKCMD(x)	CHECK(client.command(x) == COMPLETE)
439 #define	CHECKPARM(a,b)	CHECK(client.jobParm(a,b))
440 #define	IFPARM(a,b,v)	{ if ((b) != (v)) CHECKPARM(a,b) }
441 
442 bool
createJob(SendFaxClient & client,fxStr & emsg)443 SendFaxJob::createJob(SendFaxClient& client, fxStr& emsg)
444 {
445     if (!client.setCurrentJob("DEFAULT")) {	// inherit from default
446 	emsg = client.getLastResponse();
447 	return (false);
448     }
449     if (!client.newJob(jobid, groupid, emsg))	// create new job on server
450 	return (false);
451 
452     time_t now = Sys::now();
453 
454     CHECKPARM("FROMUSER", client.getSenderName())
455 
456     struct tm tts;
457     if (sendTime != "") {
458 	if (!parseAtSyntax(sendTime, *localtime(&now), tts, emsg)) {
459 	    emsg.insert(sendTime | ": ");
460 	    return (false);
461 	}
462 	now = mktime(&tts);
463 	// NB: must send time relative to GMT
464 	CHECK(client.jobSendTime(*gmtime(&now)))
465     } else
466 	tts = *localtime(&now);
467     if (killTime != "") {
468 	struct tm when;
469 	if (!parseAtSyntax(killTime, tts, when, emsg)) {
470 	    emsg.insert(killTime | ": ");
471 	    return (false);
472 	}
473 	CHECK(client.jobLastTime(mktime(&when) - now))
474     }
475     if (retryTime != (u_int) -1)
476 	CHECK(client.jobRetryTime(retryTime))
477     IFPARM("MODEM", client.getModem(), "");	// XXX should be per-job state
478     IFPARM("MAXDIALS", maxDials, (u_int) -1)
479     IFPARM("MAXTRIES", maxRetries, (u_int) -1)
480     CHECKPARM("SCHEDPRI", priority)
481     /*
482      * If the dialstring is different from the
483      * displayable number then pass both.
484      */
485     IFPARM("TSI", tsi, "")
486     IFPARM("EXTERNAL", external, number)
487     CHECKPARM("DIALSTRING", number)
488     IFPARM("SUBADDR", subaddr, "")
489     IFPARM("PASSWD", passwd, "")
490     CHECKPARM("NOTIFYADDR", mailbox)
491     IFPARM("TOUSER", name, "")
492     IFPARM("TOCOMPANY", company, "")
493     IFPARM("TOLOCATION", location, "")
494     IFPARM("TOVOICE", voicenumber, "")
495     IFPARM("FROMCOMPANY", fromcompany, "")
496     IFPARM("FROMLOCATION", fromlocation, "")
497     IFPARM("FROMVOICE", fromvoice, "")
498     IFPARM("REGARDING", regarding, "")
499     IFPARM("JOBINFO", jobtag, "")
500     CHECKPARM("VRES", (u_int) vres)
501     CHECKPARM("PAGEWIDTH", (u_int) pageWidth)
502     CHECKPARM("PAGELENGTH", (u_int) pageLength)
503     IFPARM("MINBR", minsp, (u_int) -1)
504     IFPARM("BEGBR", desiredbr, (u_int) -1)
505     IFPARM("BEGST", desiredst, (u_int) -1)
506     if (desiredec != (u_int) -1)
507 	CHECKPARM("USEECM", (bool) desiredec)
508     if (desireddf != (u_int) -1) {
509 	CHECKPARM("DATAFORMAT",
510 	    desireddf == 0	? "g31d" :
511 	    desireddf == 1	? "g32d" :
512 	    desireddf == 2	? "g32dunc" :
513 	    desireddf == 3	? "g4"   :
514 				  "g31d")
515     }
516     if (sendTagLine) {
517 	CHECKPARM("USETAGLINE", true)
518 	CHECKPARM("TAGLINE", tagline)
519     }
520     if (useXVRes) {
521 	CHECKPARM("USEXVRES", true)
522     }
523     if (doneop == "archive") {
524 	CHECKPARM("DONEOP", "archive")
525     }
526     CHECKPARM("NOTIFY",
527 	notify == when_done	? "done" :
528 	notify == when_requeued	? "done+requeue" :
529 				  "none")
530     CHECKPARM("PAGECHOP",
531 	pagechop == chop_default? "default" :
532 	pagechop == chop_none	? "none" :
533 	pagechop == chop_all	? "all" :
534 				  "last")
535     IFPARM("CHOPTHRESHOLD", chopthreshold, -1)
536     IFPARM("PAGERANGE", pageRange, "")
537     if (coverFile != "") {
538 	int fd = Sys::open(coverFile, O_RDONLY);
539 	if (fd < 0) {
540 	    emsg = fxStr::format(NLS::TEXT("%s: Can not open: %s"),
541 		(const char*) coverFile, strerror(errno));
542 	    return (false);			// XXX
543 	}
544 	fxStr coverDoc;
545 	bool fileSent =
546 	       client.setFormat(FaxClient::FORM_PS)
547 	    && client.setType(FaxClient::TYPE_I)	// XXX??? TYPE_A
548 	    && client.sendZData(fd, &FaxClient::storeTemp, coverDoc, emsg);
549 	Sys::close(fd);
550 	if (!fileSent) {
551 	    if (emsg == "")
552 		emsg = NLS::TEXT("Document transfer failed: ") | client.getLastResponse();
553 	    return (false);
554 	}
555 	CHECK(client.jobCover(coverDoc))
556     }
557     /*
558      * Append documents and polling requests.
559      */
560     u_int i, n;
561     for (i = 0, n = client.getNumberOfFiles(); i < n; i++)
562 	CHECK(client.jobDocument(client.getFileDocument(i)))
563     for (i = 0, n = client.getNumberOfPollRequests(); i < n; i++) {
564 	fxStr sep, pwd;
565 	client.getPollRequest(i, sep, pwd);
566 	CHECK(client.jobPollRequest(sep, pwd))
567     }
568     return (true);
569 failure:
570     emsg = client.getLastResponse();
571     return (false);
572 }
573 #undef CHECKPARM
574 #undef IFPARM
575 #undef CHECKCMD
576 #undef CHECK
577 
578 fxIMPLEMENT_ObjArray(SendFaxJobArray, SendFaxJob)
579