Hylafax Developers Mailing List Archives
|
[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
[hylafax-devel] My RTN patch and many other changes/enhancements, third edition :-)
Now against the current CVS!
The following is included:
1. Method FaxModem::correctPhaseCData() to correct broken T.4 data,
stored in TIFF Class F, and t4test utility to test this method is
added (faxd/FaxModem.h, faxd/FaxModem.c++, faxd/G3Encoder.c++,
faxd/t4test.c++) -- RTN patch, part one
2. Class2SendRTC configuration parameter now affects Class2.0
operation (according to the standards DCE has to appends RTC
itself). (faxd/Class20.c++, man/config.4f) -- RTN patch, part two
3. RTNHandlingMethod configuration parameter and the related code
is added. Quote from config.4f:
[---cut---]
RTNHandlingMethod
Specifies how to react to RTN signal, received from the remote;
one of ``Retransmit'', ``Giveup'' and ``Ignore''. ``Retransmit''
assumes that the page is not sent succesfully if RTN signal has been received.
Hylafax will made up to 2 additional attempts to send the page,
decreasing signalling rate and retraining. If RTN is still there,
it will place up to 2 additional calls. So if the remote always respond with
RTN, the page will be send 9 times. Although this algorithm comply with
T.30 specs and was originally implemented by Sam Leffler as the only
possible choice, real fax machines behave completely different. There is a
non-written rule among fax developers, that RTN means ``over and out'' -- hang
up immediately and never try to send the same page to the same destination
again. That is because RTN usually indicates problems with flow control,
incorrectly encoded T.4 data, incompatibility between local and remote
equipment etc., but very rarely is caused by the real noise on the line.
This ``over and out'' behaviour can be activated by ``Giveup'' value.
There is also third option, not so radical as ``Giveup''. Yes, we will never
retransmit the page, but we can try to send the next page, and let the
remote to decide what to do (accept our decision or hang up). Thus one page will
(or will not) be missed but we have a chance to successfully send all other pages.
This behaviour can be activated by ``Ignore'' value.
[---cut---]
Maybe "Ignore" option should be default?
(faxd/ModemConfig.h, faxd/ModemConfig.c++, faxd/Class1Send.c++,
faxd/Class2Send.c++, man/config.4f) -- RTN patch, part three
4. Class2.0 post-page-handling code corrected. (faxd/Class20.c++)
5. Class1 post-page-handling code corrected. In case of RTN it behaves the
same (correct) way as in Class2/2.0. (faxd/Class1Send.c++)
6. Zyxel & USR template updates. Due to the USR firmware bug parameter
Class2SendRTC: "yes" is neccessary for USR Class 2.0 after applying the
patch. (config/zyxel-1496e, config/zyxel-1496e-2.0, config/zyxel-1,
config/usr-2.0)
7. NSF decoding routines, identifing remote fax equipment and remote
station ID are added. E.g., here is a quote from my log:
[---cut---]
Apr 13 19:56:43.17: [17273]: --> [4:+FCO]
Apr 13 19:56:43.17: [17273]: --> [58:+FNF:00 00 0E 00 00 00 16 0F 01 03 00 10 05 02 95 C8 08 ]
Apr 13 19:56:43.17: [17273]: REMOTE NSF "00 00 0E 00 00 00 16 0F 01 03 00 10 05 02 95 C8 08"
Apr 13 19:56:43.18: [17273]: NSF remote fax equipment: Panasonic KX-F780
Apr 13 19:56:43.18: [17273]: --> [26:+FCI: ]
Apr 13 19:56:43.18: [17273]: REMOTE CSI ""
Apr 13 19:56:43.18: [17273]: --> [20:+FIS:1,3,0,2,1,0,0,4]
Apr 13 19:56:43.18: [17273]: --> [2:OK]
[---cut---]
(faxd/NSF.h, faxd/NSF.c++, faxd/FaxModem.h,
faxd/FaxModeM.c++). Contribution to my NSF list is welcome! :-)
8. Default value for ModemResetCmds is now "ATZ". (ModemSoftResetCmd
parameter was never used :-)) (faxd/ModemConfig.c++, man/config.4f)
9. Unused ModemResetCmd parameter has been deleted. (faxd/ModemConfig.c++)
10. Default value for ModemFlowControl is now XONXOFF -- I think, reasonable
default for the most modems. (faxd/ModemConfig.c++, man/config.4f)
Proposed changes are significant enough, and although I have carefully
tested them, I would like to know your opinion before you commit them :-) I
also would be quite happy if someone read my changes to config.4f and
correct my broken English, where necessary :-)
I think I have made everything I promised for beta 3. Now it's your turn! :-))
Hope to hear from you soon,
Dmitry
diff -uNr hylafax-cvs/config/usr-2.0 hylafax-work/config/usr-2.0
--- hylafax-cvs/config/usr-2.0 Mon Jan 4 13:19:07 1999
+++ hylafax-work/config/usr-2.0 Sat Apr 1 23:42:27 2000
@@ -78,3 +78,7 @@
# It is not necessary for the Courier modem.
#
Class2NRCmd: AT+FNR=1,1,1,0
+#
+# Modem violates the Class 2.0 specs and do not send RTC itself
+#
+Class2SendRTC: yes
diff -uNr hylafax-cvs/config/zyxel-1496e hylafax-work/config/zyxel-1496e
--- hylafax-cvs/config/zyxel-1496e Tue Oct 13 00:47:48 1998
+++ hylafax-work/config/zyxel-1496e Thu Apr 13 18:03:30 2000
@@ -66,16 +66,9 @@
#
# Additional reset commands:
#
-# &B1 DTE-DCE rate is fixed at DTE setting
-# &N0 Auto-negotiate highest possible DCE-DCE link rate
-# &S0 DSR always on
-# *F0 Deny remote configuration
-# S18=2 Receive at 38400
-# S38.3=1 DCD on/off sequence follows UNIX standard; also
-# fix receiving baud rate at S18 value
-# S39=0 (avoid Class 2 compatibility hacks)
+# S38.3=1 DCD on/off sequence follows UNIX standard
#
-ModemResetCmds: AT&B1&N0&S0*F0S18=2S38.3=1S39=0
+ModemResetCmds: ATZS38.3=1
#
# We normally append the "@" symbol to the dial string so that
# the modem will wait 5 seconds before attempting to connect
@@ -106,8 +99,17 @@
#
PagerSetupCmds: AT&K0&N15 # use V.22 at 1200 bps (PageNet)
#
-# Rev 6.1x firmware have a bug in the ECM support so
-# explicitly disable it's use. To re-enable its use just
-# comment out the following line.
+# ECM is not implemented in U1496 despite AT+FCC=? says
#
Class2DCCQueryCmd: "!(0,1),(0-5),(0-4),(0-2),(0,1),(0),(0),(0-7)"
+#
+# Quality Checking is not implemented in U1496 despite AT+FCQ=? says
+#
+Class2CQQueryCmd: "!(0),(0)"
+#
+# Disable unnecessary, not implemented and possibly dangerous commands
+#
+Class2BORCmd: "AT"
+Class2PIECmd: "AT"
+Class2PHCTOCmd: "AT"
+Class2TBCCmd: "AT"
diff -uNr hylafax-cvs/config/zyxel-1496e-1 hylafax-work/config/zyxel-1496e-1
--- hylafax-cvs/config/zyxel-1496e-1 Tue Oct 13 00:47:48 1998
+++ hylafax-work/config/zyxel-1496e-1 Thu Apr 13 18:04:58 2000
@@ -55,13 +55,9 @@
#
# Additional reset commands:
#
-# &B1 DTE-DCE rate is fixed at DTE setting
-# &N0 Auto-negotiate highest possible DCE-DCE link rate
-# &S0 DSR always on
-# *F0 Deny remote configuration
# S38.3=1 DCD on/off sequence follows UNIX standard
#
-ModemResetCmds: AT&B1&N0&S0*F0S38.3=1
+ModemResetCmds: ATZS38.3=1
#
# We normally append the "@" symbol to the dial string so that
# the modem will wait 5 seconds before attempting to connect
diff -uNr hylafax-cvs/config/zyxel-1496e-2.0 hylafax-work/config/zyxel-1496e-2.0
--- hylafax-cvs/config/zyxel-1496e-2.0 Tue Oct 13 00:47:48 1998
+++ hylafax-work/config/zyxel-1496e-2.0 Thu Apr 13 18:03:51 2000
@@ -61,16 +61,9 @@
#
# Additional reset commands:
#
-# &B1 DTE-DCE rate is fixed at DTE setting
-# &N0 Auto-negotiate highest possible DCE-DCE link rate
-# &S0 DSR always on
-# *F0 Deny remote configuration
-# S18=2 Receive at 38400
-# S38.3=1 DCD on/off sequence follows UNIX standard; also
-# fix receiving baud rate at S18 value
-# S39=0 (avoid Class 2 compatibility hacks)
+# S38.3=1 DCD on/off sequence follows UNIX standard
#
-ModemResetCmds: AT&B1&N0&S0*F0S18=2S38.3=1S39=0
+ModemResetCmds: ATZS38.3=1
#
ModemDialCmd: ATDT%s # no '@' 'cuz then busy not recognized
NoCarrierRetrys: 3 # retry 3 times on no carrier
@@ -85,8 +78,17 @@
#
PagerSetupCmds: AT&K0&N15 # use V.22 at 1200 bps (PageNet)
#
-# Rev 6.1x firmware have a bug in the ECM support so
-# explicitly disable it's use. To re-enable its use just
-# comment out the following line.
+# ECM is not implemented in U1496 despite AT+FCC=? says
#
Class2DCCQueryCmd: "!(0,1),(0-5),(0-4),(0-2),(0,1),(0),(0),(0-7)"
+#
+# Quality Checking is not implemented in U1496 despite AT+FCQ=? says
+#
+Class2CQQueryCmd: "!(0),(0)"
+#
+# Disable unnecessary, not implemented and possibly dangerous commands
+#
+Class2BORCmd: "AT"
+Class2PIECmd: "AT"
+Class2PHCTOCmd: "AT"
+Class2TBCCmd: "AT"
diff -uNr hylafax-cvs/faxd/Class1Send.c++ hylafax-work/faxd/Class1Send.c++
--- hylafax-cvs/faxd/Class1Send.c++ Sun Jun 13 11:41:00 1999
+++ hylafax-work/faxd/Class1Send.c++ Wed Apr 12 20:35:03 2000
@@ -115,6 +115,7 @@
do {
switch (frame.getRawFCF()) {
case FCF_NSF:
+ recvNSF(NSF(frame.getFrameData(), frame.getFrameDataLength(), frameRev));
break;
case FCF_CSI:
{ fxStr csi; recvCSI(decodeTSI(csi, frame)); }
@@ -275,6 +276,7 @@
tracePPR("SEND recv", ppr);
switch (ppr) {
case FCF_RTP: // ack, continue after retraining
+ ignore:
params.br = (u_int) -1; // force retraining above
/* fall thru... */
case FCF_MCF: // ack confirmation
@@ -305,22 +307,29 @@
emsg = "Remote fax disconnected prematurely";
return (send_retry);
case FCF_RTN: // nak, retry after retraining
- if ((++ntrys % 2) == 0) {
- /*
- * Drop to a lower signalling rate and retry.
- */
- if (params.br == BR_2400) {
- emsg = "Unable to transmit page"
- " (NAK at all possible signalling rates)";
- return (send_retry);
- }
- --params.br;
- curcap = NULL; // force sendTraining to reselect
- }
- if (!sendTraining(params, 3, emsg))
- return (send_retry);
- morePages = true; // force continuation
- next = params; // avoid retraining above
+ switch( conf.rtnHandling ){
+ case RTN_IGNORE:
+ goto ignore; // ignore error and try to send next page
+ // after retraining
+ case RTN_GIVEUP:
+ emsg = "Unable to transmit page"
+ " (giving up after RTN)";
+ return (send_failed); // "over and out"
+ }
+ // case RTN_RETRANSMIT
+ if (++ntrys >= 3) {
+ emsg = "Unable to transmit page"
+ " (giving up after 3 attempts)";
+ return (send_retry);
+ }
+ if (params.br == BR_2400) {
+ emsg = "Unable to transmit page"
+ "(NAK at all possible signalling rates)";
+ return (send_retry);
+ }
+ next.br--;
+ curcap = NULL; // force sendTraining to reselect
+ morePages = true; // retransmit page
break;
case FCF_PIN: // nak, retry w/ operator intervention
emsg = "Unable to transmit page"
@@ -733,6 +742,12 @@
totdata = totdata+ts - (dp-data);
} else
dp = data;
+
+ /*
+ * correct broken Phase C (T.4) data if neccessary
+ */
+ correctPhaseCData(dp, &totdata, fillorder, params);
+
/*
* Send the page of data. This is slightly complicated
* by the fact that we may have to add zero-fill before the
diff -uNr hylafax-cvs/faxd/Class20.c++ hylafax-work/faxd/Class20.c++
--- hylafax-cvs/faxd/Class20.c++ Sun Jun 13 11:41:01 1999
+++ hylafax-work/faxd/Class20.c++ Thu Apr 13 15:49:15 2000
@@ -131,7 +131,7 @@
bool rc = sendPageData(tif, pageChop);
if (!rc)
abortDataTransfer();
- else
+ else if( conf.class2SendRTC )
rc = sendRTC(params.is2D());
if (flowControl == FLOW_XONXOFF)
setXONXOFF(getInputFlow(), FLOW_XONXOFF, ACT_DRAIN);
@@ -162,12 +162,24 @@
case AT_FHNG:
if (!isNormalHangup())
return (false);
- /* fall thru... */
- case AT_OK: // page data good
- ppr = PPR_MCF; // could be PPR_RTP/PPR_PIP
+ ppr = PPR_MCF;
return (true);
- case AT_ERROR: // page data bad
- ppr = PPR_RTN; // could be PPR_PIN
+ case AT_OK:
+ case AT_ERROR:
+ /*
+ * Despite of the (wrong) comment above,
+ * we do explicit status query e.g. to
+ * distinguish between RTN and PIN
+ */
+ {
+ fxStr s;
+ if(!atQuery("AT+FPS?", s) ||
+ sscanf(s, "%u", &ppr) != 1){
+ protoTrace("MODEM protocol botch (\"%s\"), %s",
+ (const char*)s, "can not parse PPR");
+ return (false); // force termination
+ }
+ }
return (true);
case AT_EMPTYLINE:
case AT_TIMEOUT:
diff -uNr hylafax-cvs/faxd/Class2Recv.c++ hylafax-work/faxd/Class2Recv.c++
--- hylafax-cvs/faxd/Class2Recv.c++ Sun Jun 13 11:41:01 1999
+++ hylafax-work/faxd/Class2Recv.c++ Wed Apr 12 19:57:20 2000
@@ -212,6 +212,12 @@
*/
if (hostDidCQ)
ppr = isQualityOK(params) ? PPR_MCF : PPR_RTN;
+#if 0
+ /*
+ * RTN debug code: always respond with RTN to sending facsimile
+ */
+ ppr = PPR_RTN;
+#endif
if (ppr & 1)
TIFFWriteDirectory(tif); // complete page write
else
diff -uNr hylafax-cvs/faxd/Class2Send.c++ hylafax-work/faxd/Class2Send.c++
--- hylafax-cvs/faxd/Class2Send.c++ Sun Jun 13 11:41:01 1999
+++ hylafax-work/faxd/Class2Send.c++ Wed Apr 12 19:17:04 2000
@@ -131,7 +131,7 @@
gotParams = parseClass2Capabilities(skipStatus(rbuf), dis);
break;
case AT_FNSF:
- recvNSF(skipStatus(rbuf));
+ recvNSF(NSF(skipStatus(rbuf)));
break;
case AT_FCSI:
recvCSI(stripQuotes(skipStatus(rbuf)));
@@ -250,6 +250,7 @@
case PPR_MCF: // page good
case PPR_PIP: // page good, interrupt requested
case PPR_RTP: // page good, retrain requested
+ ignore:
countPage(); // bump page count
notifyPageSent(tif);// update server
if (pph[2] == 'Z')
@@ -276,6 +277,15 @@
transferOK = true;
break;
case PPR_RTN: // page bad, retrain requested
+ switch( conf.rtnHandling ){
+ case RTN_IGNORE:
+ goto ignore; // ignore error and trying to send next page
+ case RTN_GIVEUP:
+ emsg = "Unable to transmit page"
+ " (giving up after RTN)";
+ goto failed; // "over and out"
+ }
+ // case RTN_RETRANSMIT
if (++ntrys >= 3) {
emsg = "Unable to transmit page"
" (giving up after 3 attempts)";
@@ -387,6 +397,12 @@
totdata = totdata+ts - (dp-data);
} else
dp = data;
+
+ /*
+ * correct broken Phase C (T.4) data if necessary
+ */
+ correctPhaseCData(dp, &totdata, fillorder, params);
+
beginTimedTransfer();
rc = putModemDLEData(dp, (u_int) totdata, bitrev, getDataTimeout());
endTimedTransfer();
diff -uNr hylafax-cvs/faxd/ClassModem.c++ hylafax-work/faxd/ClassModem.c++
--- hylafax-cvs/faxd/ClassModem.c++ Sun Jun 13 11:41:02 1999
+++ hylafax-work/faxd/ClassModem.c++ Thu Apr 13 18:21:17 2000
@@ -131,6 +131,8 @@
const fxStr& flow = conf.getFlowCmd(conf.flowControl);
resetCmds = "AT"
| stripAT(conf.resetCmds) // prepend to insure our needs
+ | "\nAT" // some modems do not allow any command
+ // after ATZ
| stripAT(conf.echoOffCmd)
| stripAT(conf.verboseResultsCmd)
| stripAT(conf.resultCodesCmd)
diff -uNr hylafax-cvs/faxd/FaxModem.c++ hylafax-work/faxd/FaxModem.c++
--- hylafax-cvs/faxd/FaxModem.c++ Sun Jun 13 11:41:03 1999
+++ hylafax-work/faxd/FaxModem.c++ Thu Apr 13 19:34:29 2000
@@ -164,16 +164,24 @@
return (false);
}
void
-FaxModem::recvNSF(const fxStr& s)
+FaxModem::recvNSF( const NSF& aNsf )
{
- nsf = s;
- protoTrace("REMOTE NSF \"%s\"", (const char*) nsf);
+ nsf = aNsf;
+ optFrames |= 0x8;
+ protoTrace("REMOTE NSF \"%s\"", (const char*) nsf.getHexNsf() );
+ protoTrace("NSF remote fax equipment: %s %s",
+ (const char*)nsf.getVendor(),
+ (const char*)nsf.getModel());
+ if( nsf.stationIdFound() )
+ protoTrace("NSF %sremote station ID: \"%s\"",
+ nsf.vendorFound()? "": "possible ",
+ nsf.getStationId());
}
bool
-FaxModem::getSendNSF(fxStr& s)
+FaxModem::getSendNSF(NSF& aNsf)
{
if (optFrames & 0x8) {
- s = nsf;
+ aNsf = nsf;
return (true);
} else
return (false);
@@ -623,4 +631,187 @@
{
if (curreq)
server.notifyPageSent(*curreq, TIFFFileName(tif));
+}
+
+/*
+ * Phase C data correction
+ */
+
+#include "G3Decoder.h"
+#include "G3Encoder.h"
+#include "StackBuffer.h"
+#include "Class2Params.h"
+
+class MemoryDecoder : public G3Decoder {
+private:
+ u_char* bp;
+ u_int width;
+ u_int byteWidth;
+ u_long cc;
+
+ u_int fillorder;
+ bool is2D;
+
+ u_char* endOfData; // used by cutExtraRTC
+
+ uint16* runs;
+ u_char* rowBuf;
+
+ int decodeNextByte();
+public:
+ MemoryDecoder(u_char* data, u_int wid, u_long n,
+ u_int fillorder, bool twoDim);
+ ~MemoryDecoder();
+ u_char* current() { return bp; }
+ void fixFirstEOL();
+ u_char* cutExtraRTC();
+};
+
+MemoryDecoder::MemoryDecoder(u_char* data, u_int wid, u_long n,
+ u_int order, bool twoDim)
+{
+ bp = data;
+ width = wid;
+ byteWidth = howmany(width, 8);
+ cc = n;
+
+ fillorder = order;
+ is2D = twoDim;
+
+ runs = new uint16[2*width]; // run arrays for cur+ref rows
+ rowBuf = new u_char[byteWidth];
+ setupDecoder(fillorder, is2D);
+ setRuns(runs, runs+width, width);
+}
+MemoryDecoder::~MemoryDecoder()
+{
+ delete rowBuf;
+ delete runs;
+}
+
+int
+MemoryDecoder::decodeNextByte()
+{
+ if (cc == 0)
+ raiseRTC(); // XXX don't need to recognize EOF
+ cc--;
+ return (*bp++);
+}
+
+#ifdef roundup
+#undef roundup
+#endif
+#define roundup(a,b) ((((a)+((b)-1))/(b))*(b))
+
+/*
+ * TIFF Class F specs say:
+ *
+ * "As illustrated in FIGURE 1/T.4 in Recommendation T.4 (the Red
+ * Book, page 20), facsimile documents begin with an EOL (which in
+ * Class F is byte-aligned)..."
+ *
+ * This is wrong! "Byte-aligned" first EOL means extra zero bits
+ * which are not allowed by T.4. Reencode first row to fix this
+ * "byte-alignment".
+ */
+void MemoryDecoder::fixFirstEOL()
+{
+ fxStackBuffer result;
+ G3Encoder enc(result);
+ enc.setupEncoder(fillorder, is2D);
+
+ memset(rowBuf, 0, byteWidth*sizeof(u_char)); // clear row to white
+ if(!RTCraised()) {
+ u_char* start = current();
+ (void)decodeRow(rowBuf, width);
+ /*
+ * syncronize to the next EOL and calculate pointer to it
+ * (see detailed explanation of look_ahead in TagLine.c++)
+ */
+ (void)isNextRow1D();
+ u_int look_ahead = roundup(getPendingBits(),8) / 8;
+ u_int decoded = current() - look_ahead - start;
+
+ enc.encode(rowBuf, width, 1);
+ u_int encoded = result.getLength();
+
+ while( encoded < decoded ){
+ result.put((char) 0);
+ encoded++;
+ }
+ if( encoded == decoded ){
+ memcpy(start, (const char*)result, encoded);
+ }
+ }
+}
+
+
+/*
+ * TIFF Class F specs say:
+ *
+ * "Aside from EOL's, TIFF Class F files contain only image data. This
+ * means that the Return To Control sequence (RTC) is specifically
+ * prohibited..."
+ *
+ * Nethertheless Ghostscript and possibly other TIFF Class F writers
+ * append RTC or single EOL to the last encoded line. Remove them.
+ */
+u_char* MemoryDecoder::cutExtraRTC()
+{
+ u_char* start = current();
+
+ /*
+ * We expect RTC near the end of data and thus
+ * do not check all image to save processing time.
+ * It's safe because we will resync on the first
+ * encountered EOL.
+ *
+ * NB: We expect G3Decoder::data==0 and
+ * G3Decoder::bit==0 (no data in the accumulator).
+ * As we cannot explicitly clear the accumulator
+ * (bit and data are private), cutExtraRTC()
+ * should be called immediately after
+ * MemoryDecoder() constructing.
+ */
+ const u_long CheckArea = 20;
+ if( cc > CheckArea ){
+ bp += (cc-CheckArea);
+ cc = CheckArea;
+ }
+
+ endOfData = NULL;
+ if(!RTCraised()) {
+ /*
+ * syncronize to the next EOL and calculate pointer to it
+ * (see detailed explanation of look_ahead in TagLine.c++)
+ */
+ (void)isNextRow1D();
+ u_int look_ahead = roundup(getPendingBits(),8) / 8;
+ endOfData = current() - look_ahead;
+ for (;;) {
+ if( decodeRow(NULL, width) ){
+ /*
+ * endOfData is now after last good row. Thus we correctly handle
+ * RTC, single EOL in the end, or no RTC/EOL at all
+ */
+ endOfData = current();
+ }
+ }
+ }
+ return endOfData;
+}
+
+void
+FaxModem::correctPhaseCData(u_char* buf, u_long* pBufSize,
+ u_int fillorder, const Class2Params& params)
+{
+ MemoryDecoder dec1(buf, params.pageWidth(), *pBufSize, fillorder, params.is2D());
+ dec1.fixFirstEOL();
+ /*
+ * We have to construct new decoder. See comments to cutExtraRTC().
+ */
+ MemoryDecoder dec2(buf, params.pageWidth(), *pBufSize, fillorder, params.is2D());
+ u_char* endOfData = dec2.cutExtraRTC();
+ if( endOfData )
+ *pBufSize = endOfData - buf;
}
diff -uNr hylafax-cvs/faxd/FaxModem.h hylafax-work/faxd/FaxModem.h
--- hylafax-cvs/faxd/FaxModem.h Sun Jun 13 11:41:03 1999
+++ hylafax-work/faxd/FaxModem.h Tue Apr 11 21:39:12 2000
@@ -33,12 +33,17 @@
#include "tiffio.h"
#include "G3Decoder.h"
#include "FaxSendStatus.h"
+#include "NSF.h"
class FaxMachineInfo;
class fxStackBuffer;
class FaxFont;
class FaxServer;
+// NB: these would be enums in the FaxModem class
+// if there were a portable way to refer to them!
+typedef unsigned int RTNHandling; // RTN signal handling method
+
/*
* This is an abstract class that defines the interface to
* the set of modem drivers. Real drivers are derived from
@@ -80,7 +85,7 @@
fxStr tsi; // received TSI/CSI
fxStr sub; // received subaddressing string
fxStr pwd; // received password string
- fxStr nsf; // received nonstandard facilities
+ NSF nsf; // received nonstandard facilities
// NB: remaining session state is below (params) or maintained by subclass
protected:
// NB: these are defined protected for convenience (XXX)
@@ -108,7 +113,7 @@
void recvTSI(const fxStr&);
void recvPWD(const fxStr&);
void recvSUB(const fxStr&);
- void recvNSF(const fxStr&);
+ void recvNSF(const NSF&);
void recvCSI(const fxStr&);
void recvDCS(const Class2Params&);
void recvSetupTIFF(TIFF* tif, long group3opts, int fillOrder);
@@ -132,7 +137,18 @@
bool setupTagLineSlop(const Class2Params&);
u_int getTagLineSlop() const;
u_char* imageTagLine(u_char* buf, u_int fillorder, const Class2Params&);
+/*
+ * Correct if neccessary Phase C (T.4) data (remove extra RTC etc.)
+ */
+ void correctPhaseCData(u_char* buf, u_long* pBufSize,
+ u_int fillorder, const Class2Params& params);
public:
+ enum {
+ RTN_RETRANSMIT = 0, // retransmit page after RTN until MCF/MPS
+ RTN_GIVEUP = 1, // immediately abort
+ RTN_IGNORE = 2, // ignore error and send next page
+ };
+
virtual ~FaxModem();
bool isFaxModem() const;
@@ -196,7 +212,7 @@
virtual void sendAbort() = 0;
// query interfaces for optional state
virtual bool getSendCSI(fxStr&);
- virtual bool getSendNSF(fxStr&);
+ virtual bool getSendNSF(NSF&);
/*
* Fax receive protocol. The expected sequence is:
diff -uNr hylafax-cvs/faxd/G3Encoder.c++ hylafax-work/faxd/G3Encoder.c++
--- hylafax-cvs/faxd/G3Encoder.c++ Sun Jun 13 11:41:03 1999
+++ hylafax-work/faxd/G3Encoder.c++ Mon Mar 27 13:19:51 2000
@@ -71,10 +71,13 @@
G3Encoder::encode(const void* vp, u_int w, u_int h)
{
u_int rowbytes = howmany(w, 8);
+ bool firstEOL = true;
while (h-- > 0) {
- if (bit != 4) // byte-align EOL
- putBits(0, (bit < 4) ? bit+4 : bit-4);
+ if( firstEOL ) // according to T.4 first EOL
+ firstEOL = false; // should not be aligned
+ else if (bit != 4)
+ putBits(0, (bit < 4) ? bit+4 : bit-4); // byte-align other EOLs
if (is2D)
putBits((EOL<<1)|1, 12+1);
else
diff -uNr hylafax-cvs/faxd/Makefile.in hylafax-work/faxd/Makefile.in
--- hylafax-cvs/faxd/Makefile.in Thu Sep 2 14:16:22 1999
+++ hylafax-work/faxd/Makefile.in Wed Apr 12 22:16:16 2000
@@ -76,6 +76,7 @@
HDLCFrame.c++ \
Job.c++ \
Modem.c++ \
+ NSF.c++ \
ModemConfig.c++ \
PCFFont.c++ \
QLink.c++ \
@@ -91,6 +92,7 @@
faxSendApp.c++ \
tagtest.c++ \
tsitest.c++ \
+ t4test.c++ \
pageSendApp.c++
MODEM_OBJS=ClassModem.o \
FaxModem.o \
@@ -110,6 +112,7 @@
G3Encoder.o \
HDLCFrame.o \
ModemConfig.o \
+ NSF.o \
FaxFont.o \
PCFFont.o \
TagLine.o
@@ -143,7 +146,7 @@
FAXGETTYOBJS= Getty.o Getty@GETTY@.o faxGettyApp.o
TARGETS=libfaxserver.${DSO} \
faxq faxsend faxgetty pagesend faxqclean \
- tsitest tagtest cqtest choptest
+ tsitest tagtest cqtest choptest t4test
default all::
@${MAKE} incdepend
@@ -193,10 +196,12 @@
${C++F} -o $@ tsitest.o libfaxserver.${DSO} ${LDFLAGS}
trigtest: trigtest.o libfaxserver.${DSO} ${LIBS}
${C++F} -o $@ trigtest.o libfaxserver.${DSO} ${LDFLAGS}
+t4test: t4test.o libfaxserver.${DSO} ${LIBS}
+ ${C++F} -o $@ t4test.o libfaxserver.${DSO} ${LDFLAGS}
PUTSERV=${INSTALL} -idb ${PRODUCT}.sw.server
install: default
${PUTSERV} -F ${SBIN} -m 755 -O faxq faxqclean
${PUTSERV} -F ${LIBEXEC} -m 755 -O faxgetty faxsend pagesend
- ${PUTSERV} -F ${SBIN} -m 755 -O tsitest tagtest cqtest choptest
+ ${PUTSERV} -F ${SBIN} -m 755 -O tsitest tagtest cqtest choptest t4test
diff -uNr hylafax-cvs/faxd/ModemConfig.c++ hylafax-work/faxd/ModemConfig.c++
--- hylafax-cvs/faxd/ModemConfig.c++ Mon Aug 23 15:19:33 1999
+++ hylafax-work/faxd/ModemConfig.c++ Thu Apr 13 17:50:20 2000
@@ -80,8 +80,7 @@
{ "modemanswerfaxbegincmd", &ModemConfig::answerFaxBeginCmd },
{ "modemanswerdatabegincmd", &ModemConfig::answerDataBeginCmd },
{ "modemanswervoicebegincmd", &ModemConfig::answerVoiceBeginCmd },
-{ "modemresetcmds", &ModemConfig::resetCmds },
-{ "modemresetcmd", &ModemConfig::resetCmds },
+{ "modemresetcmds", &ModemConfig::resetCmds, "ATZ" },
{ "modemdialcmd", &ModemConfig::dialCmd, "ATDT%s" },
{ "modemnoflowcmd", &ModemConfig::noFlowCmd },
{ "modemsoftflowcmd", &ModemConfig::softFlowCmd },
@@ -206,7 +205,7 @@
for (i = N(numbers)-1; i >= 0; i--)
(*this).*numbers[i].p = numbers[i].def;
- flowControl = ClassModem::FLOW_NONE;// no flow control
+ flowControl = ClassModem::FLOW_XONXOFF;// software flow control
maxRate = ClassModem::BR19200; // reasonable for most modems
minSpeed = BR_2400; // minimum transmit speed
waitForConnect = false; // unique modem answer response
@@ -214,6 +213,7 @@
class2SendRTC = false; // default per Class 2 spec
setVolumeCmds("ATM0 ATL0M1 ATL1M1 ATL2M1 ATL3M1");
recvDataFormat = DF_ALL; // default to no transcoding
+ rtnHandling = FaxModem::RTN_RETRANSMIT; // retransmit until MCF/MPS
}
void
@@ -463,6 +463,37 @@
return (df);
}
+bool
+ModemConfig::findRTNHandling(const char* cp, RTNHandling& rh)
+{
+ static const struct {
+ const char* name;
+ RTNHandling rh;
+ } rhnames[] = {
+ { "RETRANSMIT", FaxModem::RTN_RETRANSMIT },
+ { "GIVEUP", FaxModem::RTN_GIVEUP },
+ { "IGNORE", FaxModem::RTN_IGNORE },
+ { "H_POLLACK", FaxModem::RTN_IGNORE }, // inventor's name as an alias :-)
+ };
+ for (u_int i = 0; i < N(rhnames); i++)
+ if (valeq(cp, rhnames[i].name)) {
+ rh = rhnames[i].rh;
+ return (true);
+ }
+ return (false);
+}
+
+u_int
+ModemConfig::getRTNHandling(const char* cp)
+{
+ RTNHandling rh;
+ if (!findRTNHandling(cp, rh)) {
+ configError("Unknown RTN handling method \"%s\", using RETRANSMIT", cp);
+ rh = FaxModem::RTN_RETRANSMIT; // default
+ }
+ return (rh);
+}
+
void
ModemConfig::parseCID(const char* rbuf, CallerID& cid) const
{
@@ -501,6 +532,8 @@
minSpeed = getSpeed(value);
else if (streq(tag, "recvdataformat"))
recvDataFormat = getDataFormat(value);
+ else if (streq(tag, "rtnhandlingmethod"))
+ rtnHandling = getRTNHandling(value);
else
return (false);
return (true);
diff -uNr hylafax-cvs/faxd/ModemConfig.h hylafax-work/faxd/ModemConfig.h
--- hylafax-cvs/faxd/ModemConfig.h Sun Jun 13 11:41:04 1999
+++ hylafax-work/faxd/ModemConfig.h Tue Apr 11 21:39:08 2000
@@ -39,11 +39,13 @@
void setVolumeCmds(const fxStr& value);
u_int getSpeed(const char* value);
u_int getDataFormat(const char* value);
+ u_int getRTNHandling(const char* cp);
static bool findRate(const char*, BaudRate&);
static bool findATResponse(const char*, ATResponse&);
static bool findFlow(const char*, FlowControl&);
static bool findDataFormat(const char*, u_int&);
+ static bool findRTNHandling(const char*, RTNHandling&);
protected:
ModemConfig();
@@ -170,6 +172,8 @@
fxStr tagLineFontFile; // font file for imaging tag lines
u_int recvDataFormat; // received facsimile data format
+ RTNHandling rtnHandling; // RTN signal handling method
+
virtual ~ModemConfig();
void parseCID(const char*, CallerID&) const;
diff -uNr hylafax-cvs/faxd/NSF.c++ hylafax-work/faxd/NSF.c++
--- hylafax-cvs/faxd/NSF.c++ Thu Jan 1 03:00:00 1970
+++ hylafax-work/faxd/NSF.c++ Thu Apr 13 19:35:27 2000
@@ -0,0 +1,209 @@
+/*
+ * This file does not exist in the original Hylafax distribution.
+ * Created by Dmitry Bely, April 2000
+ */
+/*
+ * Copyright (c) 1994-1996 Sam Leffler
+ * Copyright (c) 1994-1996 Silicon Graphics, Inc.
+ * HylaFAX is a trademark of Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <ctype.h>
+#include "NSF.h"
+
+struct ModelData
+{
+ const char* modelId;
+ const char* modelName;
+};
+
+struct NSFData {
+ const char* vendorId;
+ static
+ const int vendorIdSize = 3; // Country & provider code (T.35)
+ const char* vendorName;
+ bool inverseStationIdOrder;
+ int modelIdPos;
+ int modelIdSize;
+ const ModelData* knownModels;
+};
+
+static const ModelData Panasonic0E[] =
+{{"\x00\x00\x00\x96", "KX-F90" },
+ {"\x00\x00\x00\x16", "KX-F780" },
+ {NULL}};
+
+static const ModelData Panasonic79[] =
+{{"\x00\x00\x00\x02", "UF-S10" },
+ {NULL}};
+
+static const ModelData Samsung8C[] =
+{{"\x00\x00\x01\x00", "SF-2010" },
+ {NULL}};
+
+static const NSFData KnownNSF[] =
+{
+ {"\x00\x00\x09", NULL, false },
+ {"\x00\x00\x0E", "Panasonic", false, 3, 4, Panasonic0E },
+ {"\x00\x00\x11", "Canon", false },
+ {"\x00\x00\x19", "Xerox", true },
+ {"\x00\x00\x21", "Lanier?", true },
+ {"\x00\x00\x25", "Ricoh", true },
+ {"\x00\x00\x26", NULL, false },
+ {"\x00\x00\x45", "Muratec", false },
+ {"\x00\x00\x51", "Sanyo", false },
+ {"\x00\x00\x66", "UTAX", true },
+ {"\x00\x00\x79", "Panasonic", false, 3, 4, Panasonic79 },
+ {"\x20\x41\x59", "Siemens", false },
+ {"\x59\x59\x01", NULL, false },
+ {"\x86\x00\x8C", "Samsung", false, 3, 4, Samsung8C },
+ {"\x86\x00\x98", "Samsung", false },
+ {"\xAD\x00\x36", "HP", false },
+ {"\xB5\x00\x2E", "Delrina", false },
+ {NULL}
+};
+
+NSF::NSF()
+{
+ clear();
+}
+
+
+NSF::NSF( const char* hexNSF )
+{
+ clear();
+ loadHexData( hexNSF );
+ decode();
+}
+
+NSF::NSF( const u_char* rawData, int size, const u_char* revTab )
+{
+ clear();
+ loadRawData( rawData, size, revTab );
+ decode();
+}
+
+void NSF::clear()
+{
+ nsf.resize(0);
+ hexNsf.resize(0);
+ vendor = "unknown";
+ model = "";
+ stationId = "";
+ vendorDecoded = false;
+ stationIdDecoded = false;
+}
+
+void NSF::loadHexData( const char* hexNSF )
+{
+ hexNsf.append( hexNSF );
+ const char *p = hexNSF;
+ char *pNext = NULL;
+ for( ;; ){
+ int val = strtol( p, &pNext, 16 );
+ if( pNext == p )
+ break;
+ p = pNext;
+ nsf.append( (unsigned char)val );
+ }
+}
+
+void NSF::loadRawData( const u_char* rawData, int size, const u_char* revTab )
+{
+ nsf.append( (const char*)rawData, size );
+ u_char *p = (u_char*)(const char*)nsf;
+ u_char *end = p+size;
+ for( ; p < end; p++ ){
+ *p = revTab[*p];
+ char buf[10];
+ sprintf( buf, "%02X ", *p );
+ hexNsf.append(buf);
+ }
+ // remove trailing space
+ hexNsf.resize( hexNsf.length() - 1 );
+}
+
+void NSF::decode()
+{
+ for( const NSFData* p = KnownNSF; p->vendorId; p++ ){
+ if( !memcmp( p->vendorId, &nsf[0], p->vendorIdSize ) ){
+ vendor = p->vendorName;
+ if( p->knownModels ){
+ for( const ModelData* pp = p->knownModels; pp->modelId; pp++ )
+ if( !memcmp( pp->modelId, &nsf[p->modelIdPos], p->modelIdSize ) )
+ model = pp->modelName;
+ }
+ findStationId( p->inverseStationIdOrder );
+ vendorDecoded = true;
+ }
+ }
+ if( !vendorFound() )
+ findStationId( 0 );
+}
+
+void NSF::findStationId( bool reverseOrder )
+{
+ const char* id = NULL;
+ u_int idSize = 0;
+ const char* maxId = NULL;
+ u_int maxIdSize = 0;
+ /*
+ * Trying to find the longest printable ASCII sequence
+ */
+ for( const char *p = (const char*)nsf, *end = p + nsf.length(); p < end; p++ ){
+ if( isprint(*p) ){
+ if( !idSize++ )
+ id = p;
+ if( idSize > maxIdSize ){
+ maxId = id;
+ maxIdSize = idSize;
+ }
+ }
+ else {
+ id = NULL;
+ idSize = 0;
+ }
+ }
+
+ /*
+ * Minimum acceptable id length
+ */
+ const u_int MinIdSize = 4;
+ if( maxIdSize >= MinIdSize ){
+ stationId.resize(0);
+ const char* p;
+ int dir;
+ if( reverseOrder ){
+ p = maxId + maxIdSize - 1;
+ dir = -1;
+ }
+ else {
+ p = maxId;
+ dir = 1;
+ }
+ for( u_int i = 0; i < maxIdSize; i++ ){
+ stationId.append( *p );
+ p += dir;
+ }
+ stationIdDecoded = true;
+ }
+}
diff -uNr hylafax-cvs/faxd/NSF.h hylafax-work/faxd/NSF.h
--- hylafax-cvs/faxd/NSF.h Thu Jan 1 03:00:00 1970
+++ hylafax-work/faxd/NSF.h Thu Apr 13 19:42:12 2000
@@ -0,0 +1,66 @@
+/*
+ * This file does not exist in the original Hylafax distribution.
+ * Created by Dmitry Bely, April 2000
+ */
+/*
+ * Copyright (c) 1994-1996 Sam Leffler
+ * Copyright (c) 1994-1996 Silicon Graphics, Inc.
+ * HylaFAX is a trademark of Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef NSF_H
+#define NSF_H
+
+#include "Str.h"
+
+class NSF {
+ fxStr nsf;
+ fxStr hexNsf;
+ fxStr vendor;
+ fxStr model;
+ fxStr stationId;
+ bool vendorDecoded;
+ bool stationIdDecoded;
+public:
+ NSF();
+ NSF( const char* hexNSF );
+ NSF( const u_char* rawData, int size, const u_char* revTab );
+ /*
+ * We are happy with default copy constructor and copy assignment,
+ * so do not explicitly define them (but will use)
+ */
+ const fxStr& getRawNsf(){ return nsf; }
+ const fxStr& getHexNsf(){ return hexNsf; }
+ bool vendorFound(){ return vendorDecoded; }
+ bool stationIdFound(){ return stationIdDecoded; }
+ const char* getVendor(){ return (const char*)vendor; }
+ const char* getModel(){ return (const char*)model; }
+ const char* getStationId(){ return (const char*)stationId; }
+private:
+ void clear();
+ void loadHexData( const char* hexNSF );
+ void loadRawData( const u_char* rawData, int size, const u_char* revTab );
+ void findStationId( bool reverseOrder );
+ void decode();
+};
+
+#endif /* NSF_H */
diff -uNr hylafax-cvs/faxd/t4test.c++ hylafax-work/faxd/t4test.c++
--- hylafax-cvs/faxd/t4test.c++ Thu Jan 1 03:00:00 1970
+++ hylafax-work/faxd/t4test.c++ Thu Apr 13 16:24:30 2000
@@ -0,0 +1,405 @@
+/*
+ * This file does not exist in the original Hylafax distribution.
+ * Created by Dmitry Bely, March 2000
+ */
+/*
+ * Copyright (c) 1994-1996 Sam Leffler
+ * Copyright (c) 1994-1996 Silicon Graphics, Inc.
+ * HylaFAX is a trademark of Silicon Graphics
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that (i) the above copyright notices and this permission notice appear in
+ * all copies of the software and related documentation, and (ii) the names of
+ * Sam Leffler and Silicon Graphics may not be used in any advertising or
+ * publicity relating to the software without the specific, prior written
+ * permission of Sam Leffler and Silicon Graphics.
+ *
+ * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
+ * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
+ * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
+ * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+/*
+ * Program for testing T.4 data correction routines
+ *
+ * Usage: t4test [-o output.tif] input.tif
+ */
+#include "Sys.h"
+
+#include "G3Decoder.h"
+#include "G3Encoder.h"
+#include "StackBuffer.h"
+#include "tiffio.h"
+#include "Class2Params.h"
+#if HAS_LOCALE
+extern "C" {
+#include <locale.h>
+}
+#endif
+
+class MemoryDecoder : public G3Decoder {
+private:
+ u_char* bp;
+ u_int width;
+ u_int byteWidth;
+ u_long cc;
+
+ u_int fillorder;
+ bool is2D;
+
+ u_char* endOfData; // used by cutExtraRTC
+
+ uint16* runs;
+ u_char* rowBuf;
+
+ int decodeNextByte();
+
+ void invalidCode(const char* type, int x);
+ void badPixelCount(const char* type, int got, int expected);
+ void badDecodingState(const char* type, int x);
+public:
+ MemoryDecoder(u_char* data, u_int wid, u_long n,
+ u_int fillorder, bool twoDim);
+ ~MemoryDecoder();
+ u_char* current() { return bp; }
+ void fixFirstEOL();
+ u_char* cutExtraRTC();
+};
+
+MemoryDecoder::MemoryDecoder(u_char* data, u_int wid, u_long n,
+ u_int order, bool twoDim)
+{
+ bp = data;
+ width = wid;
+ byteWidth = howmany(width, 8);
+ cc = n;
+
+ fillorder = order;
+ is2D = twoDim;
+
+ runs = new uint16[2*width]; // run arrays for cur+ref rows
+ rowBuf = new u_char[byteWidth];
+ setupDecoder(fillorder, is2D);
+ setRuns(runs, runs+width, width);
+}
+MemoryDecoder::~MemoryDecoder()
+{
+ delete rowBuf;
+ delete runs;
+}
+
+int
+MemoryDecoder::decodeNextByte()
+{
+ if (cc == 0)
+ raiseRTC(); // XXX don't need to recognize EOF
+ cc--;
+ return (*bp++);
+}
+void
+MemoryDecoder::invalidCode(const char* type, int x)
+{
+ printf("Invalid %s code word, x %d\n", type, x);
+}
+void
+MemoryDecoder::badPixelCount(const char* type, int got, int expected)
+{
+ printf("Bad %s pixel count, got %d, expected %d\n",
+ type, got, expected);
+}
+void
+MemoryDecoder::badDecodingState(const char* type, int x)
+{
+ printf("Panic, bad %s decoding state, x %d\n", type, x);
+}
+
+#ifdef roundup
+#undef roundup
+#endif
+#define roundup(a,b) ((((a)+((b)-1))/(b))*(b))
+
+/*
+ * TIFF Class F specs say:
+ *
+ * "As illustrated in FIGURE 1/T.4 in Recommendation T.4 (the Red
+ * Book, page 20), facsimile documents begin with an EOL (which in
+ * Class F is byte-aligned)..."
+ *
+ * This is wrong! "Byte-aligned" first EOL means extra zero bits
+ * which are not allowed by T.4. Reencode first row to fix this
+ * "byte-alignment".
+ */
+void MemoryDecoder::fixFirstEOL()
+{
+ printf( "fixFirstEOL()\n" );
+ fxStackBuffer result;
+ G3Encoder enc(result);
+ enc.setupEncoder(fillorder, is2D);
+
+ memset(rowBuf, 0, byteWidth*sizeof(u_char)); // clear row to white
+ if(!RTCraised()) {
+ u_char* start = current();
+ (void)decodeRow(rowBuf, width);
+ /*
+ * syncronize to the next EOL and calculate pointer to it
+ * (see detailed explanation of look_ahead in TagLine.c++)
+ */
+ (void)isNextRow1D();
+ u_int look_ahead = roundup(getPendingBits(),8) / 8;
+ u_int decoded = current() - look_ahead - start;
+
+ enc.encode(rowBuf, width, 1);
+ u_int encoded = result.getLength();
+
+ while( encoded < decoded ){
+ result.put((char) 0);
+ encoded++;
+ }
+ if( encoded == decoded ){
+ memcpy(start, (const char*)result, encoded);
+ printf( "Ok\n" );
+ }
+ }
+}
+
+
+/*
+ * TIFF Class F specs say:
+ *
+ * "Aside from EOL's, TIFF Class F files contain only image data. This
+ * means that the Return To Control sequence (RTC) is specifically
+ * prohibited..."
+ *
+ * Nethertheless Ghostscript and possibly other TIFF Class F writers
+ * append RTC or single EOL to the last encoded line. Remove them.
+ */
+u_char* MemoryDecoder::cutExtraRTC()
+{
+ printf( "cutExtraRTC()\n" );
+ printf("initial size = %ul\n", cc );
+ u_char* start = current();
+
+ /*
+ * We expect RTC near the end of data and thus
+ * do not check all image to save processing time.
+ * It's safe because we will resync on the first
+ * encountered EOL.
+ *
+ * NB: We expect G3Decoder::data==0 and
+ * G3Decoder::bit==0 (no data in the accumulator).
+ * As we cannot explicitly clear the accumulator
+ * (bit and data are private), cutExtraRTC()
+ * should be called immediately after
+ * MemoryDecoder() constructing.
+ */
+ const u_long CheckArea = 20;
+ if( cc > CheckArea ){
+ bp += (cc-CheckArea);
+ cc = CheckArea;
+ }
+
+ endOfData = NULL;
+ if(!RTCraised()) {
+ /*
+ * syncronize to the next EOL and calculate pointer to it
+ * (see detailed explanation of look_ahead in TagLine.c++)
+ */
+ (void)isNextRow1D();
+ u_int look_ahead = roundup(getPendingBits(),8) / 8;
+ endOfData = current() - look_ahead;
+ printf( "endOfData = %p\n", endOfData );
+ for (;;) {
+ if( decodeRow(NULL, width) ){
+ /*
+ * endOfData is now after last good row. Thus we correctly handle
+ * RTC, single EOL in the end, or no RTC/EOL at all
+ */
+ endOfData = current();
+ printf( "endOfData = %p\n", endOfData );
+ }
+ }
+ }
+
+ printf("endOfData = %p, size after RTC cut = %ul\n", endOfData, endOfData-start );
+ return endOfData;
+}
+
+void
+correctPhaseCData(u_char* buf, u_long* pBufSize,
+ u_int fillorder, const Class2Params& params)
+{
+ MemoryDecoder dec1(buf, params.pageWidth(), *pBufSize, fillorder, params.is2D());
+ dec1.fixFirstEOL();
+ /*
+ * We have to construct new decoder. See comments to cutExtraRTC().
+ */
+ MemoryDecoder dec2(buf, params.pageWidth(), *pBufSize, fillorder, params.is2D());
+ u_char* endOfData = dec2.cutExtraRTC();
+ if( endOfData )
+ *pBufSize = endOfData - buf;
+}
+
+void
+vlogError(const char* fmt, va_list ap)
+{
+ vfprintf(stderr, fmt, ap);
+ fputs(".\n", stderr);
+}
+
+// NB: must duplicate this to avoid pulling faxApp&co.
+
+extern "C" void
+_fxassert(const char* msg, const char* file, int line)
+{
+ fprintf(stderr, "Assertion failed \"%s\", file \"%s\" line %d.\n",
+ msg, file, line);
+ abort();
+ /*NOTREACHED*/
+}
+
+const char* appName;
+
+void
+usage()
+{
+ fprintf(stderr,
+ "usage: %s [-m format] [-o t.tif] [-f font.pcf] input.tif\n",
+ appName);
+ exit(-1);
+}
+
+void
+fatal(const char* fmt ...)
+{
+ fprintf(stderr, "%s: ", appName);
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputs(".\n", stderr);
+ exit(-1);
+}
+
+int
+main(int argc, char* argv[])
+{
+ extern int optind, opterr;
+ extern char* optarg;
+ int c;
+ const char* output = "t.tif";
+
+#ifdef LC_CTYPE
+ setlocale(LC_CTYPE, ""); // for <ctype.h> calls
+#endif
+#ifdef LC_TIME
+ setlocale(LC_TIME, ""); // for strftime calls
+#endif
+ appName = argv[0];
+ while ((c = getopt(argc, argv, "o:")) != -1)
+ switch (c) {
+ case 'o':
+ output = optarg;
+ break;
+ case '?':
+ usage();
+ /*NOTREACHED*/
+ }
+ if (argc - optind != 1)
+ usage();
+ TIFF* tif = TIFFOpen(argv[optind], "r");
+ if (!tif)
+ fatal("%s: Cannot open, or not a TIFF file", argv[optind]);
+ uint16 comp;
+ TIFFGetField(tif, TIFFTAG_COMPRESSION, &comp);
+ if (comp != COMPRESSION_CCITTFAX3)
+ fatal("%s: Not a Group 3-encoded TIFF file", argv[optind]);
+
+ TIFF* otif = TIFFOpen(output, "w");
+ if (!otif)
+ fatal("%s: Cannot create output file", output);
+ TIFFSetDirectory(tif, 0);
+
+ Class2Params params;
+ params.vr = VR_NORMAL;
+ params.wd = WD_1728;
+ params.ln = LN_INF;
+ params.df = DF_1DMR;
+
+ do {
+ TIFFSetField(otif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
+ TIFFSetField(otif, TIFFTAG_IMAGEWIDTH, 1728);
+ uint32 l;
+ TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &l);
+ params.vr = (l < 1500 ? VR_NORMAL : VR_FINE);
+ TIFFSetField(otif, TIFFTAG_XRESOLUTION, 204.);
+ TIFFSetField(otif, TIFFTAG_YRESOLUTION, (float) params.verticalRes());
+ TIFFSetField(otif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+ TIFFSetField(otif, TIFFTAG_IMAGELENGTH, l);
+ TIFFSetField(otif, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField(otif, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(otif, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+ TIFFSetField(otif, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3);
+ TIFFSetField(otif, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB);
+ uint32 r;
+ TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &r);
+ /*
+ * output all data in a single strip
+ */
+ TIFFSetField(otif, TIFFTAG_ROWSPERSTRIP, uint32(-1));
+ TIFFSetField(otif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(otif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
+ uint32 opts = 0;
+ TIFFGetField(tif, TIFFTAG_GROUP3OPTIONS, &opts);
+ params.df = (opts & GROUP3OPT_2DENCODING) ? DF_2DMR : DF_1DMR;
+ TIFFSetField(otif, TIFFTAG_GROUP3OPTIONS, opts);
+ uint16 o;
+ if (TIFFGetField(otif, TIFFTAG_ORIENTATION, &o))
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, o);
+
+ uint16 fillorder;
+ TIFFGetFieldDefaulted(tif, TIFFTAG_FILLORDER, &fillorder);
+ /*
+ * Calculate total amount of space needed to read
+ * the image into memory (in its encoded format).
+ */
+ uint32* stripbytecount;
+ (void) TIFFGetField(tif, TIFFTAG_STRIPBYTECOUNTS, &stripbytecount);
+ tstrip_t nstrips = TIFFNumberOfStrips(tif);
+ tstrip_t strip;
+ uint32 totdata = 0;
+ for (strip = 0; strip < nstrips; strip++)
+ totdata += stripbytecount[strip];
+ /*
+ * Read the image into memory.
+ */
+ u_char* data = new u_char[totdata];
+ u_int off = 0; // skip tag line slop area
+ for (strip = 0; strip < nstrips; strip++) {
+ uint32 sbc = stripbytecount[strip];
+ if (sbc > 0 && TIFFReadRawStrip(tif, strip, data+off, sbc) >= 0)
+ off += (u_int) sbc;
+ }
+
+ correctPhaseCData( data, &totdata, fillorder, params );
+
+ /*
+ * write result as a single strip to
+ * the output file
+ */
+ if (fillorder != FILLORDER_LSB2MSB)
+ TIFFReverseBits(data, totdata);
+ if (TIFFWriteRawStrip(otif, 0, data, totdata) == -1)
+ fatal("%s: Write error at strip %u, writing %lu bytes",
+ output, strip, (u_long) totdata);
+ delete data;
+ } while (TIFFReadDirectory(tif) && TIFFWriteDirectory(otif));
+ TIFFClose(otif);
+ return (0);
+}
diff -uNr hylafax-cvs/man/config.4f hylafax-work/man/config.4f
--- hylafax-cvs/man/config.4f Wed Aug 18 13:35:11 1999
+++ hylafax-work/man/config.4f Thu Apr 13 19:05:14 2000
@@ -174,6 +174,7 @@
RingFax string \- distinctive ring fax call identifier
RingsBeforeAnswer integer \s-10\s+1 rings to wait before answering phone
RingVoice string \- distinctive ring voice call identifier
+RTNHandlingMethod string \s-1Retransmit\s+1 RTN signal handling method
SendFaxCmd\(dg string \s-1bin/faxsend\s+1 fax transmit command script
SendPageCmd\(dg string \s-1bin/pagesend\s+1 pager transmit command script
SendUUCPCmd\(dg string \s-1bin/uucpsend\s+1 \s-1UUCP\s+1 transmit command script
@@ -207,7 +208,7 @@
ModemDialCmd string \s-1ATDT%s\s+1 command for dialing (%s for number to dial)
ModemDialResponseTimeout integer \s-1180000\s+1 dialing command timeout (ms)
ModemEchoOffCmd string \s-1ATE0\s+1 command for disabling command echo
-ModemFlowControl string \s-1None\s+1 \s-1DTE-DCE\s+1 flow control scheme
+ModemFlowControl string \s-1XONXOFF\s+1 \s-1DTE-DCE\s+1 flow control scheme
ModemFrameFillOrder string \s-1LSB2MSB\s+1 bit order for \s-1HDLC\s+1 frames
ModemHardFlowCmd string \- command for setting hardware flow control between \s-1DTE\s+1 and \s-1DCE\s+1
ModemMinSpeed string \s-12400\s+1 minimum acceptable transmit speed
@@ -220,7 +221,7 @@
ModemPageStartTimeout integer \s-1180000\s+1 page send/receive timeout (ms)
ModemRate integer \s-119200\s+1 baud rate to use for \s-1DCE-DTE\s+1 communication
ModemRecvFillOrder string \s-1LSB2MSB\s+1 bit order for received facsimile data
-ModemResetCmds string \- additional commands when resetting modem
+ModemResetCmds string \s-1ATZ\s+1 additional commands when resetting modem
ModemResetDelay integer \s-12600\s+1 delay (ms) after sending modem reset commands
ModemResultCodesCmd string \s-1ATQ0\s+1 command for enabling result codes
ModemRevQueryCmd string \s-1\fIsee below\fP\s+1 command for querying modem firmware revision
@@ -304,6 +305,7 @@
Class2PTSCmd string \s-1AT+FPS\s+1 Class 2.0: command to set received page status
Class2RecvDataTrigger string \s-1``\e22''\s+1 Class 2.0: character to send to trigger recv
Class2RELCmd string \- Class 2.0: command to enable byte-aligned \s-1EOL\s+1 codes
+Class2SendRTC boolean \s-1No\s+1 Class 2.0: append \s-1RTC\s+1 to page data on transmit
Class2SFLOCmd string \s-1AT+FLO=1\s+1 Class 2.0: command to set software flow control
Class2SPLCmd string \s-1AT+FSP\s+1 Class 2.0: command to set polling request
Class2TBCCmd string \s-1AT+FPP=0\s+1 Class 2.0: command to enable stream mode
@@ -975,6 +977,29 @@
and
.BR RingFax .
.TP
+.B RTNHandlingMethod
+Specifies how to react to RTN signal, received from the remote;
+one of ``\s-1Retransmit\s+1'', ``\s-1Giveup\s+1'' and
+``\s-1Ignore\s+1''. ``\s-1Retransmit\s+1'' assumes that the
+page is not sent succesfully if RTN signal has been received.
+Hylafax will made up to 2 additional attempts to send the page,
+decreasing signalling rate and retraining. If RTN is still there,
+it will place up to 2 additional calls. So if the remote always respond with
+RTN, the page will be send 9 times. Although this algorithm comply with
+T.30 specs and was originally implemented by Sam Leffler as the only
+possible choice, real fax machines behave completely different. There is a
+non-written rule among fax developers, that RTN means ``over and out'' -- hang
+up immediately and never try to send the same page to the same destination
+again. That is because RTN usually indicates problems with flow control,
+incorrectly encoded T.4 data, incompatibility between local and remote
+equipment etc., but very rarely is caused by the real noise on the line.
+This ``over and out'' behaviour can be activated by ``Giveup'' value.
+There is also third option, not so radical as ``Giveup''. Yes, we will never
+retransmit the page, but we can try to send the next page, and let the
+remote to decide what to do (accept our decision or hang up). Thus one page will
+(or will not) be missed but we have a chance to successfully send all other pages.
+This behaviour can be activated by ``Ignore'' value.
+.TP
.B SendFaxCmd\(dg
The command to use to process outbound facsimile jobs; see
.IR faxsend (1M).
@@ -1511,6 +1536,11 @@
communication.
However, beware that many modems only support software
flow control when sending or receiving facsimile.
+.IP
+Note that modems usually support software flow control even
+if they have no explicit AT-command to activate it; in this case
+it is switched on when the modem enters fax mode, having
+AT+FCLASS=... from \s-1DTE\s+1.
.TP
.B ModemFrameFillOrder
The bit order to expect for received
@@ -1630,9 +1660,7 @@
.SM DTR
handling, etc. should be specified through the appropriate
configuration parameters and not through this parameter.
-In addition the soft reset command (usually ``\s-1ATZ\s+1'')
-should not be included in this string; the servers issue this
-command explicitly.
+Recommended default value is ``\s-1ATZ\s+1''.
.TP
.B ModemResetDelay
The time, in milliseconds, to pause after resetting a modem
@@ -1730,7 +1758,7 @@
.SM DCE.
.TP
.B ModemSoftResetCmd
-The command to force a soft reset of the modem.
+The command to force a soft reset of the modem (currenty is not used).
.TP
.B ModemType
This parameter must be set to one of: ``Class2'', ``Class2.0'',
@@ -2091,7 +2119,8 @@
.B Class2SendRTC
Whether or not to append an explicit ``Return To Control'' (\s-1RTC\s+1)
signal to the page data when transmitting.
-The Class 2 spec (i.e. SP-2388-A) states the modem will append
+The Class 2 and Class 2.0 specs (i.e. SP-2388-A and TIA/EIA-592) state
+that the modem will append
.SM RTC
when it receives the post-page message command from the host; this
parameter is provided in case the modem does not correctly implement