Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1912 lines
67 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. class20.c
  5. Abstract:
  6. This is the main source for Class2.0 specific functions for fax-modem T.30 driver
  7. Author:
  8. Source base was originated by Win95 At Work Fax package.
  9. RafaelL - July 1997 - port to NT
  10. Revision History:
  11. --*/
  12. #define USE_DEBUG_CONTEXT DEBUG_CONTEXT_T30_CLASS2
  13. #include "prep.h"
  14. #include "efaxcb.h"
  15. #include "tiff.h"
  16. #include "glbproto.h"
  17. #include "t30gl.h"
  18. #include "cl2spec.h"
  19. #include "psslog.h"
  20. #define FILE_ID FILE_ID_CLASS20
  21. #include "pssframe.h"
  22. #define STRSAFE_NO_DEPRECATE
  23. #include <strsafe.h>
  24. extern WORD CodeToBPS[16];
  25. extern UWORD rguwClass2Speeds[];
  26. extern DWORD PageWidthInPixelsFromDCS[];
  27. BYTE bClass20DLE_nextpage[3] = { DLE, 0x2c, 0 };
  28. BYTE bClass20DLE_enddoc[3] = { DLE, 0x2e, 0 };
  29. BYTE bMRClass20RTC[10] = { 0x01, 0x30, 0x00, 0x06, 0xc0, 0x00, 0x18, 0x00, 0x03, 0x00};
  30. BYTE bMHClass20RTC[9] = { 0x00, 0x08, 0x80, 0x00, 0x08, 0x80, 0x00, 0x08, 0x00};
  31. void
  32. Class20Init(
  33. PThrdGlbl pTG
  34. )
  35. {
  36. pTG->lpCmdTab = 0;
  37. pTG->Class2bDLEETX[0] = DLE;
  38. pTG->Class2bDLEETX[1] = ETX;
  39. pTG->Class2bDLEETX[2] = 0;
  40. sprintf( pTG->cbszFDT, "AT+FDT\r" );
  41. sprintf( pTG->cbszFDR, "AT+FDR\r" );
  42. sprintf( pTG->cbszFPTS, "AT+FPS=%%d\r" );
  43. sprintf( pTG->cbszFCR, "AT+FCR=1\r" );
  44. sprintf( pTG->cbszFNR, "AT+FNR=1,1,1,1\r" );
  45. sprintf( pTG->cbszFCQ, "AT+FCQ=0,0\r" );
  46. sprintf( pTG->cbszFBUG, "AT+FBUG=0\r" );
  47. sprintf( pTG->cbszSET_FBOR, "AT+FBO=%%d\r" );
  48. // DCC - set High Res, Huffman, no ECM/BFT, default all others.
  49. sprintf( pTG->cbszFDCC_ALL, "AT+FCC=%%d,%%d,,,0,0,0,0\r" );
  50. sprintf( pTG->cbszFDCC_RECV_ALL, "AT+FCC=1,%%d,,,0,0,0,0\r" );
  51. sprintf( pTG->cbszFDIS_RECV_ALL, "AT+FDIS=1,%%d,2,2,0,0,0,\r" );
  52. sprintf( pTG->cbszFDCC_RES, "AT+FDCC=1\r" );
  53. sprintf( pTG->cbszFDCC_BAUD, "AT+FDCC=1,%%d\r" );
  54. sprintf( pTG->cbszFDIS_BAUD, "AT+FDIS=1,%%d\r" );
  55. sprintf( pTG->cbszFDIS_IS, "AT+FIS?\r" );
  56. sprintf( pTG->cbszFDIS_NOQ_IS, "AT+FDIS\r" );
  57. sprintf( pTG->cbszFDCC_IS, "AT+FCC?\r" );
  58. sprintf( pTG->cbszFDIS_STRING, "+FIS" );
  59. sprintf( pTG->cbszFDIS, "AT+FIS=%%1d,%%1d,%%1d,%%1d,%%1d,0,0,0\r" );
  60. sprintf( pTG->cbszONE, "1" );
  61. sprintf( pTG->cbszCLASS2_FMFR, "AT+FMI?\r" );
  62. sprintf( pTG->cbszCLASS2_FMDL, "AT+FMM?\r" );
  63. sprintf( pTG->cbszFDT_CONNECT, "CONNECT" );
  64. sprintf( pTG->cbszFCON, "+FCO" );
  65. sprintf( pTG->cbszFLID, "AT+FLI=\"%%s\"\r" );
  66. sprintf( pTG->cbszENDPAGE, "AT+FET=0\r" );
  67. sprintf( pTG->cbszENDMESSAGE, "AT+FET=2\r" );
  68. sprintf( pTG->cbszCLASS2_ATTEN, "AT\r" );
  69. sprintf( pTG->cbszATA, "ATA\r" );
  70. sprintf( pTG->cbszCLASS2_HANGUP, "ATH0\r" );
  71. sprintf( pTG->cbszCLASS2_CALLDONE, "ATS0=0\r" );
  72. sprintf( pTG->cbszCLASS2_ABORT, "AT+FKS\r" );
  73. sprintf( pTG->cbszCLASS2_DIAL, "ATD%%c %%s\r" );
  74. sprintf( pTG->cbszCLASS2_NODIALTONE, "NO DIAL" );
  75. sprintf( pTG->cbszCLASS2_BUSY, "BUSY" );
  76. sprintf( pTG->cbszCLASS2_NOANSWER, "NO ANSWER" );
  77. sprintf( pTG->cbszCLASS2_OK, "OK" );
  78. sprintf( pTG->cbszCLASS2_FHNG, "+FHS" );
  79. sprintf( pTG->cbszCLASS2_ERROR, "ERROR" );
  80. sprintf( pTG->cbszCLASS2_NOCARRIER, "NO CARRIER" );
  81. Class2SetProtParams(pTG, &pTG->Inst.ProtParams);
  82. }
  83. /*++
  84. Routine Description:
  85. Issue "AT+FDIS=?" command, parse response into pTG->DISPcb
  86. Return Value:
  87. TRUE - success, FALSE - failure
  88. --*/
  89. BOOL Class20GetDefaultFDIS(PThrdGlbl pTG)
  90. {
  91. UWORD uwRet=0;
  92. BYTE bTempBuf[200+RESPONSE_BUF_SIZE];
  93. LPBYTE lpbyte;
  94. HRESULT hr;
  95. DEBUG_FUNCTION_NAME("Class20GetDefaultFDIS");
  96. // Find out what the default DIS is
  97. if(!(uwRet=Class2iModemDialog( pTG,
  98. pTG->cbszFDIS_IS,
  99. (UWORD)(strlen(pTG->cbszFDIS_IS)),
  100. LOCALCOMMAND_TIMEOUT,
  101. 0,
  102. TRUE,
  103. pTG->cbszCLASS2_OK,
  104. pTG->cbszCLASS2_ERROR,
  105. (C2PSTR) NULL)))
  106. {
  107. DebugPrintEx(DEBUG_WRN,"FDIS? failed");
  108. // ignore
  109. }
  110. // See if the reply was ERROR or timeout, if so try a different command
  111. if ( uwRet == 2)
  112. {
  113. if(!(uwRet=Class2iModemDialog( pTG,
  114. pTG->cbszFDIS_IS,
  115. (UWORD)(strlen(pTG->cbszFDIS_IS)),
  116. LOCALCOMMAND_TIMEOUT,
  117. 0,
  118. TRUE,
  119. pTG->cbszCLASS2_OK,
  120. (C2PSTR) NULL)))
  121. {
  122. // No FDIS, FDCC worked - quit!
  123. DebugPrintEx(DEBUG_ERR,"No FDIS? or FDCC? worked");
  124. return FALSE;
  125. }
  126. // If the first character in the reply before a number
  127. // is a ',', insert a '1' for normal & fine res (Exar hack)
  128. for (lpbyte = pTG->lpbResponseBuf2; *lpbyte != '\0'; lpbyte++)
  129. {
  130. if (*lpbyte == ',')
  131. {
  132. // found a leading comma
  133. hr = StringCchPrintf(bTempBuf, ARR_SIZE(bTempBuf), "%s%s", pTG->cbszONE, lpbyte);
  134. if (FAILED(hr))
  135. {
  136. DebugPrintEx(DEBUG_WRN,"StringCchPrintf failed (ec=0x%08X)",hr);
  137. }
  138. else
  139. {
  140. hr = StringCchCopy(lpbyte, ARR_SIZE(pTG->lpbResponseBuf2) - (lpbyte-pTG->lpbResponseBuf2), bTempBuf);
  141. if (FAILED(hr))
  142. {
  143. DebugPrintEx(DEBUG_WRN,"StringCchCopy failed (ec=0x%08X)",hr);
  144. }
  145. }
  146. DebugPrintEx(DEBUG_MSG,"Leading comma in DCC string =%s", (LPSTR)&pTG->lpbResponseBuf2);
  147. }
  148. if ( (*lpbyte >= '0') && (*lpbyte <= '9') )
  149. {
  150. break;
  151. }
  152. }
  153. }
  154. // If the repsonse was just a number string without "+FDIS" in front
  155. // of it, add the +FDIS. Some modem reply with it, some do not. The
  156. // general parsing algorithm used below in Class2ResponseAction needs
  157. // to know the command that the numbers refer to.
  158. if ( pTG->lpbResponseBuf2[0] != '\0' &&
  159. (strstr( (LPSTR)pTG->lpbResponseBuf2, (LPSTR)pTG->cbszFDIS_STRING)==NULL))
  160. {
  161. // did not get the FDIS in the response!
  162. hr = StringCchPrintf(bTempBuf, ARR_SIZE(bTempBuf), "%s: %s", pTG->cbszFDIS_STRING, pTG->lpbResponseBuf2);
  163. if (FAILED(hr))
  164. {
  165. DebugPrintEx(DEBUG_WRN,"StringCchPrintf failed (ec=0x%08X)",hr);
  166. }
  167. else
  168. {
  169. hr = StringCchCopy(pTG->lpbResponseBuf2, ARR_SIZE(pTG->lpbResponseBuf2), bTempBuf);
  170. if (FAILED(hr))
  171. {
  172. DebugPrintEx(DEBUG_WRN,"StringCchCopy failed (ec=0x%08X)",hr);
  173. }
  174. }
  175. }
  176. DebugPrintEx(DEBUG_MSG,"Received %s from FDIS?", (LPSTR)(&(pTG->lpbResponseBuf2)));
  177. // Process default DIS to see if we have to send a DCC to change
  178. // it. Some modems react badly to just sending a DCC with ",,,"
  179. // so we can't rely on the modem keeping DIS parameters unchanged
  180. // after a DCC like that. We'll use the FDISResponse routine to load
  181. // the default DIS values into a PCB structure
  182. if ( Class2ResponseAction(pTG, (LPPCB) &pTG->DISPcb) == FALSE )
  183. {
  184. DebugPrintEx(DEBUG_ERR,"Failed to process FDIS Response");
  185. return FALSE;
  186. }
  187. return TRUE;
  188. }
  189. BOOL T30Cl20Tx(PThrdGlbl pTG,LPSTR szPhone)
  190. {
  191. USHORT uRet1, uRet2;
  192. BYTE bBuf[200];
  193. UWORD Encoding, Res, PageWidth, PageLength, uwLen;
  194. BYTE bIDBuf[200+max(MAXTOTALIDLEN,20)+4];
  195. CHAR szTSI[max(MAXTOTALIDLEN,20)+4] = {0};
  196. BOOL fBaudChanged;
  197. BOOL RetCode;
  198. DEBUG_FUNCTION_NAME("T30Cl20Tx");
  199. uRet2 = 0;
  200. if(!(pTG->lpCmdTab = iModemGetCmdTabPtr(pTG)))
  201. {
  202. DebugPrintEx(DEBUG_ERR,"iModemGetCmdTabPtr failed.");
  203. uRet1 = T30_CALLFAIL;
  204. pTG->fFatalErrorWasSignaled = 1;
  205. SignalStatusChange(pTG, FS_FATAL_ERROR);
  206. RetCode = FALSE;
  207. goto done;
  208. }
  209. // first get SEND_CAPS if possible.
  210. if(!Class2GetBC(pTG, SEND_CAPS)) // get send caps
  211. {
  212. uRet1 = T30_CALLFAIL;
  213. pTG->fFatalErrorWasSignaled = 1;
  214. SignalStatusChange(pTG, FS_FATAL_ERROR);
  215. RetCode = FALSE;
  216. goto done;
  217. }
  218. // Go to Class2.0
  219. if(!iModemGoClass(pTG, 3))
  220. {
  221. DebugPrintEx(DEBUG_ERR,"Failed to Go to Class 2.0");
  222. uRet1 = T30_CALLFAIL;
  223. pTG->fFatalErrorWasSignaled = 1;
  224. SignalStatusChange(pTG, FS_FATAL_ERROR);
  225. RetCode = FALSE;
  226. goto done;
  227. }
  228. // Begin by checking for manufacturer and ATI code.
  229. // Look this up against the modem specific table we
  230. // have and set up the send strings needed for
  231. // this modem.
  232. if(!Class2GetModemMaker(pTG))
  233. {
  234. DebugPrintEx(DEBUG_WRN,"Call to GetModemMaker failed");
  235. // Ignore failure!!!
  236. }
  237. // set manufacturer specific strings
  238. Class2SetMFRSpecific(pTG);
  239. // Get the capabilities of the software. I am only using this
  240. // right now for the TSI field (below where I send +FLID).
  241. // Really, this should also be used instead of the hardcoded DIS
  242. // values below.
  243. // ALL COMMANDS LOOK FOR MULTILINE RESPONSES WHILE MODEM IS ONHOOK.
  244. // A "RING" COULD APPEAR AT ANY TIME!
  245. _fmemset((LPB)szTSI, 0, strlen(szTSI));
  246. Class2SetDIS_DCSParams( pTG,
  247. SEND_CAPS,
  248. (LPUWORD)&Encoding,
  249. (LPUWORD)&Res,
  250. (LPUWORD)&PageWidth,
  251. (LPUWORD)&PageLength,
  252. (LPSTR) szTSI,
  253. sizeof(szTSI)/sizeof(szTSI[0]));
  254. bIDBuf[0] = '\0';
  255. uwLen = (UWORD)wsprintf(bIDBuf, pTG->cbszFLID, (LPSTR)szTSI);
  256. if(!Class2iModemDialog( pTG,
  257. bIDBuf,
  258. uwLen,
  259. LOCALCOMMAND_TIMEOUT,
  260. 0,
  261. TRUE,
  262. pTG->cbszCLASS2_OK,
  263. pTG->cbszCLASS2_ERROR,
  264. (C2PSTR) NULL))
  265. {
  266. DebugPrintEx(DEBUG_WRN,"Local ID failed");
  267. // ignore failure
  268. }
  269. if (!Class20GetDefaultFDIS(pTG))
  270. {
  271. DebugPrintEx(DEBUG_ERR, "Class20GetDefaultFDIS failed");
  272. uRet1 = T30_CALLFAIL;
  273. pTG->fFatalErrorWasSignaled = 1;
  274. SignalStatusChange(pTG, FS_FATAL_ERROR);
  275. RetCode = FALSE;
  276. goto done;
  277. }
  278. fBaudChanged = FALSE;
  279. // See if we have to change the baud rate to a lower value.
  280. // This only happens if the user set an ini string constraining
  281. // the high end speed or if the user turned off V.17 for sending
  282. // Check the V.17 inhibit and lower baud if necessary
  283. if ( (pTG->DISPcb.Baud > 3) && (!pTG->ProtParams2.fEnableV17Send) )
  284. {
  285. DebugPrintEx(DEBUG_MSG,"Lowering baud from %d for V.17 inihibit", CodeToBPS[pTG->DISPcb.Baud]);
  286. pTG->DISPcb.Baud = 3; //9600 won't use V.17
  287. fBaudChanged = TRUE;
  288. }
  289. // Now see if the high end baud rate has been constrained
  290. if ( (pTG->ProtParams2.HighestSendSpeed != 0) &&
  291. (CodeToBPS[pTG->DISPcb.Baud] > (WORD)pTG->ProtParams2.HighestSendSpeed))
  292. {
  293. DebugPrintEx( DEBUG_MSG,
  294. "Have to lower baud from %d to %d",
  295. CodeToBPS[pTG->DISPcb.Baud],
  296. pTG->ProtParams2.HighestSendSpeed);
  297. fBaudChanged = TRUE;
  298. switch (pTG->ProtParams2.HighestSendSpeed)
  299. {
  300. case 2400:
  301. pTG->DISPcb.Baud = 0;
  302. break;
  303. case 4800:
  304. pTG->DISPcb.Baud = 1;
  305. break;
  306. case 7200:
  307. pTG->DISPcb.Baud = 2;
  308. break;
  309. case 9600:
  310. pTG->DISPcb.Baud = 3;
  311. break;
  312. case 12000:
  313. pTG->DISPcb.Baud = 4;
  314. break;
  315. default:
  316. DebugPrintEx(DEBUG_ERR,"Bad HighestSpeed");
  317. uRet1 = T30_CALLFAIL;
  318. pTG->fFatalErrorWasSignaled = 1;
  319. SignalStatusChange(pTG, FS_FATAL_ERROR);
  320. RetCode = FALSE;
  321. goto done;
  322. break;
  323. }
  324. }
  325. uwLen=(UWORD)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_ALL, Res, pTG->DISPcb.Baud);
  326. if(!Class2iModemDialog( pTG,
  327. bBuf,
  328. uwLen,
  329. LOCALCOMMAND_TIMEOUT,
  330. 0,
  331. TRUE,
  332. pTG->cbszCLASS2_OK,
  333. (C2PSTR) NULL))
  334. {
  335. uRet1 = T30_CALLFAIL;
  336. pTG->fFatalErrorWasSignaled = 1;
  337. SignalStatusChange(pTG, FS_FATAL_ERROR);
  338. RetCode = FALSE;
  339. goto done;
  340. }
  341. // Do BOR based on the value from the modem table set in
  342. // Class2SetMFRSpecific
  343. uwLen = (UWORD)wsprintf(bBuf, pTG->cbszSET_FBOR, pTG->CurrentMFRSpec.iSendBOR);
  344. if(!Class2iModemDialog( pTG,
  345. bBuf,
  346. uwLen,
  347. LOCALCOMMAND_TIMEOUT,
  348. 0,
  349. TRUE,
  350. pTG->cbszCLASS2_OK,
  351. pTG->cbszCLASS2_ERROR,
  352. (C2PSTR) NULL))
  353. {
  354. DebugPrintEx(DEBUG_WRN,"FBOR failed");
  355. // Ignore BOR failure!!!
  356. }
  357. if(!Class2iModemDialog( pTG,
  358. pTG->cbszFNR,
  359. (UWORD)(strlen(pTG->cbszFNR)),
  360. ANS_LOCALCOMMAND_TIMEOUT,
  361. 0,
  362. TRUE,
  363. pTG->cbszCLASS2_OK,
  364. pTG->cbszCLASS2_ERROR,
  365. (C2PSTR) NULL))
  366. {
  367. DebugPrintEx(DEBUG_WRN,"FNR failed");
  368. // ignore error
  369. }
  370. // Dial the number
  371. // have to call hangup on every path out of here
  372. // after Dial is called. If Dial fails, it calls Hangup
  373. // if it succeeds we have to call Hangup when we're done
  374. PSSLogEntry(PSS_MSG, 0, "Phase A - Call establishment");
  375. SignalStatusChange(pTG, FS_DIALING);
  376. PSSLogEntry(PSS_MSG, 1, "Dialing...");
  377. if((uRet2 = Class2Dial(pTG, szPhone)) != CONNECT_OK)
  378. {
  379. uRet1 = T30_DIALFAIL;
  380. if (! pTG->fFatalErrorWasSignaled)
  381. {
  382. pTG->fFatalErrorWasSignaled = 1;
  383. SignalStatusChange(pTG, FS_FATAL_ERROR);
  384. }
  385. RetCode = FALSE;
  386. goto done;
  387. }
  388. pTG->Inst.state = BEFORE_RECVCAPS;
  389. // we should be using the sender msg here but that says Training
  390. // at speed=xxxx etc which we don't know, so we just use the
  391. // Recvr message which just says "negotiating"
  392. // Send the data
  393. uRet1 = (USHORT)Class20Send(pTG );
  394. if ( uRet1 == T30_CALLDONE)
  395. {
  396. DebugPrintEx(DEBUG_MSG,"******* DONE WITH CALL, ALL OK");
  397. // have to call hangup on every path out of here
  398. // we have to call Hangup here
  399. Class2ModemHangup(pTG );
  400. SignalStatusChange(pTG, FS_COMPLETED);
  401. RetCode = TRUE;
  402. }
  403. else
  404. {
  405. DebugPrintEx(DEBUG_ERR,"******* DONE WITH CALL, **** FAILED *****");
  406. // Make sure Modem is in OK state
  407. FComOutFilterClose(pTG );
  408. FComXon(pTG, FALSE);
  409. // have to call hangup on every path out of here
  410. // Class2ModemABort calls Hangup
  411. Class2ModemAbort(pTG );
  412. Class2SignalFatalError(pTG);
  413. RetCode = FALSE;
  414. }
  415. uRet2 = 0;
  416. done:
  417. return RetCode;
  418. }
  419. BOOL Class20Send(PThrdGlbl pTG)
  420. {
  421. LPBUFFER lpbf;
  422. SWORD swRet;
  423. ULONG lTotalLen=0;
  424. PCB Pcb;
  425. USHORT uTimeout=30000;
  426. BOOL err_status, fAllPagesOK = TRUE;
  427. BC bc;
  428. UWORD Encoding, Res=0, PageWidth, PageLength, uwLen;
  429. BYTE bFDISBuf[200];
  430. CHAR szTSI[max(MAXTOTALIDLEN,20)+4];
  431. BYTE bNull = 0;
  432. DWORD TiffConvertThreadId;
  433. USHORT uNextSend;
  434. DEBUG_FUNCTION_NAME("Class20Send");
  435. /*
  436. * We have just dialed... Now we have to look for the FDIS response from
  437. * the modem. It will be followed by an OK - hunt for the OK.
  438. */
  439. PSSLogEntry(PSS_MSG, 0, "Phase B - Negotiation");
  440. if (Class2iModemDialog( pTG,
  441. NULL,
  442. 0,
  443. STARTSENDMODE_TIMEOUT,
  444. 0,
  445. TRUE,
  446. pTG->cbszCLASS2_OK,
  447. (C2PSTR) NULL) != 1)
  448. {
  449. PSSLogEntry(PSS_ERR, 1, "Failed to receive DIS - aborting");
  450. err_status = T30_CALLFAIL;
  451. return err_status;
  452. }
  453. if (pTG->fFoundFHNG)
  454. {
  455. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  456. err_status = T30_CALLFAIL;
  457. return err_status;
  458. }
  459. // The response will be in pTG->lpbResponseBuf2 - this is loaded in
  460. // Class2iModemDialog.
  461. // Parse through the received strings, looking for the DIS, CSI,
  462. // NSF
  463. if ( Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE )
  464. {
  465. DebugPrintEx(DEBUG_ERR,"Failed to process ATD Response");
  466. PSSLogEntry(PSS_ERR, 1, "Failed to parse received DIS - aborting");
  467. err_status = T30_CALLFAIL;
  468. return err_status;
  469. }
  470. PSSLogEntry(PSS_MSG, 1, "CSI is %s", Pcb.szID);
  471. PSSLogEntry(PSS_MSG, 1, "DIS specified the following capabilities:");
  472. LogClass2DISDetails(pTG, &Pcb);
  473. //Now that pcb is set up, call ICommReceiveCaps to tell icomfile
  474. Class2InitBC(pTG, (LPBC)&bc, sizeof(bc), RECV_CAPS);
  475. Class2PCBtoBC(pTG, (LPBC)&bc, sizeof(bc), &Pcb);
  476. // Class2 modems do their own negotiation & we need to stay in sync
  477. // Otherwise, we might send MR data while the modem sends a DCS
  478. // saying it is MH. This happens a lot with Exar modems because
  479. // they dont accept an FDIS= command during the call.
  480. // FIX: On all Class2 sends force remote caps to always be MH
  481. // Then in efaxrun we will always negotiate MH & encode MH
  482. // We are relying on the fact that (a) it seems that all/most
  483. // Class2 modems negotiate MH (b) Hopefully ALL Exar ones
  484. // negotiate MH and (c) We will override all non-Exar modem's
  485. // intrinsic negotiation by sending an AT+FDIS= just before the FDT
  486. // Also (d) This change makes our behaviour match Snowball exactly
  487. // so we will work no better or worse than it :-)
  488. bc.Fax.Encoding = MH_DATA;
  489. if( ICommRecvCaps(pTG, (LPBC)&bc) == FALSE )
  490. {
  491. DebugPrintEx(DEBUG_ERR,"Failed return from ICommRecvCaps.");
  492. err_status = T30_CALLFAIL;
  493. return err_status;
  494. }
  495. // now get the SEND_PARAMS
  496. if(!Class2GetBC(pTG, SEND_PARAMS)) // sleep until we get it
  497. {
  498. err_status = T30_CALLFAIL;
  499. return err_status;
  500. }
  501. // Turn off flow control.
  502. FComXon(pTG, FALSE);
  503. // The Send params were set during the call to Class2GetBC
  504. // We'll use these to set the ID (for the TSI) and the DCS params
  505. // Send the FDT and get back the DCS. The FDT must be followed by
  506. // CONNECT and a ^Q (XON)
  507. // The FDT string must have the correct resolution and encoding
  508. // for this session. FDT=Encoding, Res, width, length
  509. // Encoding 0=MH, 1=MR,2=uncompressed,3=MMR
  510. // Res 0=200x100 (normal), 1=200x200 (fine)
  511. // PageWidth 0=1728pixels/215mm,1=2048/255,2=2432/303,
  512. // 3=1216/151,4=864/107
  513. // PageLength 0=A4,1=B4,2=unlimited
  514. Class2SetDIS_DCSParams( pTG,
  515. SEND_PARAMS,
  516. (LPUWORD)&Encoding,
  517. (LPUWORD)&Res,
  518. (LPUWORD)&PageWidth,
  519. (LPUWORD)&PageLength,
  520. (LPSTR) szTSI,
  521. sizeof(szTSI)/sizeof(szTSI[0]));
  522. //
  523. // Current Win95 version of Class2 TX is limited to MH only.
  524. // While not changing this, we will at least allow MR selection in future.
  525. //
  526. if (!pTG->fTiffThreadCreated)
  527. {
  528. if (Encoding)
  529. {
  530. pTG->TiffConvertThreadParams.tiffCompression = TIFF_COMPRESSION_MR;
  531. }
  532. else
  533. {
  534. pTG->TiffConvertThreadParams.tiffCompression = TIFF_COMPRESSION_MH;
  535. }
  536. if (Res)
  537. {
  538. pTG->TiffConvertThreadParams.HiRes = 1;
  539. }
  540. else
  541. {
  542. pTG->TiffConvertThreadParams.HiRes = 0;
  543. // use LoRes TIFF file prepared by FaxSvc
  544. // pTG->lpwFileName[ wcslen(pTG->lpwFileName) - 1] = (unsigned short) ('$');
  545. }
  546. _fmemcpy (pTG->TiffConvertThreadParams.lpszLineID, pTG->lpszPermanentLineID, 8);
  547. pTG->TiffConvertThreadParams.lpszLineID[8] = 0;
  548. DebugPrintEx(DEBUG_MSG,"Creating TIFF helper thread");
  549. pTG->hThread = CreateThread( NULL,
  550. 0,
  551. (LPTHREAD_START_ROUTINE) TiffConvertThread,
  552. (LPVOID) pTG,
  553. 0,
  554. &TiffConvertThreadId);
  555. if (!pTG->hThread)
  556. {
  557. DebugPrintEx(DEBUG_ERR,"TiffConvertThread create FAILED");
  558. err_status = T30_CALLFAIL;
  559. return err_status;
  560. }
  561. pTG->fTiffThreadCreated = 1;
  562. pTG->AckTerminate = 0;
  563. pTG->fOkToResetAbortReqEvent = 0;
  564. if ( (pTG->RecoveryIndex >=0 ) && (pTG->RecoveryIndex < MAX_T30_CONNECT) )
  565. {
  566. T30Recovery[pTG->RecoveryIndex].TiffThreadId = TiffConvertThreadId;
  567. T30Recovery[pTG->RecoveryIndex].CkSum = ComputeCheckSum((LPDWORD) &T30Recovery[pTG->RecoveryIndex].fAvail,
  568. sizeof ( T30_RECOVERY_GLOB ) / sizeof (DWORD) - 1 );
  569. }
  570. }
  571. // Even modems that take FDT=x,x,x,x don't seem to really do it
  572. // right. So, for now, just send FDIS followed by FDT except for
  573. // the EXAR modems!!
  574. uwLen = (UWORD)wsprintf(bFDISBuf,
  575. pTG->cbszFDIS,
  576. Res,
  577. min(Pcb.Baud, pTG->DISPcb.Baud),
  578. PageWidth,
  579. PageLength,
  580. Encoding);
  581. if(!Class2iModemDialog( pTG,
  582. bFDISBuf,
  583. uwLen,
  584. LOCALCOMMAND_TIMEOUT,
  585. 0,
  586. TRUE,
  587. pTG->cbszCLASS2_OK,
  588. pTG->cbszCLASS2_ERROR,
  589. (C2PSTR) NULL))
  590. {
  591. DebugPrintEx(DEBUG_WRN,"Failed get response from FDIS");
  592. // Ignore it -we are going to send what we have!
  593. }
  594. if (Class2iModemDialog( pTG,
  595. pTG->cbszFDT,
  596. (UWORD)(strlen(pTG->cbszFDT)),
  597. STARTSENDMODE_TIMEOUT,
  598. 0,
  599. TRUE,
  600. pTG->cbszFDT_CONNECT,
  601. (C2PSTR) NULL) != 1)
  602. {
  603. DebugPrintEx(DEBUG_ERR,"FDT Received %s",(LPSTR)(&(pTG->lpbResponseBuf2)));
  604. DebugPrintEx(DEBUG_ERR,"FDT to start first PAGE Failed!");
  605. if (pTG->fFoundFHNG)
  606. {
  607. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  608. }
  609. err_status = T30_CALLFAIL;
  610. return err_status;
  611. }
  612. DebugPrintEx(DEBUG_MSG,"FDT Received %s",(LPSTR)(&(pTG->lpbResponseBuf2)));
  613. // Turn on flow control.
  614. FComXon(pTG, TRUE);
  615. // Search through Response for the DCS frame - need it so set
  616. // the correct zero stuffing
  617. if ( Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE )
  618. {
  619. DebugPrintEx(DEBUG_ERR,"Failed to process FDT Response");
  620. PSSLogEntry(PSS_ERR, 1, "Failed to parse sent DCS - aborting");
  621. err_status = T30_CALLFAIL;
  622. return err_status;
  623. }
  624. PSSLogEntry(PSS_MSG, 1, "TSI is \"%s\"", pTG->LocalID);
  625. PSSLogEntry(PSS_MSG, 1, "DCS was sent as follows:");
  626. LogClass2DCSDetails(pTG, &Pcb);
  627. // Got a response - see if baud rate is OK
  628. DebugPrintEx( DEBUG_MSG,
  629. "Negotiated Baud Rate = %d, lower limit is %d",
  630. Pcb.Baud,
  631. pTG->ProtParams2.LowestSendSpeed);
  632. if (CodeToBPS[Pcb.Baud] < (WORD)pTG->ProtParams2.LowestSendSpeed)
  633. {
  634. DebugPrintEx(DEBUG_MSG,"Aborting due to too low baud rate!");
  635. err_status = T30_CALLFAIL;
  636. return err_status;
  637. }
  638. // Use values obtained from the DCS frame to set zero stuffing.
  639. // (These were obtained by call to Class2ResponseAction above).
  640. // Zero stuffing is a function of minimum scan time (determined
  641. // by resolution and the returned scan minimum) and baud.
  642. // Fixed the Hack--added a Baud field
  643. // Init must be BEFORE SetStuffZero!
  644. FComOutFilterInit(pTG );
  645. FComSetStuffZERO(pTG, Class2MinScanToBytesPerLine(pTG, Pcb.MinScan, (BYTE) Pcb.Baud, Pcb.Resolution));
  646. err_status = T30_CALLDONE;
  647. while ((swRet=ICommGetSendBuf(pTG, &lpbf, SEND_STARTPAGE)) == 0)
  648. {
  649. PSSLogEntry(PSS_MSG, 0, "Phase C - Page Transmission");
  650. PSSLogEntry(PSS_MSG, 1, "Sending page %d data...", pTG->PageCount);
  651. lTotalLen = 0;
  652. FComOverlappedIO(pTG, TRUE); // TRUE
  653. while ((swRet=ICommGetSendBuf(pTG, &lpbf, SEND_SEQ)) == 0)
  654. {
  655. lTotalLen += lpbf->wLengthData;
  656. DebugPrintEx(DEBUG_MSG,"total length: %ld", lTotalLen);
  657. if(!(Class2ModemSendMem(pTG, lpbf->lpbBegData,lpbf->wLengthData) & (MyFreeBuf(pTG, lpbf))))
  658. {
  659. DebugPrintEx(DEBUG_ERR,"Class2ModemSendBuf Failed");
  660. PSSLogEntry(PSS_ERR, 1, "Failed to send page data - aborting");
  661. err_status = T30_CALLFAIL;
  662. FComOverlappedIO(pTG, FALSE);
  663. return err_status;
  664. }
  665. } // end of SEND_SEQ while
  666. PSSLogEntry(PSS_MSG, 2, "send: page %d data, %d bytes", pTG->PageCount, lTotalLen);
  667. if (swRet != SEND_EOF)
  668. {
  669. DebugPrintEx(DEBUG_ERR,"ICommGetSendBuf failed, swRet=%d", swRet);
  670. PSSLogEntry(PSS_MSG, 2, "send: <dle><etx>");
  671. FComDirectAsyncWrite(pTG, pTG->Class2bDLEETX, 2);
  672. PSSLogEntry(PSS_ERR, 1, "Failed to send page data - aborting");
  673. err_status = T30_CALLFAIL;
  674. return err_status;
  675. }
  676. PSSLogEntry(PSS_MSG, 2, "send: <RTC>");
  677. if (Encoding)
  678. {
  679. if (! FComDirectAsyncWrite(pTG, bMRClass20RTC, 10) )
  680. {
  681. DebugPrintEx(DEBUG_ERR,"Failed to terminate page with MR RTC");
  682. PSSLogEntry(PSS_ERR, 1, "Failed to send RTC - aborting");
  683. err_status = T30_CALLFAIL;
  684. return err_status;
  685. }
  686. }
  687. else
  688. {
  689. if (! FComDirectAsyncWrite(pTG, bMHClass20RTC, 9) )
  690. {
  691. DebugPrintEx(DEBUG_ERR,"Failed to terminate page with MH RTC");
  692. PSSLogEntry(PSS_ERR, 1, "Failed to send RTC - aborting");
  693. err_status = T30_CALLFAIL;
  694. return err_status;
  695. }
  696. }
  697. DebugPrintEx(DEBUG_MSG,"out of while send_seq loop.");
  698. // Acknowledge that we sent the page
  699. PSSLogEntry(PSS_MSG, 0, "Phase D - Post Message Exchange");
  700. //See if more pages to send...
  701. if ( (uNextSend = ICommNextSend(pTG)) == NEXTSEND_MPS )
  702. {
  703. // Terminate the Page with DLE-,
  704. DebugPrintEx(DEBUG_MSG,"Another page to send...");
  705. PSSLogEntry(PSS_MSG, 1, "Sending MPS");
  706. PSSLogEntry(PSS_MSG, 2, "send: <dle><mps>");
  707. // Terminate the Page with DLE-ETX
  708. if(!FComDirectAsyncWrite(pTG, bClass20DLE_nextpage, 2))
  709. {
  710. PSSLogEntry(PSS_ERR, 1, "Failed to send <dle><mps> - aborting");
  711. err_status = T30_CALLFAIL;
  712. return err_status;
  713. }
  714. }
  715. else
  716. {
  717. // Send end of message sequence
  718. // Terminate the document with DLE-0x2e
  719. PSSLogEntry(PSS_MSG, 1, "Sending EOP");
  720. PSSLogEntry(PSS_MSG, 2, "send: <dle><eop>");
  721. if(!FComDirectAsyncWrite(pTG, bClass20DLE_enddoc, 2))
  722. {
  723. PSSLogEntry(PSS_ERR, 1, "Failed to send <dle><eop> - aborting");
  724. err_status = T30_CALLFAIL;
  725. return err_status;
  726. }
  727. }
  728. // Flow control is turned off inside of ModemDrain.
  729. swRet = (SWORD)Class2ModemDrain(pTG);
  730. switch (swRet)
  731. {
  732. case 0:
  733. DebugPrintEx(DEBUG_ERR,"Failed to drain");
  734. err_status = T30_CALLFAIL;
  735. return err_status;
  736. case 1:
  737. PSSLogEntry(PSS_MSG, 1, "Received MCF");
  738. // We want ICommGetSendBuf(SEND_STARTPAGE) to give us the next page
  739. pTG->T30.ifrResp = ifrMCF;
  740. break;
  741. default:
  742. PSSLogEntry(PSS_MSG, 1, "Received RTN");
  743. // fAllPagesOK = FALSE; // This page was bad, but we retransmit it
  744. // We want ICommGetSendBuf(SEND_STARTPAGE) to give us the same page again
  745. pTG->T30.ifrResp = ifrRTN;
  746. }
  747. if ((uNextSend == NEXTSEND_MPS) || (pTG->T30.ifrResp == ifrRTN))
  748. {
  749. if (pTG->fFoundFHNG)
  750. {
  751. // This could've happened during Class2ModemDrain
  752. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  753. err_status = T30_CALLFAIL;
  754. return err_status;
  755. }
  756. // Now, Send the FDT to start the next page (this was done for
  757. // the first page before entering the multipage loop).
  758. if (Class2iModemDialog( pTG,
  759. pTG->cbszFDT,
  760. (UWORD) strlen(pTG->cbszFDT),
  761. STARTSENDMODE_TIMEOUT,
  762. 0,
  763. TRUE,
  764. pTG->cbszFDT_CONNECT,
  765. (C2PSTR) NULL) != 1)
  766. {
  767. DebugPrintEx(DEBUG_ERR,"FDT to start next PAGE Failed!");
  768. if (pTG->fFoundFHNG)
  769. {
  770. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  771. }
  772. err_status = T30_CALLFAIL;
  773. return err_status;
  774. }
  775. // Turn on flow control.
  776. FComXon(pTG, TRUE);
  777. } //if we do not have another page, do the else...
  778. else
  779. {
  780. if ((pTG->fFoundFHNG) && (pTG->dwFHNGReason!=0))
  781. {
  782. // This could've happened during Class2ModemDrain
  783. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  784. err_status = T30_CALLFAIL;
  785. return err_status;
  786. }
  787. break; // All done sending pages...
  788. }
  789. if ( err_status == T30_CALLFAIL)
  790. {
  791. break;
  792. }
  793. } //End of multipage while
  794. FComOutFilterClose(pTG );
  795. FComXon(pTG, FALSE);
  796. // If *any* page failed to send correctly, the call failed!
  797. if (!fAllPagesOK)
  798. {
  799. err_status = T30_CALLFAIL;
  800. }
  801. return err_status;
  802. }
  803. /**************************************************************
  804. Receive specific routines start here
  805. ***************************************************************/
  806. BOOL T30Cl20Rx (PThrdGlbl pTG)
  807. {
  808. USHORT uRet1, uRet2;
  809. BYTE bBuf[200];
  810. UWORD uwLen;
  811. UWORD Encoding, Res, PageWidth, PageLength;
  812. BYTE bIDBuf[200+max(MAXTOTALIDLEN,20)+4];
  813. CHAR szCSI[max(MAXTOTALIDLEN,20)+4];
  814. BOOL fBaudChanged;
  815. BOOL RetCode;
  816. DEBUG_FUNCTION_NAME("T30Cl20Rx");
  817. uRet2 = 0;
  818. if(!(pTG->lpCmdTab = iModemGetCmdTabPtr(pTG )))
  819. {
  820. DebugPrintEx(DEBUG_ERR,"iModemGetCmdTabPtr failed.");
  821. uRet1 = T30_CALLFAIL;
  822. pTG->fFatalErrorWasSignaled = 1;
  823. SignalStatusChange(pTG, FS_FATAL_ERROR);
  824. RetCode = FALSE;
  825. goto done;
  826. }
  827. // first get SEND_CAPS
  828. if(!Class2GetBC(pTG, SEND_CAPS)) // sleep until we get it
  829. {
  830. uRet1 = T30_CALLFAIL;
  831. pTG->fFatalErrorWasSignaled = 1;
  832. SignalStatusChange(pTG, FS_FATAL_ERROR);
  833. RetCode = FALSE;
  834. goto done;
  835. }
  836. // Go to Class2.0
  837. if(!iModemGoClass(pTG, 3))
  838. {
  839. DebugPrintEx(DEBUG_ERR,"Failed to Go to Class 2.0");
  840. uRet1 = T30_CALLFAIL;
  841. pTG->fFatalErrorWasSignaled = 1;
  842. SignalStatusChange(pTG, FS_FATAL_ERROR);
  843. RetCode = FALSE;
  844. goto done;
  845. }
  846. // Begin by checking for manufacturer and ATI code.
  847. // Look this up against the modem specific table we
  848. // have and set up the receive strings needed for
  849. // this modem.
  850. if(!Class2GetModemMaker(pTG))
  851. {
  852. DebugPrintEx(DEBUG_WRN,"Call to GetModemMaker failed");
  853. // Ignore failure!!!
  854. }
  855. // set manufacturer specific strings
  856. Class2SetMFRSpecific(pTG);
  857. // Get the capabilities of the software. I am only using this
  858. // right now for the CSI field (below where I send +FLID).
  859. // Really, this should also be used instead of the hardcoded DIS
  860. // values below.
  861. // ALL COMMANDS LOOK FOR MULTILINE RESPONSES WHILE MODEM IS ONHOOK.
  862. // A "RING" COULD APPEAR AT ANY TIME!
  863. _fmemset((LPB)szCSI, 0, sizeof(szCSI));
  864. Class2SetDIS_DCSParams( pTG,
  865. SEND_CAPS,
  866. (LPUWORD)&Encoding,
  867. (LPUWORD)&Res,
  868. (LPUWORD)&PageWidth,
  869. (LPUWORD)&PageLength,
  870. (LPSTR) szCSI,
  871. sizeof(szCSI)/sizeof(szCSI[0]));
  872. if (!Class20GetDefaultFDIS(pTG))
  873. {
  874. DebugPrintEx(DEBUG_ERR, "Class20GetDefaultFDIS failed");
  875. uRet1 = T30_CALLFAIL;
  876. pTG->fFatalErrorWasSignaled = 1;
  877. SignalStatusChange(pTG, FS_FATAL_ERROR);
  878. RetCode = FALSE;
  879. goto done;
  880. }
  881. fBaudChanged = FALSE;
  882. // See if we have to change the baud rate to a lower value.
  883. // This only happens if the user set an ini string inhibiting
  884. // V.17 receive
  885. if ( (pTG->DISPcb.Baud > 3) && (!pTG->ProtParams2.fEnableV17Recv) )
  886. {
  887. DebugPrintEx(DEBUG_MSG,"Lowering baud from %d for V.17 receive inihibit", CodeToBPS[pTG->DISPcb.Baud]);
  888. pTG->DISPcb.Baud = 3; //9600 won't use V.17
  889. fBaudChanged = TRUE;
  890. }
  891. // Now, look and see if any of the values in the DIS are "bad"
  892. // That is, make sure we can receive high res and we are not
  893. // claiming that we are capable of MR or MMR. Also, see if we changed
  894. // the baud rate. Also make sure we can receive wide pages.
  895. // Set the current session parameters
  896. uwLen=(UWORD)wsprintf((LPSTR)bBuf, pTG->cbszFDCC_RECV_ALL, pTG->DISPcb.Baud);
  897. if(!Class2iModemDialog( pTG,
  898. bBuf,
  899. uwLen,
  900. LOCALCOMMAND_TIMEOUT,
  901. 0,
  902. TRUE,
  903. pTG->cbszCLASS2_OK,
  904. (C2PSTR) NULL))
  905. {
  906. uRet1 = T30_CALLFAIL;
  907. pTG->fFatalErrorWasSignaled = 1;
  908. SignalStatusChange(pTG, FS_FATAL_ERROR);
  909. RetCode = FALSE;
  910. goto done;
  911. }
  912. // Enable Reception
  913. if(!Class2iModemDialog( pTG,
  914. pTG->cbszFCR,
  915. (UWORD)(strlen(pTG->cbszFCR) ),
  916. ANS_LOCALCOMMAND_TIMEOUT,
  917. 0,
  918. TRUE,
  919. pTG->cbszCLASS2_OK,
  920. pTG->cbszCLASS2_ERROR,
  921. (C2PSTR) NULL))
  922. {
  923. DebugPrintEx(DEBUG_ERR,"FCR failed");
  924. uRet1 = T30_CALLFAIL;
  925. pTG->fFatalErrorWasSignaled = 1;
  926. SignalStatusChange(pTG, FS_FATAL_ERROR);
  927. RetCode = FALSE;
  928. goto done;
  929. }
  930. if(!Class2iModemDialog( pTG,
  931. pTG->cbszFNR,
  932. (UWORD) (strlen(pTG->cbszFNR) ),
  933. ANS_LOCALCOMMAND_TIMEOUT,
  934. 0,
  935. TRUE,
  936. pTG->cbszCLASS2_OK,
  937. pTG->cbszCLASS2_ERROR,
  938. (C2PSTR) NULL))
  939. {
  940. DebugPrintEx(DEBUG_WRN,"FNR failed");
  941. // ignore error
  942. }
  943. // Turn off Copy Quality Checking - also skip for Sierra type modems
  944. if(!Class2iModemDialog( pTG,
  945. pTG->cbszFCQ,
  946. (UWORD) (strlen(pTG->cbszFCQ) ),
  947. ANS_LOCALCOMMAND_TIMEOUT,
  948. 0,
  949. TRUE,
  950. pTG->cbszCLASS2_OK,
  951. pTG->cbszCLASS2_ERROR,
  952. (C2PSTR) NULL))
  953. {
  954. DebugPrintEx(DEBUG_WRN,"FCQ failed");
  955. // Ignore CQ failure!!!
  956. }
  957. // Set the local ID - need ID from above to do this.
  958. bIDBuf[0] = '\0';
  959. uwLen = (UWORD)wsprintf(bIDBuf, pTG->cbszFLID, (LPSTR)szCSI);
  960. if(!Class2iModemDialog( pTG,
  961. bIDBuf,
  962. uwLen,
  963. ANS_LOCALCOMMAND_TIMEOUT,
  964. 0,
  965. TRUE,
  966. pTG->cbszCLASS2_OK,
  967. pTG->cbszCLASS2_ERROR,
  968. (C2PSTR) NULL))
  969. {
  970. DebugPrintEx(DEBUG_WRN,"Local ID failed");
  971. // ignore failure
  972. }
  973. // Answer the phone
  974. // have to call hangup on every path out of here
  975. // after Answer is called. If Answer fails, it calls Hangup.
  976. // if it succeeds we have to call Hangup when we're done
  977. SignalStatusChange(pTG, FS_ANSWERED);
  978. PSSLogEntry(PSS_MSG, 0, "Phase A - Call establishment");
  979. PSSLogEntry(PSS_MSG, 1, "Answering...");
  980. if((uRet2 = Class2Answer(pTG)) != CONNECT_OK)
  981. {
  982. DebugPrintEx(DEBUG_ERR, "Failed to answer - aborting");
  983. // SignalStatusChange is called inside Class2Answer
  984. uRet1 = T30_CALLFAIL;
  985. RetCode = FALSE;
  986. goto done;
  987. }
  988. DebugPrintEx(DEBUG_MSG,"Done with Class2 Answer - succeeded");
  989. PSSLogEntry(PSS_MSG, 0, "Phase B - Negotiation");
  990. PSSLogEntry(PSS_MSG, 1, "CSI is %s", szCSI);
  991. PSSLogEntry(PSS_MSG, 1, "DIS was composed with the following capabilities:");
  992. LogClass2DISDetails(pTG, &pTG->DISPcb);
  993. // Receive the data
  994. uRet1 = (USHORT)Class20Receive(pTG );
  995. // t-jonb: If we've already called PutRecvBuf(RECV_STARTPAGE), but not
  996. // PutRecvBuf(RECV_ENDPAGE / DOC), then InFileHandleNeedsBeClosed==1, meaning
  997. // there's a .RX file that hasn't been copied to the .TIF file. Since the
  998. // call was disconnected, there will be no chance to send RTN. Therefore, we call
  999. // PutRecvBuf(RECV_ENDDOC_FORCESAVE) to keep the partial page and tell
  1000. // rx_thrd to terminate.
  1001. if (pTG->InFileHandleNeedsBeClosed)
  1002. {
  1003. if (! FlushFileBuffers (pTG->InFileHandle ) )
  1004. {
  1005. DebugPrintEx(DEBUG_WRN, "FlushFileBuffers FAILED LE=%lx", GetLastError());
  1006. // Continue to save what we have
  1007. }
  1008. pTG->BytesIn = pTG->BytesInNotFlushed;
  1009. ICommPutRecvBuf(pTG, NULL, RECV_ENDDOC_FORCESAVE);
  1010. }
  1011. if ( uRet1 == T30_CALLDONE)
  1012. {
  1013. DebugPrintEx(DEBUG_MSG,"******* DONE WITH CALL, ALL OK");
  1014. // have to call hangup on every path out of here
  1015. // we have to call Hangup here
  1016. Class2ModemHangup(pTG );
  1017. SignalStatusChange(pTG, FS_COMPLETED);
  1018. RetCode = TRUE;
  1019. }
  1020. else
  1021. {
  1022. DebugPrintEx(DEBUG_ERR,"******* DONE WITH CALL, **** FAILED *****");
  1023. // Make sure modem is in an OK state!
  1024. FComXon(pTG, FALSE);
  1025. // have to call hangup on every path out of here
  1026. // Abort calls Hangup
  1027. Class2ModemAbort(pTG );
  1028. Class2SignalFatalError(pTG);
  1029. RetCode = FALSE;
  1030. }
  1031. uRet2 = 0;
  1032. done:
  1033. return RetCode;
  1034. }
  1035. BOOL Class20Receive(PThrdGlbl pTG)
  1036. {
  1037. LPBUFFER lpbf;
  1038. SWORD swRet;
  1039. ULONG lTotalLen=0;
  1040. PCB Pcb;
  1041. USHORT uTimeout=30000, uRet;
  1042. BOOL err_status;
  1043. BC bc;
  1044. LPSTR lpsTemp;
  1045. DWORD HiRes;
  1046. DEBUG_FUNCTION_NAME("Class20Receive");
  1047. /*
  1048. * We have just answered!
  1049. */
  1050. // The repsonse to the ATA command is in the global variable
  1051. // pTG->lpbResponseBuf2.
  1052. if ( Class2ResponseAction(pTG, (LPPCB) &Pcb) == FALSE )
  1053. {
  1054. PSSLogEntry(PSS_ERR, 1, "Failed to parse response from ATA - aborting");
  1055. err_status = T30_CALLFAIL;
  1056. return err_status;
  1057. }
  1058. PSSLogEntry(PSS_MSG, 1, "TSI is %s", Pcb.szID);
  1059. PSSLogEntry(PSS_MSG, 1, "Received DCS is as follows");
  1060. LogClass2DCSDetails(pTG, &Pcb);
  1061. if (!Class2IsValidDCS(&Pcb))
  1062. {
  1063. PSSLogEntry(PSS_ERR, 1, "Received bad DCS parameters - aborting");
  1064. err_status = T30_CALLFAIL;
  1065. return err_status;
  1066. }
  1067. if (!Class2UpdateTiffInfo(pTG, &Pcb))
  1068. {
  1069. DebugPrintEx(DEBUG_WRN, "Class2UpdateTiffInfo failed");
  1070. }
  1071. //Now that pcb is set up, call ICommReceiveParams to tell icomfile
  1072. Class2InitBC(pTG, (LPBC)&bc, sizeof(bc), RECV_PARAMS);
  1073. Class2PCBtoBC(pTG, (LPBC)&bc, sizeof(bc), &Pcb);
  1074. if( ICommRecvParams(pTG, (LPBC)&bc) == FALSE )
  1075. {
  1076. DebugPrintEx(DEBUG_ERR,"Failed return from ICommRecvParams.");
  1077. err_status = T30_CALLFAIL;
  1078. return err_status;
  1079. }
  1080. //
  1081. // once per RX - create TIFF file as soon as we know the compression / resolution.
  1082. //
  1083. pTG->Encoding = Pcb.Encoding;
  1084. pTG->Resolution = Pcb.Resolution;
  1085. if (Pcb.Resolution & (AWRES_mm080_077 | AWRES_200_200) )
  1086. {
  1087. HiRes = 1;
  1088. }
  1089. else
  1090. {
  1091. HiRes = 0;
  1092. }
  1093. if ( !pTG->fTiffOpenOrCreated)
  1094. {
  1095. //
  1096. // top 32bits of 64bit handle are guaranteed to be zero
  1097. //
  1098. pTG->Inst.hfile = TiffCreateW ( pTG->lpwFileName,
  1099. pTG->TiffInfo.CompressionType,
  1100. pTG->TiffInfo.ImageWidth,
  1101. FILLORDER_LSB2MSB,
  1102. HiRes
  1103. );
  1104. if (! (pTG->Inst.hfile))
  1105. {
  1106. lpsTemp = UnicodeStringToAnsiString(pTG->lpwFileName);
  1107. DebugPrintEx( DEBUG_ERR,
  1108. "Can't create tiff file %s compr=%d",
  1109. lpsTemp,
  1110. pTG->TiffInfo.CompressionType);
  1111. MemFree(lpsTemp);
  1112. err_status = T30_CALLFAIL;
  1113. return err_status;
  1114. }
  1115. pTG->fTiffOpenOrCreated = 1;
  1116. lpsTemp = UnicodeStringToAnsiString(pTG->lpwFileName);
  1117. DebugPrintEx( DEBUG_MSG,
  1118. "Created tiff file %s compr=%d HiRes=%d",
  1119. lpsTemp,
  1120. pTG->TiffInfo.CompressionType,
  1121. HiRes);
  1122. MemFree(lpsTemp);
  1123. }
  1124. // **** Apparently, we don't want flow control on, so we'll turn
  1125. // it off. Is this true???? If I turn it on, fcom.c fails a
  1126. // debug check in filterreadbuf.
  1127. FComXon(pTG, FALSE);
  1128. // Send the FDR. The FDR must be responded to by a CONNECT.
  1129. if (Class2iModemDialog( pTG,
  1130. pTG->cbszFDR,
  1131. (UWORD) (strlen(pTG->cbszFDR) ),
  1132. STARTSENDMODE_TIMEOUT,
  1133. 0,
  1134. TRUE,
  1135. pTG->cbszFDT_CONNECT,
  1136. (C2PSTR) NULL) != 1)
  1137. {
  1138. DebugPrintEx(DEBUG_ERR,"Failed get response from initial FDR");
  1139. if (pTG->fFoundFHNG)
  1140. {
  1141. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  1142. }
  1143. err_status = T30_CALLFAIL;
  1144. return err_status;
  1145. }
  1146. DebugPrintEx(DEBUG_MSG,"FDR Received %s", (LPSTR)(&(pTG->lpbResponseBuf2)));
  1147. // Might have to search through FDR response, but I doubt it.
  1148. PSSLogEntry(PSS_MSG, 0, "Phase C - Receive page");
  1149. // Now we need to send a DC2 (0x12) to tell the modem it is OK
  1150. // to give us data.
  1151. // Some modems use ^Q instead of ^R - The correct value was written
  1152. // into the DC@ string in Class20Callee where we checked for
  1153. // manufacturer
  1154. PSSLogEntry(PSS_MSG, 2, "send: <DC2> (=ASCII %d)", *(pTG->CurrentMFRSpec.szDC2));
  1155. FComDirectSyncWriteFast(pTG, pTG->CurrentMFRSpec.szDC2, 1);
  1156. // Now we can receive the data and give it to the icomfile routine
  1157. err_status = T30_CALLDONE;
  1158. while ((swRet=(SWORD)ICommPutRecvBuf(pTG, NULL, RECV_STARTPAGE)) == TRUE)
  1159. {
  1160. PSSLogEntry(PSS_MSG, 1, "Receiving page %d data...", pTG->PageCount+1);
  1161. // The READ_TIMEOUT is used to timeout calls to ReadBuf() either in the
  1162. #define READ_TIMEOUT 15000
  1163. lTotalLen = 0;
  1164. do
  1165. {
  1166. DebugPrintEx(DEBUG_MSG,"In receiving a page loop");
  1167. uRet=Class2ModemRecvBuf(pTG, &lpbf, READ_TIMEOUT);
  1168. if(lpbf)
  1169. {
  1170. lTotalLen += lpbf->wLengthData;
  1171. DebugPrintEx(DEBUG_MSG,"In lpbf if. Total Length %ld", lTotalLen);
  1172. if(!ICommPutRecvBuf(pTG, lpbf, RECV_SEQ))
  1173. {
  1174. DebugPrintEx(DEBUG_ERR,"Bad return - PutRecvBuf in page");
  1175. err_status=T30_CALLFAIL;
  1176. return err_status;
  1177. }
  1178. lpbf = 0;
  1179. }
  1180. }
  1181. while(uRet == RECV_OK);
  1182. PSSLogEntry(PSS_MSG, 2, "recv: page %d data, %d bytes", pTG->PageCount+1, lTotalLen);
  1183. if(uRet == RECV_EOF)
  1184. {
  1185. DebugPrintEx(DEBUG_MSG,"Got EOF from RecvBuf");
  1186. // RSL needed interface to TIFF thread
  1187. pTG->fLastReadBlock = 1;
  1188. ICommPutRecvBuf(pTG, NULL, RECV_FLUSH);
  1189. }
  1190. else
  1191. {
  1192. // Timeout from ModemRecvBuf
  1193. BYTE bCancel = 0x18;
  1194. DebugPrintEx(DEBUG_ERR,"ModemRecvBuf Timeout or Error=%d", uRet);
  1195. PSSLogEntry(PSS_ERR, 1, "Failed to receive page data - aborting");
  1196. PSSLogEntry(PSS_MSG, 2, "send: <can> (=ASCII 24)");
  1197. FComDirectSyncWriteFast(pTG, &bCancel, 1);
  1198. err_status = T30_CALLFAIL;
  1199. return err_status;
  1200. }
  1201. PSSLogEntry(PSS_MSG, 1, "Successfully received page data");
  1202. PSSLogEntry(PSS_MSG, 0, "Phase D - Post Message Exchange");
  1203. // See if more pages to receive by parsing the FDR response...
  1204. // After the DLEETX was received by Class2ModemRecvBuf, the
  1205. // FPTS and FET response should be coming from the modem, terminated
  1206. // by an OK. Let's go read that!
  1207. if (Class2iModemDialog(pTG,
  1208. NULL,
  1209. 0,
  1210. STARTSENDMODE_TIMEOUT,
  1211. 0,
  1212. TRUE,
  1213. pTG->cbszCLASS2_OK,
  1214. (C2PSTR)NULL) != 1)
  1215. {
  1216. PSSLogEntry(PSS_ERR, 1, "Failed to receive EOP or MPS or EOM - aborting");
  1217. err_status = T30_CALLFAIL;
  1218. return err_status;
  1219. }
  1220. if (pTG->fFoundFHNG)
  1221. {
  1222. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  1223. err_status = T30_CALLFAIL;
  1224. return err_status;
  1225. }
  1226. DebugPrintEx(DEBUG_MSG,"EOP Received %s",(LPSTR)(&(pTG->lpbResponseBuf2)));
  1227. // Process the response and see if more pages are coming
  1228. if (Class2EndPageResponseAction(pTG ) == MORE_PAGES)
  1229. {
  1230. // t-jonb: Here, we should be sending AT+FPS=1 or AT+FPS=2, according to fPageIsBad.
  1231. // However, some modems (observed on USR Courier V.34 and USR Sportster 33.6)
  1232. // don't understand it. So, we have to work with the modem's own quality assessment.
  1233. // For class 2.0, ICommPutRecvBuf will decide whether to save the page based the
  1234. // value from modem's +FPS: response (Saved in pTG->FPTSreport by
  1235. // Class2EndPageResponseAction).
  1236. ICommPutRecvBuf(pTG, NULL, RECV_ENDPAGE);
  1237. if (pTG->fPageIsBadOverride)
  1238. {
  1239. err_status = T30_CALLFAIL; // User will see "partially received"
  1240. }
  1241. PSSLogEntry(PSS_MSG, 1, "sent MCF"); // Sending RTN is not yet implemented
  1242. // Now, Send the FDR to start the next page (this was done for
  1243. // the first page before entering the multipage loop).
  1244. if (Class2iModemDialog( pTG,
  1245. pTG->cbszFDR,
  1246. (UWORD)(strlen(pTG->cbszFDR) ),
  1247. STARTSENDMODE_TIMEOUT,
  1248. 0,
  1249. TRUE,
  1250. pTG->cbszFDT_CONNECT,
  1251. (C2PSTR) NULL) != 1)
  1252. {
  1253. DebugPrintEx(DEBUG_ERR,"FDR to start next PAGE Failed!");
  1254. if (pTG->fFoundFHNG)
  1255. {
  1256. PSSLogEntry(PSS_ERR, 1, "Call was disconnected");
  1257. }
  1258. err_status = T30_CALLFAIL;
  1259. return err_status;
  1260. }
  1261. // Need to check whether modem performed re-negotiation, and
  1262. // update the TIFF accordingly
  1263. if (Class2ResponseAction(pTG, (LPPCB) &Pcb))
  1264. {
  1265. PSSLogEntry(PSS_MSG, 1, "Received DCS is as follows");
  1266. LogClass2DCSDetails(pTG, &Pcb);
  1267. if (!Class2UpdateTiffInfo(pTG, &Pcb))
  1268. {
  1269. DebugPrintEx(DEBUG_WRN, "Class2UpdateTiffInfo failed");
  1270. }
  1271. }
  1272. PSSLogEntry(PSS_MSG, 0, "Phase C - Receive page");
  1273. PSSLogEntry(PSS_MSG, 2, "send: <DC2> (=ASCII %d)", *(pTG->CurrentMFRSpec.szDC2));
  1274. // Now send the correct DC2 string set in Class20Callee
  1275. // (DC2 is standard, some use ^q instead)
  1276. FComDirectSyncWriteFast(pTG, pTG->CurrentMFRSpec.szDC2, 1);
  1277. } //if we do not have another page, do the else...
  1278. else
  1279. {
  1280. break; // All done receiving pages...
  1281. }
  1282. } //End of multipage while
  1283. DebugPrintEx(DEBUG_MSG,"out of while multipage loop. about to send final FDR.");
  1284. //RSL
  1285. ICommPutRecvBuf(pTG, NULL, RECV_ENDDOC);
  1286. if (pTG->fPageIsBadOverride)
  1287. {
  1288. err_status = T30_CALLFAIL; // User will see "partially received"
  1289. }
  1290. // Send end of message sequence
  1291. // Send the last FPTS - do we really need to do this???
  1292. // Send last FDR
  1293. if(!Class2iModemDialog( pTG,
  1294. pTG->cbszFDR,
  1295. (UWORD) (strlen(pTG->cbszFDR) ),
  1296. STARTSENDMODE_TIMEOUT,
  1297. 0,
  1298. TRUE,
  1299. pTG->cbszCLASS2_OK,
  1300. (C2PSTR)NULL))
  1301. {
  1302. err_status = T30_CALLFAIL;
  1303. return err_status;
  1304. }
  1305. PSSLogEntry(PSS_MSG, 1, "sent MCF"); // Sending RTN is not yet implemented
  1306. FComXon(pTG, FALSE);
  1307. return err_status;
  1308. }
  1309. BOOL Class20Parse(PThrdGlbl pTG, CL2_COMM_ARRAY *cl2_comm, BYTE lpbBuf[])
  1310. {
  1311. int i,
  1312. j,
  1313. comm_numb = 0,
  1314. parameters;
  1315. BYTE switch_char,
  1316. char_1,
  1317. char_2;
  1318. char c;
  1319. BOOL found_command = FALSE;
  1320. DEBUG_FUNCTION_NAME("Class20Parse");
  1321. #define STRING_PARAMETER 1
  1322. #define NUMBER_PARAMETERS 2
  1323. for(i = 0; lpbBuf[i] != '\0'; ++i)
  1324. {
  1325. if (comm_numb >= MAX_CLASS2_COMMANDS)
  1326. {
  1327. DebugPrintEx(DEBUG_WRN, "Reached maximum number of commands");
  1328. break;
  1329. }
  1330. switch ( lpbBuf[i] )
  1331. {
  1332. case 'C':
  1333. if (lpbBuf[++i] == 'O' && lpbBuf[++i] == 'N')
  1334. {
  1335. cl2_comm->command[comm_numb++] = CL2DCE_CONNECT;
  1336. for(; lpbBuf[i] != '\r'; ++i )
  1337. ;
  1338. }
  1339. else
  1340. {
  1341. DebugPrintEx(DEBUG_ERR,"Parse: Bad First C values");
  1342. return FALSE;
  1343. }
  1344. break;
  1345. case 'O':
  1346. if (lpbBuf[++i] == 'K' )
  1347. {
  1348. cl2_comm->command[comm_numb++] = CL2DCE_OK;
  1349. for(; lpbBuf[i] != '\r'; ++i )
  1350. ;
  1351. }
  1352. else
  1353. {
  1354. DebugPrintEx(DEBUG_ERR, "Parse: Bad O values");
  1355. return FALSE;
  1356. }
  1357. break;
  1358. case 0x11:
  1359. cl2_comm->command[comm_numb++] = CL2DCE_XON;
  1360. break;
  1361. case '+':
  1362. if( lpbBuf[++i] != 'F' )
  1363. {
  1364. DebugPrintEx(DEBUG_ERR, "Parse: Bad + values");
  1365. return FALSE;
  1366. }
  1367. switch_char = lpbBuf[++i];
  1368. char_1 = lpbBuf[++i];
  1369. char_2 = lpbBuf[++i];
  1370. switch ( switch_char )
  1371. {
  1372. case 'C':
  1373. // Connect Message +FCON.
  1374. if ( char_1 == 'O' )
  1375. {
  1376. cl2_comm->command[comm_numb] = CL2DCE_FCON;
  1377. parameters = FALSE;
  1378. }
  1379. // Report of Remote ID. +FCIG.
  1380. else if (char_1 == 'I' )
  1381. {
  1382. cl2_comm->command[comm_numb] = CL2DCE_FCSI;
  1383. parameters = STRING_PARAMETER;
  1384. }
  1385. // Report DCS frame information +FCS - Clanged for Class2.0
  1386. else if ( char_1 == 'S' )
  1387. {
  1388. cl2_comm->command[comm_numb] = CL2DCE_FDCS;
  1389. parameters = NUMBER_PARAMETERS;
  1390. }
  1391. else
  1392. {
  1393. DebugPrintEx(DEBUG_ERR, "Parse: Bad C values");
  1394. return FALSE;
  1395. }
  1396. break;
  1397. case 'D':
  1398. if ( char_1 == 'M' )
  1399. {
  1400. cl2_comm->command[comm_numb] = CL2DCE_FDM;
  1401. parameters = NUMBER_PARAMETERS;
  1402. }
  1403. else
  1404. {
  1405. DebugPrintEx(DEBUG_ERR,"Parse: Bad D values");
  1406. return FALSE;
  1407. }
  1408. break;
  1409. case 'E':
  1410. // Post page message report. +FET.
  1411. if ( char_1 == 'T' )
  1412. {
  1413. --i;
  1414. cl2_comm->command[comm_numb] = CL2DCE_FET;
  1415. parameters = NUMBER_PARAMETERS;
  1416. }
  1417. else
  1418. {
  1419. DebugPrintEx(DEBUG_ERR, "Parse: Bad E values");
  1420. return FALSE;
  1421. }
  1422. break;
  1423. case 'H':
  1424. // Debug report transmitted HDLC frames +FHT
  1425. if ( char_1 == 'T' )
  1426. {
  1427. --i;
  1428. cl2_comm->command[comm_numb] = CL2DCE_FHT;
  1429. parameters = STRING_PARAMETER;
  1430. }
  1431. // Debug report received HDLC frames +FHR
  1432. if ( char_1 == 'R' )
  1433. {
  1434. --i;
  1435. cl2_comm->command[comm_numb] = CL2DCE_FHR;
  1436. parameters = STRING_PARAMETER;
  1437. }
  1438. // Report hang up. +FHNG.
  1439. else if ( char_1 == 'S' )
  1440. {
  1441. cl2_comm->command[comm_numb] = CL2DCE_FHNG;
  1442. parameters = NUMBER_PARAMETERS;
  1443. DebugPrintEx(DEBUG_MSG, "Found FHNG");
  1444. pTG->fFoundFHNG = TRUE;
  1445. }
  1446. else
  1447. {
  1448. DebugPrintEx(DEBUG_ERR, "Parse: Bad H values");
  1449. return FALSE;
  1450. }
  1451. break;
  1452. case 'I':
  1453. // Report DIS frame information +FIS - Changed for Class2.0
  1454. if ( char_1 == 'S' )
  1455. {
  1456. cl2_comm->command[comm_numb] = CL2DCE_FDIS;
  1457. parameters = NUMBER_PARAMETERS;
  1458. }
  1459. else
  1460. {
  1461. DebugPrintEx(DEBUG_ERR,"Parse: Bad I values");
  1462. return FALSE;
  1463. }
  1464. break;
  1465. case 'N':
  1466. // Report NSF frame reciept.
  1467. if ( char_1 == 'F' )
  1468. {
  1469. cl2_comm->command[comm_numb] = CL2DCE_FNSF;
  1470. parameters = NUMBER_PARAMETERS;
  1471. }
  1472. // Report NSS frame reciept.
  1473. else if ( char_1 == 'S' )
  1474. {
  1475. cl2_comm->command[comm_numb] = CL2DCE_FNSS;
  1476. parameters = NUMBER_PARAMETERS;
  1477. }
  1478. // Report NSC frame reciept.
  1479. else if ( char_1 == 'C' )
  1480. {
  1481. cl2_comm->command[comm_numb] = CL2DCE_FNSC;
  1482. parameters = NUMBER_PARAMETERS;
  1483. }
  1484. else
  1485. {
  1486. DebugPrintEx(DEBUG_ERR, "Parse: Bad N values");
  1487. return FALSE;
  1488. }
  1489. break;
  1490. case 'P':
  1491. // Report of Remote ID. +FPI - Changed for Class2.0
  1492. if (char_1 == 'I')
  1493. {
  1494. cl2_comm->command[comm_numb] = CL2DCE_FCIG;
  1495. parameters = STRING_PARAMETER;
  1496. }
  1497. // Report poll request. +FPO - Changed for Class2.0
  1498. else if ( char_1 == 'O' )
  1499. {
  1500. cl2_comm->command[comm_numb] = CL2DCE_FPOLL;
  1501. parameters = FALSE;
  1502. }
  1503. // Page Transfer Status Report +FPS - Changed for Class2.0
  1504. else if ( char_1 == 'S' )
  1505. {
  1506. cl2_comm->command[comm_numb] = CL2DCE_FPTS;
  1507. parameters = NUMBER_PARAMETERS;
  1508. }
  1509. else
  1510. {
  1511. DebugPrintEx(DEBUG_ERR,"Parse: Bad P values");
  1512. return FALSE;
  1513. }
  1514. break;
  1515. case 'T':
  1516. // Report DTC frame information +FTC - Changed for Class2.0
  1517. if ( char_1 == 'C' )
  1518. {
  1519. cl2_comm->command[comm_numb] = CL2DCE_FDTC;
  1520. parameters = NUMBER_PARAMETERS;
  1521. }
  1522. // Report remote ID +FTI - Changed for Class2.0
  1523. else if ( char_1 == 'I' )
  1524. {
  1525. cl2_comm->command[comm_numb] = CL2DCE_FTSI;
  1526. parameters = STRING_PARAMETER;
  1527. }
  1528. else
  1529. {
  1530. DebugPrintEx(DEBUG_ERR,"Parse: Bad T values");
  1531. return FALSE;
  1532. }
  1533. break;
  1534. case 'V':
  1535. // Report voice request +FVOICE.
  1536. if ( char_1 == 'O' )
  1537. {
  1538. cl2_comm->command[comm_numb] = CL2DCE_FVOICE;
  1539. parameters = FALSE;
  1540. }
  1541. else
  1542. {
  1543. DebugPrintEx(DEBUG_ERR, "Parse: Bad V values");
  1544. return FALSE;
  1545. }
  1546. }
  1547. // Transfer the associated paramters to the parameter array.
  1548. if (parameters == NUMBER_PARAMETERS)
  1549. {
  1550. for (i+=1,j=0; lpbBuf[i] != '\r' && lpbBuf[i] != '\0'; ++i)
  1551. {
  1552. // Skip past the non numeric characters.
  1553. if ( lpbBuf[i] < '0' || lpbBuf[i] > '9' )
  1554. {
  1555. continue;
  1556. }
  1557. /* Convert the character representation of the numeric
  1558. parameter into a true number, and store in the
  1559. parameter list. */
  1560. cl2_comm->parameters[comm_numb][j] = 0;
  1561. for (; lpbBuf[i] >= '0' && lpbBuf[i] <= '9'; ++i)
  1562. {
  1563. cl2_comm->parameters[comm_numb][j] *= 10;
  1564. cl2_comm->parameters[comm_numb][j] += lpbBuf[i] - '0';
  1565. }
  1566. i--; // the last for loop advanced 'i' past the numeric.
  1567. j++; // get set up for next parameter
  1568. }
  1569. }
  1570. else if (parameters == STRING_PARAMETER )
  1571. {
  1572. // Skip the : that follows the +f command (eg +FTSI:)
  1573. if (lpbBuf[i+1] == ':')
  1574. {
  1575. i++;
  1576. }
  1577. // Also skip leading blanks
  1578. while (lpbBuf[i+1] == ' ')
  1579. {
  1580. i++;
  1581. }
  1582. for (i+=1, j=0; (j < MAX_PARAM_LENGTH-1) &&
  1583. (c = lpbBuf[i]) != '\r' && c != '\n' && c != '\0'; ++i, ++j)
  1584. {
  1585. cl2_comm->parameters[comm_numb][j] = c;
  1586. if ( lpbBuf[i] == '\"' )
  1587. {
  1588. --j;
  1589. }
  1590. }
  1591. cl2_comm->parameters[comm_numb][j] = '\0';
  1592. }
  1593. // No parameters, so just skip to end of line.
  1594. else
  1595. {
  1596. for(; (c=lpbBuf[i]) != '\r' && c != '\n' && c != '\0'; ++i)
  1597. ;
  1598. }
  1599. if (cl2_comm->command[comm_numb] == CL2DCE_FHNG)
  1600. {
  1601. pTG->dwFHNGReason = cl2_comm->parameters[comm_numb][0];
  1602. DebugPrintEx(DEBUG_MSG, "Found FHNG, reason = %d", pTG->dwFHNGReason);
  1603. }
  1604. // Increment command count.
  1605. ++comm_numb;
  1606. break;
  1607. default:
  1608. break;
  1609. }
  1610. }
  1611. cl2_comm->comm_count = (USHORT)comm_numb;
  1612. return TRUE;
  1613. }