core/builtins/xmodem.cpp
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // xmodem.cpp
       
     2 // 
       
     3 // Copyright (c) 2008 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include <fshell/ioutils.h>
       
    14 #include "xmodem.h"
       
    15 
       
    16 using namespace IoUtils;
       
    17 
       
    18 const TInt KMaxRetries = 5;
       
    19 const TInt KSmallBlockSize = 128;
       
    20 const TInt KLargeBlockSize = 1024;
       
    21 const TInt KLongTimeout = 60;
       
    22 const TInt KMediumTimeout = 10;
       
    23 const TInt KShortTimeout = 1;
       
    24 const TUint8 KByteSoh = 0x01;
       
    25 const TUint8 KByteStx = 0x02;
       
    26 const TUint8 KByteEot = 0x04;
       
    27 const TUint8 KByteAck = 0x06;
       
    28 const TUint8 KByteNak = 0x15;
       
    29 const TUint8 KByteCan = 0x18;
       
    30 const TUint8 KByteSub = 0x1a;
       
    31 const TUint8 KByteTelnetIac = 0xff;
       
    32 const TUint8 KByteTelnetDo = 0xfd;
       
    33 const TUint8 KByteTelnetWill = 0xfb;
       
    34 const TUint8 KByteTelnetBinaryMode = 0x00;
       
    35 _LIT(KLitEot, "\x04");
       
    36 _LIT(KLitAck, "\x06");
       
    37 _LIT(KLitNak, "\x15");
       
    38 _LIT(KLitCancel, "\x18\x18\x18");
       
    39 _LIT(KLitC, "C");
       
    40 _LIT(KLitTelnetDoBinaryMode, "\xff\xfd\x00");
       
    41 _LIT(KLitTelnetWillBinaryMode, "\xff\xfb\x00");
       
    42 
       
    43 #ifdef FSHELL_CORE_SUPPORT_XMODEM_CRCNOTAB
       
    44 
       
    45 TUint16 Crc16(const TDesC& aData)
       
    46 	{
       
    47 	TInt crc	= 0;
       
    48 	const TInt length = aData.Length();
       
    49 	for (TInt i = 0; i < length; ++i)
       
    50 		{
       
    51 		crc = crc ^ ((TInt)(aData[i]&0x00FF) << 8);
       
    52 		for (TInt j = 0; j < 8; ++j)
       
    53 			{
       
    54 			if (crc & 0x8000)
       
    55 				{
       
    56 				crc = crc << 1 ^ 0x1021;
       
    57 				}
       
    58 			else
       
    59 				{
       
    60 				crc = crc << 1;
       
    61 				}
       
    62 			}
       
    63 		}
       
    64 	return (crc	& 0xFFFF);
       
    65 	}
       
    66 
       
    67 #else
       
    68 
       
    69 static TUint16 KCrc16Tab[256]= {
       
    70 	0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
       
    71 	0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
       
    72 	0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
       
    73 	0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
       
    74 	0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
       
    75 	0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
       
    76 	0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
       
    77 	0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
       
    78 	0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
       
    79 	0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
       
    80 	0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
       
    81 	0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
       
    82 	0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
       
    83 	0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
       
    84 	0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
       
    85 	0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
       
    86 	0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
       
    87 	0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
       
    88 	0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
       
    89 	0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
       
    90 	0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
       
    91 	0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
       
    92 	0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
       
    93 	0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
       
    94 	0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
       
    95 	0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
       
    96 	0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
       
    97 	0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
       
    98 	0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
       
    99 	0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
       
   100 	0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
       
   101 	0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
       
   102 };
       
   103   
       
   104 TUint16 Crc16(const TDesC& aData)
       
   105 	{
       
   106 	TUint16 crc = 0;
       
   107 	const TInt length = aData.Length();
       
   108 	for (TInt i = 0; i < length; ++i)
       
   109 		{
       
   110 		crc = (crc<<8) ^ KCrc16Tab[((crc>>8) ^ (TUint8)aData[i])&0x00FF];
       
   111 		}
       
   112 	return crc;
       
   113 	}
       
   114 
       
   115 #endif // FSHELL_CORE_SUPPORT_XMODEM_CRCNOTAB
       
   116 
       
   117 CCommandBase* CCmdXmodem::NewLC()
       
   118 	{
       
   119 	CCmdXmodem* self = new(ELeave) CCmdXmodem();
       
   120 	CleanupStack::PushL(self);
       
   121 	self->BaseConstructL();
       
   122 	self->ConstructL();
       
   123 	return self;
       
   124 	}
       
   125 
       
   126 CCmdXmodem::~CCmdXmodem()
       
   127 	{
       
   128 	delete iBuf;
       
   129 	iTimer.Close();
       
   130 	}
       
   131 
       
   132 CCmdXmodem::CCmdXmodem() : iBlockSize(KSmallBlockSize), iPacketNumber(1)
       
   133 	{
       
   134 	}
       
   135 
       
   136 void CCmdXmodem::ConstructL()
       
   137 	{
       
   138 	User::LeaveIfError(iTimer.CreateLocal());
       
   139 	}
       
   140 
       
   141 TUint8 CCmdXmodem::ReceiveByteL(TInt aTimeout, TBool* aTimeoutOccurred)
       
   142 	{
       
   143 	return (TUint8)ReceiveShortL(aTimeout, aTimeoutOccurred);
       
   144 	}
       
   145 
       
   146 TUint16 CCmdXmodem::ReceiveShortL(TInt aTimeout, TBool* aTimeoutOccurred)
       
   147 	{
       
   148 	TBuf<1> buf;
       
   149 	ReceiveWithTimeoutL(buf, aTimeout, aTimeoutOccurred);
       
   150 	if (buf.Length() == 1)
       
   151 		{
       
   152 		return buf[0];
       
   153 		}
       
   154 	return 0;
       
   155 	}
       
   156 
       
   157 void CCmdXmodem::ReceiveWithTimeoutL(TDes& aBuf, TInt aTimeout, TBool* aTimeoutOccurred)
       
   158 	{
       
   159 	aBuf.Zero();
       
   160 	TBool timeOutOccurred(EFalse);
       
   161 
       
   162 	if (iTelnetMode & ERecvBinary)
       
   163 		{
       
   164 		TBool prevReceiveEndedWithIac(EFalse);
       
   165 		while ((aBuf.Length() < aBuf.MaxLength()) && !timeOutOccurred)
       
   166 			{
       
   167 			TPtr ptr(const_cast<TUint16*>(aBuf.Ptr()) + aBuf.Length(), 0, aBuf.MaxLength() - aBuf.Length()); // Create a TPtr over the unused part of aBuf.
       
   168 			DoReceiveWithTimeoutL(ptr, aTimeout, &timeOutOccurred);
       
   169 			if (!timeOutOccurred)
       
   170 				{
       
   171 				if (prevReceiveEndedWithIac && (ptr.Length() >= 1) && (ptr[0] == KByteTelnetIac))
       
   172 					{
       
   173 					ptr.Delete(0, 1);
       
   174 					}
       
   175 				prevReceiveEndedWithIac = EFalse;
       
   176 				TInt numChars = ptr.Length();
       
   177 				for (TInt i = 0; i < numChars; ++i)
       
   178 					{
       
   179 					if (ptr[i] == KByteTelnetIac)
       
   180 						{
       
   181 						if ((i < (numChars - 1)) && (ptr[i + 1] == KByteTelnetIac))
       
   182 							{
       
   183 							ptr.Delete(i, 1);
       
   184 							--numChars;
       
   185 							}
       
   186 						else if (i == (numChars - 1))
       
   187 							{
       
   188 							prevReceiveEndedWithIac = ETrue;
       
   189 							}
       
   190 						}
       
   191 					}
       
   192 				aBuf.SetLength(aBuf.Length() + ptr.Length());
       
   193 				}
       
   194 			}
       
   195 		}
       
   196 	else
       
   197 		{
       
   198 		DoReceiveWithTimeoutL(aBuf, aTimeout, &timeOutOccurred);
       
   199 		}
       
   200 
       
   201 	if (!timeOutOccurred)
       
   202 		{
       
   203 		Progress(_L("Received:\r\n"));
       
   204 		Dump(aBuf);
       
   205 		}
       
   206 
       
   207 	if (aTimeoutOccurred)
       
   208 		{
       
   209 		*aTimeoutOccurred = timeOutOccurred;
       
   210 		}
       
   211 	}
       
   212 
       
   213 void CCmdXmodem::DoReceiveWithTimeoutL(TDes& aBuf, TInt aTimeout, TBool* aTimeoutOccurred)
       
   214 	{
       
   215 	iStarted = ETrue;
       
   216 	aBuf.Zero();
       
   217 	if (aBuf.MaxLength() <= iUngetBuf.Length())
       
   218 		{
       
   219 		aBuf.Copy(iUngetBuf.Mid(0, aBuf.MaxLength()));
       
   220 		iUngetBuf.Delete(0, aBuf.MaxLength());
       
   221 		Progress(_L("Received (from unget buf):\r\n"));
       
   222 		Dump(aBuf);
       
   223 		}
       
   224 	else
       
   225 		{
       
   226 		aBuf.Copy(iUngetBuf);
       
   227 		TPtr ptr(const_cast<TUint16*>(aBuf.Ptr()) + aBuf.Length(), 0, aBuf.MaxLength() - aBuf.Length()); // Create a TPtr over the unused part of aBuf.
       
   228 		iUngetBuf.Zero();
       
   229 		TRequestStatus readStatus;
       
   230 		TRequestStatus timeoutStatus;
       
   231 		iTimer.After(timeoutStatus, aTimeout * 1000000);
       
   232 		Stdin().Read(aBuf, readStatus);
       
   233 		User::WaitForRequest(readStatus, timeoutStatus);
       
   234 		if (readStatus == KRequestPending)
       
   235 			{
       
   236 			Progress(_L("Timeout expired, cancelling read...\r\n"));
       
   237 			if (aTimeoutOccurred)
       
   238 				{
       
   239 				*aTimeoutOccurred = ETrue;
       
   240 				}
       
   241 			Stdin().ReadCancel();
       
   242 			User::WaitForRequest(readStatus);
       
   243 			return;
       
   244 			}
       
   245 		else
       
   246 			{
       
   247 			if (aTimeoutOccurred)
       
   248 				{
       
   249 				*aTimeoutOccurred = EFalse;
       
   250 				}
       
   251 			iTimer.Cancel();
       
   252 			User::WaitForRequest(timeoutStatus);
       
   253 			User::LeaveIfError(readStatus.Int());
       
   254 			aBuf.SetLength(aBuf.Length() + ptr.Length());
       
   255 			Progress(_L("Received (raw):\r\n"));
       
   256 			Dump(aBuf);
       
   257 			return;
       
   258 			}
       
   259 		}
       
   260 	}
       
   261 
       
   262 void CCmdXmodem::PurgeInputL()
       
   263 	{
       
   264 	Progress(_L("Purging input...\r\n"));
       
   265 	TBool timeoutOccurred;
       
   266 	do
       
   267 		{
       
   268 		ReceiveByteL(KShortTimeout, &timeoutOccurred);
       
   269 		if (timeoutOccurred)
       
   270 			{
       
   271 			Progress(_L("Timed out\r\n"));
       
   272 			}
       
   273 		else
       
   274 			{
       
   275 			Progress(_L("Received byte\r\n"));
       
   276 			}
       
   277 		}
       
   278 		while (!timeoutOccurred);
       
   279 	}
       
   280 
       
   281 void CCmdXmodem::SendL(const TDesC& aData)
       
   282 	{
       
   283 	Progress(_L("Sending:\r\n"));
       
   284 	Dump(aData);
       
   285 	iStarted = ETrue;
       
   286 
       
   287 	if (iTelnetMode & ESendBinary)
       
   288 		{
       
   289 		const TInt numChars = aData.Length();
       
   290 		TInt numIacs = 0;
       
   291 		for (TInt i = 0; i < numChars; ++i)
       
   292 			{
       
   293 			if (aData[i] == KByteTelnetIac)
       
   294 				{
       
   295 				++numIacs;
       
   296 				}
       
   297 			}
       
   298 		if (numIacs > 0)
       
   299 			{
       
   300 			Progress(_L("Escaping %d IACs\r\n"), numIacs);
       
   301 			HBufC* newBuf = HBufC::NewLC(aData.Length() + numIacs);
       
   302 			TPtr newBufPtr(newBuf->Des());
       
   303 			for (TInt i = 0; i < numChars; ++i)
       
   304 				{
       
   305 				if (aData[i] == KByteTelnetIac)
       
   306 					{
       
   307 					newBufPtr.Append(KByteTelnetIac);
       
   308 					}
       
   309 				newBufPtr.Append(aData[i]);
       
   310 				}
       
   311 			Dump(*newBuf);
       
   312 			User::LeaveIfError(Stdout().Write(*newBuf));
       
   313 			CleanupStack::PopAndDestroy(newBuf);
       
   314 			}
       
   315 		else
       
   316 			{
       
   317 			User::LeaveIfError(Stdout().Write(aData));
       
   318 			}
       
   319 		}
       
   320 	else
       
   321 		{
       
   322 		User::LeaveIfError(Stdout().Write(aData));
       
   323 		}
       
   324 	}
       
   325 
       
   326 TInt CCmdXmodem::CheckSize() const
       
   327 	{
       
   328 	if (iCrc)
       
   329 		{
       
   330 		return 2;
       
   331 		}
       
   332 	else
       
   333 		{
       
   334 		return 1;
       
   335 		}
       
   336 	}
       
   337 
       
   338 TInt CCmdXmodem::ProtocolOverhead() const
       
   339 	{
       
   340 	return 3 + CheckSize();
       
   341 	}
       
   342 
       
   343 TBool CCmdXmodem::CheckBlock() const
       
   344 	{
       
   345 	TBool match(EFalse);
       
   346 
       
   347 	if (iCrc)
       
   348 		{
       
   349 		TUint16 crc = Crc16(iBuf->Mid(2, iBlockSize));
       
   350 		TUint16 receivedCrc = ((*iBuf)[iBlockSize + 2] << 8) + (*iBuf)[iBlockSize + 3];
       
   351 		match = (crc == receivedCrc);
       
   352 		if (!match)
       
   353 			{
       
   354 			Progress(_L("CRC failed (calculated %u, received %u)\r\n"), crc, receivedCrc);
       
   355 			}
       
   356 		}
       
   357 	else
       
   358 		{
       
   359 		TUint8 sum = 0;
       
   360 		for (TInt i = 2; i < (iBlockSize + 2); ++i)	// Plus 2 because iBuf contains the two packet number bytes.
       
   361 			{
       
   362 			sum += (*iBuf)[i];
       
   363 			}
       
   364 		TUint8 received = (*iBuf)[iBlockSize + 2];
       
   365 		match = (sum == received);
       
   366 		if (!match)
       
   367 			{
       
   368 			Progress(_L("CheckSum failed (calculated %u, received %u)\r\n"), sum, received);
       
   369 			}
       
   370 		}
       
   371 
       
   372 	return match;
       
   373 	}
       
   374 
       
   375 void CCmdXmodem::Progress(TRefByValue<const TDesC> aFmt, ...) const
       
   376 	{
       
   377 	if (iVerbose)
       
   378 		{
       
   379 		TOverflowTruncate overflow;
       
   380 		VA_LIST list;
       
   381 		VA_START(list, aFmt);
       
   382 		TBuf<0x100> buf;
       
   383 		buf.AppendFormatList(aFmt, list, &overflow);
       
   384 		const_cast<CCmdXmodem*>(this)->Stderr().Write(buf);
       
   385 		}
       
   386 	}
       
   387 
       
   388 void CCmdXmodem::Dump(const TDesC& aData)
       
   389 	{
       
   390 	if (iVerbose)
       
   391 		{
       
   392 		TBuf<80> out;
       
   393 		TBuf<16> ascii;
       
   394 		TInt dataIndex = 0;
       
   395 		TInt pos = 0;
       
   396 		do
       
   397 			{
       
   398 			out.Zero();
       
   399 			ascii.Zero();
       
   400 			out.AppendNumFixedWidthUC(pos, EHex, 8);
       
   401 			out.Append(_L(": "));
       
   402 			for (TInt i = 0; i < 16; ++i)
       
   403 				{
       
   404 				if (dataIndex < aData.Length())
       
   405 					{
       
   406 					TUint8 byte = (TUint8)aData[dataIndex++];
       
   407 					out.AppendNumFixedWidthUC(byte, EHex, 2);
       
   408 					out.Append(_L(" "));
       
   409 					if ((byte < 0x20) || (byte >= 0x7f) || byte == '%')
       
   410 						{
       
   411 						byte = '.';
       
   412 						}
       
   413 					ascii.Append(TChar(byte));
       
   414 					++pos;
       
   415 					}
       
   416 				else
       
   417 					{
       
   418 					out.Append(_L("   "));
       
   419 					}
       
   420 				}
       
   421 			out.Append(ascii);
       
   422 			out.Append(_L("\r\n"));
       
   423 			Stderr().Write(out);
       
   424 			}
       
   425 			while (dataIndex < aData.Length());
       
   426 		}
       
   427 	}
       
   428 
       
   429 void CCmdXmodem::Abort()
       
   430 	{
       
   431 	TRAP_IGNORE(
       
   432 				SendL(KLitCancel);
       
   433 				PurgeInputL();
       
   434 				);
       
   435 	}
       
   436 
       
   437 void CCmdXmodem::WaitForSyncL()
       
   438 	{
       
   439 	Progress(_L("Waiting for sync...\r\n"));
       
   440 	for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries)
       
   441 		{
       
   442 		switch (ReceiveByteL(KLongTimeout))
       
   443 			{
       
   444 			case 'C':
       
   445 				Progress(_L("Receiver supports CRC\r\n"));
       
   446 				iCrc = ETrue;
       
   447 				return;
       
   448 			case KByteNak:
       
   449 				Progress(_L("Receiver does not support CRC\r\n"));
       
   450 				iCrc = EFalse;
       
   451 				return;
       
   452 			case KByteCan:
       
   453 				Progress(_L("Receiver cancelled\r\n"));
       
   454 				SendL(KLitAck);
       
   455 				User::Leave(KErrCancel);
       
   456 				break;
       
   457 			case KByteTelnetIac:
       
   458 				HandleTelnetCommandL();
       
   459 				break;
       
   460 			default:
       
   461 				break;
       
   462 			}
       
   463 		}
       
   464 
       
   465 	Progress(_L("Failed to sync\r\n"));
       
   466 	SendL(KLitCancel);
       
   467 	User::Leave(KErrCancel);
       
   468 	}
       
   469 
       
   470 CCmdXmodem::TSyncResult CCmdXmodem::SendSyncL()
       
   471 	{
       
   472 	Progress(_L("Sending sync...\r\n"));
       
   473 	iCrc = ETrue;
       
   474 	const TDesC* syncLit = &KLitC();
       
   475 	FOREVER
       
   476 		{
       
   477 		TBool skipNextSync(EFalse);
       
   478 		for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries)
       
   479 			{
       
   480 			if (skipNextSync)
       
   481 				{
       
   482 				skipNextSync = EFalse;
       
   483 				}
       
   484 			else
       
   485 				{
       
   486 				SendL(*syncLit);
       
   487 				}
       
   488 			switch (ReceiveByteL(KMediumTimeout))
       
   489 				{
       
   490 				case KByteSoh:
       
   491 					Progress(_L("Using 128 byte block size\r\n"));
       
   492 					iBlockSize = KSmallBlockSize;
       
   493 					return ENormal;
       
   494 				case KByteStx:
       
   495 					Progress(_L("Using 1024 byte block size\r\n"));
       
   496 					iBlockSize = KLargeBlockSize;
       
   497 					return ENormal;
       
   498 				case KByteCan:
       
   499 					Progress(_L("Sender cancelled\r\n"));
       
   500 					SendL(KLitAck);
       
   501 					PurgeInputL();
       
   502 					User::Leave(KErrCancel);
       
   503 					break;
       
   504 				case KByteEot:
       
   505 					Progress(_L("Received EOT\r\n"));
       
   506 					SendL(KLitAck);
       
   507 					return EEot;
       
   508 				case KByteTelnetIac:
       
   509 					HandleTelnetCommandL();
       
   510 					--numRetries;
       
   511 					skipNextSync = ETrue; // This is to avoid HyperTerminal complaining about an "Unexpected response".
       
   512 					break;
       
   513 				default:
       
   514 					break;
       
   515 				}
       
   516 			}
       
   517 		if (syncLit == &KLitC)
       
   518 			{
       
   519 			Progress(_L("Sender doesn't support CRC\r\n"));
       
   520 			syncLit = &KLitNak;
       
   521 			iCrc = EFalse;
       
   522 			}
       
   523 		else
       
   524 			{
       
   525 			Progress(_L("Failed to sync\r\n"));
       
   526 			SendL(KLitCancel);
       
   527 			User::Leave(KErrCancel);
       
   528 			}
       
   529 		}
       
   530 	}
       
   531 
       
   532 TBool IsValidTelnetCommand(TChar aChar)
       
   533 	{
       
   534 	return (aChar == KByteTelnetDo) || (aChar == KByteTelnetWill);
       
   535 	}
       
   536 
       
   537 void CCmdXmodem::HandleTelnetCommandL()
       
   538 	{
       
   539 	Progress(_L("Received (what looks like) the start of a Telnet command\r\n"));
       
   540 	TBuf<1> buf;
       
   541 	TBool timedOut(EFalse);
       
   542 	ReceiveWithTimeoutL(buf, KShortTimeout, &timedOut);
       
   543 	if (!timedOut)
       
   544 		{
       
   545 		TChar command = buf[0];
       
   546 		if (IsValidTelnetCommand(command))
       
   547 			{
       
   548 			Progress(_L("Valid telnet command received\r\n"));
       
   549 			timedOut = EFalse;
       
   550 			ReceiveWithTimeoutL(buf, KShortTimeout, &timedOut);
       
   551 			if (timedOut)
       
   552 				{
       
   553 				Progress(_L("Timed-out, ungetting last character... (2)\r\n"));
       
   554 				Unget(command);
       
   555 				}
       
   556 			else if (buf[0] == KByteTelnetBinaryMode)
       
   557 				{
       
   558 				if (command == KByteTelnetDo)
       
   559 					{
       
   560 					iTelnetMode &= (~ESendBinary);
       
   561 					SendL(KLitTelnetWillBinaryMode);
       
   562 					iTelnetMode |= ESendBinary;
       
   563 					Progress(_L("Set ESendBinary\r\n"));
       
   564 					}
       
   565 				else if (command == KByteTelnetWill)
       
   566 					{
       
   567 					TBool enableSend = iTelnetMode & ESendBinary;
       
   568 					iTelnetMode &= (~ESendBinary);
       
   569 					SendL(KLitTelnetDoBinaryMode);
       
   570 					iTelnetMode |= ERecvBinary;
       
   571 					Progress(_L("Set ERecvBinary\r\n"));
       
   572 					if (enableSend)
       
   573 						{
       
   574 						iTelnetMode |= ESendBinary;
       
   575 						}
       
   576 					}
       
   577 				else
       
   578 					{
       
   579 					ASSERT(EFalse);
       
   580 					}
       
   581 				}
       
   582 			else
       
   583 				{
       
   584 				Progress(_L("Unknown command option, ungetting last two characters...\r\n"));
       
   585 				Unget(command);
       
   586 				Unget(buf[0]);
       
   587 				}
       
   588 			}
       
   589 		else
       
   590 			{
       
   591 			Progress(_L("Timed-out, ungetting last character... (1)\r\n"));
       
   592 			Unget(command);
       
   593 			}
       
   594 		}
       
   595 	Progress(_L("Telnet command handled - resuming...\r\n"));
       
   596 	}
       
   597 
       
   598 void CCmdXmodem::Unget(TChar aChar)
       
   599 	{
       
   600 	ASSERT(iUngetBuf.Length() <= 1);
       
   601 	iUngetBuf.Append(aChar);
       
   602 	}
       
   603 
       
   604 void CCmdXmodem::PrepareConsoleToTransferL()
       
   605 	{
       
   606 	Write(_L("Please start the file transfer in your terminal...\r\n"));
       
   607 	User::LeaveIfError(Stdin().CaptureAllKeys());			// To prevent other things (like fshell) interpreting binary data as special key presses (like cntrl-C).
       
   608 	LeaveIfErr(Stdin().SetMode(RIoReadWriteHandle::EBinary), _L("Unable to set stdin to binary mode"));	// To prevent vt100cons from scanning for ANSI escape sequences. 
       
   609 	LeaveIfErr(Stdout().SetMode(RIoReadWriteHandle::EBinary), _L("Unable to set stdout to binary mode")); // To tell iosrv to not mess about with line endings.
       
   610 	}
       
   611 
       
   612 void CCmdXmodem::CleanupClonsoleAfterTransferL()
       
   613 	{
       
   614 	PurgeInputL();
       
   615 	LeaveIfErr(Stdin().SetMode(RIoReadWriteHandle::EText), _L("Unable to set stdin back to text mode"));
       
   616 	LeaveIfErr(Stdout().SetMode(RIoReadWriteHandle::EText), _L("Unable to set stdout back to text mode"));
       
   617 	if (Stdout().AttachedToConsole())
       
   618 		{
       
   619 		RIoConsoleWriteHandle stdout = Stdout();
       
   620 		TPoint pos(stdout.GetCursorPosL());
       
   621 		stdout.SetCursorPosAbsL(TPoint(0, pos.iY));
       
   622 		stdout.ClearToEndOfLineL();
       
   623 		}
       
   624 	}
       
   625 
       
   626 void CCmdXmodem::SendBlockL(const TDesC& aBlock)
       
   627 	{
       
   628 	Progress(_L("Sending block...\r\n"));
       
   629 	ASSERT(aBlock.Length() <= iBlockSize);
       
   630 
       
   631 	if (iBuf == NULL)
       
   632 		{
       
   633 		iBuf = HBufC::NewL(iBlockSize + ProtocolOverhead());
       
   634 		}
       
   635 
       
   636 	TPtr buf(iBuf->Des());
       
   637 
       
   638 	FOREVER
       
   639 		{
       
   640 		buf.Zero();
       
   641 		buf.Append((TUint16)KByteSoh);
       
   642 		buf.Append((TUint16)iPacketNumber);
       
   643 		buf.Append((TUint16)((TUint8)(~iPacketNumber)));
       
   644 		buf.Append(aBlock);
       
   645 		if (aBlock.Length() < iBlockSize)
       
   646 			{
       
   647 			// There's not enough data to fill this block, so pad with SUB.
       
   648 			buf.AppendFill((TUint16)KByteSub, iBlockSize - aBlock.Length());
       
   649 			}
       
   650 		if (iCrc)
       
   651 			{
       
   652 			TUint16 crc = Crc16(buf.Mid(3, iBlockSize));
       
   653 			buf.Append((crc >> 8) & 0xFF);
       
   654 			buf.Append(crc & 0xFF);
       
   655 			}
       
   656 		else
       
   657 			{
       
   658 			TUint8 sum = 0;
       
   659 			for (TInt i = 3; i < (iBlockSize + 3); ++i)
       
   660 				{
       
   661 				sum += (*iBuf)[i];
       
   662 				}
       
   663 			buf.Append((TUint16)sum);
       
   664 			}
       
   665 
       
   666 		Progress(_L("Sending block %d\r\n"), iPacketNumber);
       
   667 
       
   668 		for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries)
       
   669 			{
       
   670 			SendL(*iBuf);
       
   671 			TBool timedOut(EFalse);
       
   672 			TUint8 byte = ReceiveByteL(KLongTimeout, &timedOut);
       
   673 			if (timedOut)
       
   674 				{
       
   675 				User::Leave(KErrTimedOut);
       
   676 				}
       
   677 			switch (byte) 
       
   678 				{
       
   679 				case KByteAck:
       
   680 					Progress(_L("Block %d successfully sent\r\n"), iPacketNumber);
       
   681 					++iPacketNumber;
       
   682 					return;
       
   683 				case KByteCan:
       
   684 					Progress(_L("Receiver cancelled\r\n"));
       
   685 					SendL(KLitAck);
       
   686 					PurgeInputL();
       
   687 					User::Leave(KErrCancel);
       
   688 					break;
       
   689 				case KByteNak:
       
   690 					Progress(_L("Block NAK'd, sending again...\r\n"));
       
   691 					break;
       
   692 				default:
       
   693 					Progress(_L("Unexpected response (0x%x), sending again...\r\n"), byte);
       
   694 					break;
       
   695 				}
       
   696 			}
       
   697 		}
       
   698 	}
       
   699 
       
   700 TPtrC CCmdXmodem::ReceiveBlockL(TBool aIsFirstBlock, TBool& aIsFinalBlock)
       
   701 	{
       
   702 	_LIT(KFirst, "first ");
       
   703 	Progress(_L("Receiving %Sblock...\r\n"), aIsFirstBlock ? &KFirst : &KNullDesC);
       
   704 	TPtrC ret(NULL, 0);
       
   705 	TInt repeats = 0;
       
   706 	aIsFinalBlock = EFalse;
       
   707 
       
   708 	if (iBuf == NULL)
       
   709 		{
       
   710 		iBuf = HBufC::NewL(iBlockSize + ProtocolOverhead());
       
   711 		}
       
   712 	if (!aIsFirstBlock)	// The first byte of the first block should have already been read by SendSyncL.
       
   713 		{
       
   714 again:
       
   715 		iBuf->Des().Zero();
       
   716 		TInt numRetries;
       
   717 		for (numRetries = 0; numRetries < KMaxRetries; ++numRetries)
       
   718 			{
       
   719 			TBool startReceiving(EFalse);
       
   720 			Progress(_L("Receiving first byte of block...\r\n"));
       
   721 			TBool timedOut(EFalse);
       
   722 			TUint8 c = ReceiveByteL(KMediumTimeout, &timedOut);
       
   723 			if (timedOut)
       
   724 				{
       
   725 				Progress(_L("Timed out - sending NAK...\r\n"));
       
   726 				SendL(KLitNak);
       
   727 				}
       
   728 			else
       
   729 				{
       
   730 				switch (c)
       
   731 					{
       
   732 					case KByteSoh:
       
   733 						if (iBlockSize != KSmallBlockSize)
       
   734 							{
       
   735 							Progress(_L("Block size set to 128 bytes\r\n"));
       
   736 							iBlockSize = KSmallBlockSize;
       
   737 							}
       
   738 						startReceiving = ETrue;
       
   739 						break;
       
   740 					case KByteStx:
       
   741 						if (iBlockSize != KLargeBlockSize)
       
   742 							{
       
   743 							Progress(_L("Block size set to 1024 bytes\r\n"));
       
   744 							iBlockSize = KLargeBlockSize;
       
   745 							}
       
   746 						startReceiving = ETrue;
       
   747 						break;
       
   748 					case KByteEot:
       
   749 						Progress(_L("Last block received\r\n"));
       
   750 						aIsFinalBlock = ETrue;
       
   751 						SendL(KLitAck);
       
   752 						return ret;
       
   753 					case KByteCan:
       
   754 						Progress(_L("Sender cancelled\r\n"));
       
   755 						SendL(KLitAck);
       
   756 						PurgeInputL();
       
   757 						User::Leave(KErrCancel);
       
   758 						break;
       
   759 					default:
       
   760 						break;
       
   761 					}
       
   762 
       
   763 				if (startReceiving)
       
   764 					{
       
   765 					break;
       
   766 					}
       
   767 				}
       
   768 			}
       
   769 
       
   770 		if (numRetries == KMaxRetries)
       
   771 			{
       
   772 			Progress(_L("Failed to receive valid block\r\n"));
       
   773 			User::Leave(KErrCommsLineFail);
       
   774 			}
       
   775 		}
       
   776 
       
   777 	if (iBuf->Des().MaxLength() < (iBlockSize + ProtocolOverhead()))
       
   778 		{
       
   779 		Progress(_L("Reallocating buffer from %d to %d bytes\r\n"), iBuf->Des().MaxLength(), iBlockSize + ProtocolOverhead());
       
   780 		iBuf = iBuf->ReAllocL(iBlockSize + ProtocolOverhead());
       
   781 		}
       
   782 
       
   783 	Progress(_L("Receiving remainder of block...\r\n"));
       
   784 	TPtr ptr(const_cast<TUint16*>(iBuf->Ptr()), 0, iBlockSize + ProtocolOverhead() - 1); // - 1 because the first byte of the header isn't in the buffer. Note, can't use HBufC::Des because the max length of the resulting TPtr could be larger than the amount of data we are expecting.
       
   785 	TBool timeoutOccurred;
       
   786 	ReceiveWithTimeoutL(ptr, KLongTimeout, &timeoutOccurred);
       
   787 	if (timeoutOccurred)
       
   788 		{
       
   789 		Progress(_L("Timed out, retrying...\r\n"));
       
   790 		SendL(KLitNak);
       
   791 		PurgeInputL();
       
   792 		goto again;
       
   793 		}
       
   794 	iBuf->Des().SetLength(ptr.Length());
       
   795 
       
   796 	TUint8 b0 = (*iBuf)[0];
       
   797 	TUint8 b1 = (*iBuf)[1];
       
   798 	// workaround for a compiler bug: and b0 and ~b1 with 0xff, otherwise some compilers will compare
       
   799 	// the full 32-bit values, the top 3 bytes of which contain garbage. Observed with WINSW and GCCE
       
   800 	// compilers.
       
   801 	if (((b0 & 0xff) == ((~b1) & 0xff)) && ((b0 == iPacketNumber) || (b0 == (iPacketNumber - 1))) && CheckBlock())
       
   802 		{
       
   803 		Progress(_L("Block successfully received\r\n"));
       
   804 		if ((*iBuf)[0] == iPacketNumber)
       
   805 			{
       
   806 			Progress(_L("Packet number matched (%d)\r\n"), iPacketNumber);
       
   807 			ret.Set(iBuf->Mid(2, iBuf->Length() - CheckSize() - 2));
       
   808 			++iPacketNumber;
       
   809 			}
       
   810 		repeats = 0;
       
   811 		SendL(KLitAck);
       
   812 		}
       
   813 	else if (++repeats >= KMaxRetries)
       
   814 		{
       
   815 		Progress(_L("Block not successfully received after %d retries, cancelling...\r\n"), KMaxRetries);
       
   816 		SendL(KLitCancel);
       
   817 		PurgeInputL();
       
   818 		User::Leave(KErrCancel);
       
   819 		}
       
   820 	else
       
   821 		{
       
   822 		Progress(_L("Block not successfully received, retrying...\r\n"));
       
   823 		SendL(KLitNak);
       
   824 		goto again;
       
   825 		}
       
   826 
       
   827 	return ret;
       
   828 	}
       
   829 
       
   830 void CCmdXmodem::SendTerminateL()
       
   831 	{
       
   832 	for (TInt numRetries = 0; numRetries < KMaxRetries; ++numRetries)
       
   833 		{
       
   834 		SendL(KLitEot);
       
   835 		if (ReceiveByteL(KMediumTimeout) == KByteAck)
       
   836 			{
       
   837 			return;
       
   838 			}
       
   839 		}
       
   840 	PurgeInputL();
       
   841 	User::Leave(KErrCompletion);
       
   842 	}
       
   843 
       
   844 void CCmdXmodem::SendStdinL()
       
   845 	{
       
   846 	HBufC* buf = HBufC::NewLC(iBlockSize);
       
   847 	TPtr bufPtr(buf->Des());
       
   848 
       
   849 	while (Stdin().Read(bufPtr) == KErrNone)
       
   850 		{
       
   851 		SendBlockL(*buf);
       
   852 		bufPtr.Zero();
       
   853 		}
       
   854 	SendTerminateL();
       
   855 
       
   856 	CleanupStack::PopAndDestroy(buf);
       
   857 	}
       
   858 
       
   859 void CCmdXmodem::SendFileL(const TDesC& aFileName)
       
   860 	{
       
   861 	Progress(_L("Sending file \"%S\"...\r\n"), &aFileName);
       
   862 	RFile file;
       
   863 	User::LeaveIfError(file.Open(FsL(), aFileName, EFileRead));
       
   864 	CleanupClosePushL(file);
       
   865 
       
   866 	HBufC8* buf = HBufC8::NewLC(iBlockSize);
       
   867 	HBufC* wideBuf = HBufC::NewLC(iBlockSize);
       
   868 	TPtr8 bufPtr(buf->Des());
       
   869 	while ((file.Read(bufPtr, iBlockSize) == KErrNone) && (buf->Length() > 0))
       
   870 		{
       
   871 		wideBuf->Des().Copy(*buf);
       
   872 		SendBlockL(*wideBuf);
       
   873 		}
       
   874 	SendTerminateL();
       
   875 
       
   876 	CleanupStack::PopAndDestroy(3, &file);
       
   877 	}
       
   878 
       
   879 void CCmdXmodem::ReceiveToStdoutL()
       
   880 	{
       
   881 	TBool firstBlock(ETrue);
       
   882 	TBool finalBlock(EFalse);
       
   883 	while (!finalBlock)
       
   884 		{
       
   885 		TPtrC block(ReceiveBlockL(firstBlock, finalBlock));
       
   886 		User::LeaveIfError(Stdout().Write(block));
       
   887 		firstBlock = EFalse;
       
   888 		}
       
   889 	}
       
   890 
       
   891 void CCmdXmodem::ReceiveToFileL(const TDesC& aFileName)
       
   892 	{
       
   893 	RFile file;
       
   894 	if (iOverwrite)
       
   895 		{
       
   896 		FsL().MkDirAll(aFileName); // Create the directory if it doesn't already exist
       
   897 		User::LeaveIfError(file.Replace(FsL(), aFileName, EFileWrite));
       
   898 		}
       
   899 	else
       
   900 		{
       
   901 		User::LeaveIfError(file.Create(FsL(), aFileName, EFileWrite));
       
   902 		}
       
   903 	CleanupClosePushL(file);
       
   904 
       
   905 	HBufC8* narrowBuf = HBufC8::NewLC(iBlockSize);
       
   906 	TPtr8 narrowBufPtr(narrowBuf->Des());
       
   907 	TBool firstBlock(ETrue);
       
   908 	TBool finalBlock(EFalse);
       
   909 	while (!finalBlock)
       
   910 		{
       
   911 		TPtrC block(ReceiveBlockL(firstBlock, finalBlock));
       
   912 		firstBlock = EFalse;
       
   913 		if (block.Length() > narrowBufPtr.MaxLength())
       
   914 			{
       
   915 			HBufC8* newNarrowBuf = narrowBuf->ReAllocL(iBuf->Length());
       
   916 			CleanupStack::Pop(narrowBuf);
       
   917 			narrowBuf = newNarrowBuf;
       
   918 			CleanupStack::PushL(narrowBuf);
       
   919 			narrowBufPtr.Set(narrowBuf->Des());
       
   920 			}
       
   921 		narrowBufPtr.Copy(block);
       
   922 		User::LeaveIfError(file.Write(*narrowBuf));
       
   923 		narrowBufPtr.Zero();
       
   924 		}
       
   925 
       
   926 	CleanupStack::PopAndDestroy(2, &file);
       
   927 	}
       
   928 
       
   929 void CCmdXmodem::ReceiveToNullL()
       
   930 	{
       
   931 	TBool firstBlock(EFalse);
       
   932 	TBool finalBlock(EFalse);
       
   933 	while (!finalBlock)
       
   934 		{
       
   935 		ReceiveBlockL(firstBlock, finalBlock);
       
   936 		firstBlock = EFalse;
       
   937 		}
       
   938 	}
       
   939 
       
   940 const TDesC& CCmdXmodem::Name() const
       
   941 	{
       
   942 	_LIT(KName, "xmodem");	
       
   943 	return KName;
       
   944 	}
       
   945 
       
   946 void CCmdXmodem::DoRunL()
       
   947 	{
       
   948 	PrepareConsoleToTransferL();
       
   949 
       
   950 	if (iMode == EReceive)
       
   951 		{
       
   952 		if (iFileName.Length() > 0)
       
   953 			{
       
   954 			LeaveIfFileExists(iFileName);
       
   955 			}
       
   956 		TSyncResult syncResult = SendSyncL();
       
   957 		if (syncResult == ENormal)
       
   958 			{
       
   959 			if (iFileName.Length() > 0)
       
   960 				{
       
   961 				ReceiveToFileL(iFileName);
       
   962 				}
       
   963 			else
       
   964 				{
       
   965 				ReceiveToStdoutL();
       
   966 				}
       
   967 
       
   968 			CleanupClonsoleAfterTransferL();
       
   969 			if (iFileName.Length() > 0)
       
   970 				{
       
   971 				Printf(_L("Successfully received \"%S\".\r\n"), &iFileName);
       
   972 				}
       
   973 			}
       
   974 		}
       
   975 	else if (iMode == ESend)
       
   976 		{
       
   977 		if (iFileName.Length() > 0)
       
   978 			{
       
   979 			LeaveIfFileNotFound(iFileName);
       
   980 			}
       
   981 		WaitForSyncL();
       
   982 		if (iFileName.Length() > 0)
       
   983 			{
       
   984 			SendFileL(iFileName);
       
   985 			}
       
   986 		else
       
   987 			{
       
   988 			SendStdinL();
       
   989 			}
       
   990 
       
   991 		CleanupClonsoleAfterTransferL();
       
   992 		if (iFileName.Length() > 0)
       
   993 			{
       
   994 			Printf(_L("Successfully sent \"%S\".\r\n"), &iFileName);
       
   995 			}
       
   996 		else
       
   997 			{
       
   998 			Printf(_L("Successfully stdin.\r\n"));
       
   999 			}
       
  1000 		}
       
  1001 	User::LeaveIfError(Stdin().CancelCaptureAllKeys());
       
  1002 	}
       
  1003 
       
  1004 void CCmdXmodem::ArgumentsL(RCommandArgumentList& aArguments)
       
  1005 	{
       
  1006 	_LIT(KArg1, "mode");
       
  1007 	aArguments.AppendEnumL((TInt&)iMode, KArg1);
       
  1008 	_LIT(KArg2, "file_name");
       
  1009 	aArguments.AppendFileNameL(iFileName, KArg2);
       
  1010 	}
       
  1011 
       
  1012 void CCmdXmodem::OptionsL(RCommandOptionList& aOptions)
       
  1013 	{
       
  1014 	_LIT(KOptVerbose, "verbose");
       
  1015 	aOptions.AppendBoolL(iVerbose, KOptVerbose);
       
  1016 
       
  1017 	_LIT(KOptOverwrite, "overwrite");
       
  1018 	aOptions.AppendBoolL(iOverwrite, KOptOverwrite);
       
  1019 	}
       
  1020 
       
  1021 void CCmdXmodem::HandleLeave(TInt aError)
       
  1022 	{
       
  1023 	if (iStarted)
       
  1024 		{
       
  1025 		Abort();
       
  1026 		CleanupClonsoleAfterTransferL();
       
  1027 		}
       
  1028 	CCommandBase::HandleLeave(aError);
       
  1029 	}