Changelog for HylaFAX 4.2.1
+* improve modern distinctive ring support, add
+ NoAnswerVoice config feature, and enhance third-party
+ getty interaction (1 Jan 2005)
* add support for etc/resetmodem (30 Dec 2004)
* fix NOTIFY_FAXMASTER within notify (30 Dec 2004)
* improve tiff2pdf and use it more (29 Dec 2004)
Alvaro Aguilera Andre Albsmeier a.pogoda@web.de
Ronald Appelfelder Heinz-Ado Arnolds Jay Ashworth
Abramo Bagnara Chris Bainbridge Charly Baker
-Toki Barron Stephan Bauer Dmitry Bely
-Thomas Biege David Birnbaum Trevor Blackwell
-Siegfried Bosch Marc Boucher Ross Boylan
-Frank Brock Glen Burkhardt Yves Carlier
-Ed Casas Jonathon Chen Robert Colquhoun
-Ken Cornetet Seth Cuaiklin Albert DeKnuydt
-Paul Eggert Juergen Fischer Kevin Fleming
-John Florian Patrice Fournier Vyacheslav Frolov
-John Gilman Kris Henderson Sascha Herrmann
-Carsten Hoeger Lee Howard John Interrante
-Damiam Ivereigh Bernard Jech Nico Kadel-Garcia
-Sean Kamath Jonathan Kamens Dimitry Ketov
-Steffan Klipsch Hannu Koivisto Arjen de Korte
-Ken Lalonde Ilguiz Latypov Rob Leadbeater
-Guillaume Legoupil Greg Luck Lionel Mamane
-Pierluigi Mangani Campbell McKilligan Bodo Meissner
-John Miller Aldo Mini Boris Mironov
-Seth Mos Darren Nickerson Andrea Nicolini
-Giulio Orsero Chris Parsons Michael Pedersen
-John Perkins Simon Perry Frank Peters
-Vu Pham John Patrick Poet Harald Pollack
-Andreas Pretzsch Bernd Proissl Matthew Rice
-Tim Rice Daniel Robbins Giuseppe Sacco
-Michael Salzmann Sven Schmidt John Sellens
-Simon <iahnl@iah.nl> Vilmos Soti Alan Sparks
-Andy Sparrow Marco Steinacher Iain Stevenson
-Marius Strobl Kazuhide Takahashi Daryl Thachuk
-Steve Tuckner Aidan Van Dyk Norbert Warmuth
-Phil Watkinson James Werkowski Chas Williams
-John Williams Steve Williams John Wolfe
-Bill Young Bruce Young
+Toki Barron Stephan Bauer George Bell
+Dmitry Bely Thomas Biege David Birnbaum
+Trevor Blackwell Siegfried Bosch Marc Boucher
+Ross Boylan Frank Brock Glen Burkhardt
+Yves Carlier Ed Casas Jonathon Chen
+Robert Colquhoun Ken Cornetet Seth Cuaiklin
+Albert DeKnuydt Paul Eggert Juergen Fischer
+Kevin Fleming John Florian Patrice Fournier
+Vyacheslav Frolov John Gilman Kris Henderson
+Sascha Herrmann Carsten Hoeger Lee Howard
+John Interrante Damiam Ivereigh Bernard Jech
+Nico Kadel-Garcia Sean Kamath Jonathan Kamens
+Dimitry Ketov Steffan Klipsch Hannu Koivisto
+Arjen de Korte Ken Lalonde Ilguiz Latypov
+Rob Leadbeater Guillaume Legoupil Greg Luck
+Lionel Mamane Pierluigi Mangani Campbell McKilligan
+Bodo Meissner John Miller Aldo Mini
+Boris Mironov Seth Mos Darren Nickerson
+Andrea Nicolini Giulio Orsero Chris Parsons
+Michael Pedersen John Perkins Simon Perry
+Frank Peters Vu Pham John Patrick Poet
+Harald Pollack Andreas Pretzsch Bernd Proissl
+Matthew Rice Tim Rice Daniel Robbins
+Giuseppe Sacco Michael Salzmann Sven Schmidt
+John Sellens Simon <iahnl@iah.nl> Vilmos Soti
+Alan Sparks Andy Sparrow Marco Steinacher
+Iain Stevenson Marius Strobl Kazuhide Takahashi
+Daryl Thachuk Steve Tuckner Aidan Van Dyk
+Norbert Warmuth Phil Watkinson James Werkowski
+Chas Williams John Williams Steve Williams
+John Wolfe Bill Young Bruce Young
We appreciate and recognize the upstreamed contributions from these
distributions:
ClassModem::waitForRings(u_short rings, CallType& type, CallerID& cid)
{
bool gotring = false;
+ u_int i = 0, count = 0;
+ int incadence[5] = { 0, 0, 0, 0, 0 };
time_t timeout = conf.ringTimeout/1000; // 6 second/ring
time_t start = Sys::now();
do {
type = CALLTYPE_FAX;
else if (streq(conf.ringVoice, rbuf))
type = CALLTYPE_VOICE;
+ else if (conf.dringOff.length() && strneq(conf.dringOff, rbuf, conf.dringOff.length())) {
+ if (count++ == 0) break; //discard initial DROFF code if present
+ incadence[i++] = -atoi(rbuf + conf.dringOff.length());
+ break;
+ }
+ else if (conf.dringOn.length() && strneq(conf.dringOn, rbuf, conf.dringOn.length())) {
+ ++count;
+ incadence[i++] = atoi(rbuf + conf.dringOn.length());
+ break;
+ }
else {
if (conf.ringExtended.length() && strneq(rbuf, conf.ringExtended, conf.ringExtended.length())) // extended RING
gotring = true;
return (false);
}
}
+ if (conf.dringOn.length()) { // Compare with all distinctive ring cadences
+ modemTrace("WFR: received cadence = %d, %d, %d, %d, %d", incadence[0], incadence[1], incadence[2], incadence[3], incadence[4]);
+ type = findCallType(incadence);
+ }
gotring = true;
break;
case AT_NOANSWER:
} while (!gotring && Sys::now()-start < timeout);
return (gotring);
}
+
+CallType ClassModem::findCallType(int vec[])
+{
+// compare x^2 than x to avoid use of math functions
+
+ double limit = 0.33*0.33;
+ double dif, sum;
+ u_int n, k;
+ for (n=0; n < conf.NoDRings; ++n) {
+ for (k=0, sum=0; k < 5; ++k) {
+ dif = vec[k] - conf.distinctiveRings[n].cadence[k];
+ sum += dif*dif;
+ }
+ if (sum/conf.distinctiveRings[n].magsqrd < limit)
+ return conf.distinctiveRings[n].type;
+ }
+ return CALLTYPE_UNKNOWN;
+}
+
void modemCapability(const char* fmt, ...);
void traceBits(u_int bits, const char* bitNames[]);
void traceBitMask(u_int bits, const char* bitNames[]);
+ CallType findCallType(int vec[]);
public:
virtual ~ClassModem();
* exec getty below.
*/
void
-Getty::setupArgv(const char* args)
+Getty::setupArgv(const char* args, const fxStr& name, const fxStr& number)
{
argbuf = args;
+ nambuf = name;
+ numbuf = number;
+ bool insertName = false, insertNumber = false;
u_int l;
/*
* Substitute escape sequences.
argbuf.insert(speed, l);
l += speed.length(); // avoid loops
break;
+ case 'a':
+ argbuf.remove(l-1,3);
+ insertName = true;
+ break;
+ case 'u':
+ argbuf.remove(l-1,3);
+ insertNumber = true;
+ break;
case '%': // %% = %
argbuf.remove(l,1);
break;
argv[nargs++] = &argbuf[token];
}
}
+ if (nargs < GETTY_MAXARGS-1 && insertName && nambuf.length())
+ argv[nargs++] = &nambuf[0];
+ if (nargs < GETTY_MAXARGS-1 && insertNumber && numbuf.length())
+ argv[nargs++] = &numbuf[0];
argv[nargs] = NULL;
}
Getty::hangup()
{
// NB: this is executed in the parent
- fxStr device = fxStr::format("%s/" | line, _PATH_DEV);
+ fxStr device = fxStr::format("%s" | line, _PATH_DEV);
Sys::chown(device, UUCPLock::getUUCPUid(), UUCPLock::getUUCPGid());
Sys::chmod(device, 0600); // reset protection
}
fxStr argbuf; // stash for argv strings
fxStr tzVar; // TZ environment variable
fxStr langVar; // LANG environment variable
+ fxStr nambuf;
+ fxStr numbuf;
protected:
Getty(const char* program, const fxStr& line, const fxStr& speed);
virtual void fatal(const char* fmt ...);
public:
virtual ~Getty();
-
- void setupArgv(const char* args); // setup arguments for getty process
+ // setup arguments for getty process
+ void setupArgv(const char* args, const fxStr&, const fxStr&);
// run getty process
virtual void run(int fd, bool parentIsInit);
virtual bool wait(int& status, bool block = false);
{ "ringextended", &ModemConfig::ringExtended },
{ "cidname", &ModemConfig::cidName },
{ "cidnumber", &ModemConfig::cidNumber },
+{ "dringon", &ModemConfig::dringOn },
+{ "dringoff", &ModemConfig::dringOff },
};
static struct {
const char* name;
for (i = N(numbers)-1; i >= 0; i--)
(*this).*numbers[i].p = numbers[i].def;
+ for (i=0; i < 5; ++i) {
+ distinctiveRings[i].type = ClassModem::CALLTYPE_UNKNOWN;
+ for (u_int j=0; j < 5; ++j)
+ distinctiveRings[i].cadence[j] = 0;
+ distinctiveRings[i].magsqrd = 0;
+ }
+
flowControl = ClassModem::FLOW_XONXOFF;// software flow control
maxRate = ClassModem::BR19200; // reasonable for most modems
minSpeed = BR_2400; // minimum transmit speed
rtnHandling = FaxModem::RTN_RETRANSMIT; // retransmit until MCF/MPS
saveUnconfirmedPages = true; // keep unconfirmed pages
softRTFCC = true; // real-time fax comp. conv. (software)
+ noAnswerVoice = false; // answer voice calls
}
void
cid.number = cid.number | rbuf+cidNumber.length();
}
+void
+ModemConfig::parseDR(const char* cin)
+{
+ if (strlen(cin) < 3) return;
+ char buf[2048];
+ strncpy(buf, cin, sizeof (buf));
+ char* cp = buf;
+ u_int i = 0;
+ char* cp1 = cp;
+ while (*cp++) {
+ if (*cp == ',') {
+ *cp = '\0'; // Nuke the ','
+ processDRString(cp1, i++);
+ cp1 = ++cp;
+ }
+ }
+ processDRString(cp1, i);
+ NoDRings = i + 1;
+}
+
+void
+ModemConfig::processDRString(char* cp, const u_int i)
+{
+ if (*cp == 'V')
+ distinctiveRings[i].type = ClassModem::CALLTYPE_VOICE;
+ else if (*cp == 'F')
+ distinctiveRings[i].type = ClassModem::CALLTYPE_FAX;
+ else if (*cp == 'D')
+ distinctiveRings[i].type = ClassModem::CALLTYPE_DATA;
+
+ u_int j = 0;
+ char *cp1 = cp += 2;
+ while (*cp++) {
+ if (*cp == ':') {
+ *cp = '\0'; // Nuke the ':'
+ distinctiveRings[i].cadence[j++] = atoi(cp1);
+ cp1 = ++cp;
+ }
+ }
+ distinctiveRings[i].cadence[j] = atoi(cp1);
+
+ double sum = 0;
+ for ( u_int k=0; k < 5; ++k )
+ sum += distinctiveRings[i].cadence[k]*distinctiveRings[i].cadence[k];
+ distinctiveRings[i].magsqrd = sum;
+}
+
bool
ModemConfig::setConfigItem(const char* tag, const char* value)
{
class2UseLineCount = getBoolean(value);
else if (streq(tag, "class2rtfcc"))
class2RTFCC = getBoolean(value);
+ else if (streq(tag, "noanswervoice"))
+ noAnswerVoice = getBoolean(value);
else if (streq(tag, "modemsoftrtfcc"))
softRTFCC = getBoolean(value);
else if (streq(tag, "saveunconfirmedpages"))
saveUnconfirmedPages = getBoolean(value);
+ else if (streq(tag, "distinctiverings"))
+ parseDR(value);
else
return (false);
return (true);
fxStr ringFax; // fax call ring string
fxStr ringVoice; // voice call ring string
fxStr ringExtended; // extended ring
+ fxStr dringOn; // pattern for distinctive ring silence interval
+ fxStr dringOff; // pattern for distinctive ring ring interval
+ bool noAnswerVoice; // leave voice calls unanswered
// caller id
fxStr cidName; // pattern for name info
fxStr cidNumber; // pattern for number info
u_int cidNameAnswerLength; // answer when CID received
u_int cidNumberAnswerLength; // answer when CID received
-
// protocol timers
u_int t1Timer; // T.30 T1 timer (ms)
u_int t2Timer; // T.30 T2 timer (ms)
RTNHandling rtnHandling; // RTN signal handling method
bool saveUnconfirmedPages; // don't delete unconfirmed pages
+ // Distinctive ring data as sequences of DRON/DROF intervals
+ struct {
+ int cadence[5]; // the ring cadence as a five-dimensional vector
+ double magsqrd; // magnitude of the vector squared
+ u_int type; // call type of voice, fax, or data
+ } distinctiveRings[5]; // up to 5 distinctive ring numbers
+ u_int NoDRings; // number of distinctive rings numbers found
+
virtual ~ModemConfig();
void parseCID(const char*, CallerID&) const;
const fxStr& getFlowCmd(FlowControl) const;
+ void parseDR(const char*);
+ void processDRString(char*, const u_int);
};
#endif /* _ModemConfig_ */
(const char*) received_cid.number, (const char*) received_cid.name);
}
++ringsHeard;
+ if (dringOn.length() && ctype == ClassModem::CALLTYPE_UNKNOWN) --ringsHeard;
+ // distinctive ring failed to identify - get another ring
+
/* DID modems may only signal a call with DID data - no RING */
if (ringsBeforeAnswer && (ringsHeard >= ringsBeforeAnswer) ||
(cid.name.length() >= cidNameAnswerLength &&
advanceRotary = false;
} else {
// NB: answer based on ctype, not atype
- ctype = modemAnswerCall(ctype, emsg, dialnumber);
+ if (!(noAnswerVoice && ctype == ClassModem::CALLTYPE_VOICE))
+ ctype = modemAnswerCall(ctype, emsg, dialnumber);
callResolved = processCall(ctype, emsg, cid);
}
} else if (atype == ClassModem::ANSTYPE_ANY) {
* then we take action based on the returned call type.
*/
ctype = runGetty("EXTERN GETTY", OSnewEGetty,
- egettyArgs, emsg, lockExternCalls, true);
+ egettyArgs, emsg, lockExternCalls, cid, true);
if (ctype == ClassModem::CALLTYPE_DONE) // NB: call completed
return (true);
if (ctype != ClassModem::CALLTYPE_ERROR)
traceServer("ANSWER: DATA CONNECTION");
if (gettyArgs != "") {
sendModemStatus("d");
- runGetty("GETTY", OSnewGetty, gettyArgs, emsg, lockDataCalls);
+ runGetty("GETTY", OSnewGetty, gettyArgs, emsg, lockDataCalls, cid);
sendModemStatus("e");
} else
traceServer("ANSWER: Data connections are not permitted");
traceServer("ANSWER: VOICE CONNECTION");
if (vgettyArgs != "") {
sendModemStatus("v");
- runGetty("VGETTY", OSnewVGetty, vgettyArgs, emsg, lockVoiceCalls);
+ runGetty("VGETTY", OSnewVGetty, vgettyArgs, emsg, lockVoiceCalls, cid);
sendModemStatus("w");
} else
traceServer("ANSWER: Voice connections are not permitted");
const char* args,
fxStr& emsg,
bool keepLock,
+ const CallerID& cid,
bool keepModem
)
{
emsg = fxStr::format("%s: could not create", what);
return (ClassModem::CALLTYPE_ERROR);
}
- getty->setupArgv(args);
+ getty->setupArgv(args, cid.name, cid.number);
/*
* The getty process should not inherit the lock file.
* Remove it here before the fork so that our state is
CallType runGetty(const char* what,
Getty* (*newgetty)(const fxStr&, const fxStr&),
const char* args, fxStr &emsg,
- bool keepLock, bool keepModem = false);
+ bool keepLock, const CallerID& cid,
+ bool keepModem = false);
void setRingsBeforeAnswer(int rings);
void listenBegin();
void listenForRing();
DestControls\(S1 string \- per-destination controls file
DeviceMode octal \s-10600\s+1 protection mode to use for modem device
DialStringRules\(S2 string \- dial string rules file
+DistinctiveRings string \- configuration for distinctive ring cadences
+DRingOff string \- distinctive ring ``off'' cadence indicator
+DRingOn string \- distinctive ring ``on'' cadence indicator
DynamicConfig string \- script for dynamic receive configuration
FAXNumber string \- facsimile modem phone number
FaxRcvdCmd string \s-1bin/faxrcvd\s+1 notification script for received facsimile
ModemReadyState string \s-1R\s+1 ``ready state'' sent by \fIfaxgetty\fP
ModemRingResponse string \- command to respond after hearing RING
ModemRingsBeforeResponse integer \s-10\s+1 the number of rings before ModemRingResponse
+NoAnswerVoice boolean \s-1false\s+1 disable the answering of voice-indicated calls
NoCarrierRetrys integer \s-11\s+1 number of times to retry dialing on ``\s-2NO CARRIER\s0''
NotifyCmd\(S2 string \s-1bin/notify\s+1 user notification command script
PageChop\(S1 string \s-1last\s+1 control automatic truncation of trailing whitespace
spooling area; e.g.
.BR etc/dialrules .
.TP
+.B DistinctiveRings
+Modern distinctive ring support on most modems indicates the ring cadence
+rather than the older style of ``RING1'', ``RING2'', etc. To indicate
+the ring cadence,
+.B DRingOn
+and
+.B DRingOff
+values are presented by the modem to the
+.I faxgetty
+process. The modem indicates the entire cadence between ``RING'' indications
+Like this:
+.nf
+
+ RING
+ DROF=40
+ DRON=8
+ DROF=4
+ DRON=8
+ RING
+
+.fi
+The corresponding
+.B DistinctiveRings
+parameter for this ring cadence would be:
+.nf
+
+ DistinctiveRings: F-8-4-8
+
+.fi
+where ``F'' tells the
+.I faxgetty
+process that the ring cadence is for a facsimile (``V'' for voice
+and ``D'' for data), and the other values describe the ring cadence
+with leading and ending
+.B DRingOff
+values ignored. Multiple ring cadences are indicated by delimiting
+them with commas in this fashion:
+.nf
+
+ DistinctiveRings: V-20,F-8-4-8,D-4-2-4-8
+
+.fi
+.TP
+.B DRingOff
+A string that identifies the ``off'' value in any distinctive
+ring cadence, for example ``\s-1DROF=\s+1''.
+See also
+.B DRingOn
+and
+.BR DistinctiveRings .
+.TP
+.B DRingOn
+A string that identifies the ``on'' value in any distinctive
+ring cadence, for example ``\s-1DRON=\s+1''.
+See also
+.B DRingOff
+and
+.BR DistinctiveRings .
+.TP
.B DynamicConfig
The pathname of the optional shell script, e.g. ``etc/localid'', that
makes dynamic configuration changes, i.e., to
value, then the \*(Fx scheduler assigns jobs to them in
a round-robin fashion.
.TP
+.B NoAnswerVoice
+Whether or not to disable the answering of calls indicated as
+voice (e.g. by
+.B DistinctiveRings
+).
+.TP
.B NoCarrierRetrys
The number of times to redial a phone number after receiving
a ``\s-1NO CARRIER\s+1'' result code when the number has not