// wget.cpp
// 
// Copyright (c) 2008 - 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
// 
// Initial Contributors:
// Accenture - Initial contribution
//

// TODO: byte-range support

#include <commdbconnpref.h>
#include <httpstringconstants.h>
#include <httperr.h>
#include <mhttpdatasupplier.h>
#include <rhttpheaders.h>
#include <thttphdrfielditer.h>
#include <bautils.h>
#include "wget.h"

using namespace IoUtils;

_LIT8(KHttpProtocol,	"HTTP/TCP");
_LIT(KDateFormat,		"%D%M%Y%/0%1%/1%2%/2%3%/3 %:0%H%:1%T%:2%S.%C%:3");
const TInt KMaxHeaderNameLen = 32;
const TInt KMaxHeaderValueLen = 128;

CCommandBase* CCmdWget::NewLC()
	{
	CCmdWget* self = new(ELeave) CCmdWget();
	CleanupStack::PushL(self);
	self->BaseConstructL();
	return self;
	}

CCmdWget::~CCmdWget()
	{
	delete iUrl;
	delete iUsername;
	delete iPassword;
	if (iSessionIsOpen)
		{
		iSession.Close(); // closes iTransaction also
		}
	if (iConnection.SubSessionHandle() > 0)
		{
		iConnection.Close();
		}
	if (iSocketServ.Handle() > 0)
		{
		iSocketServ.Close();
		}
	if (iLocalFile.SubSessionHandle() > 0)
		{
		iLocalFile.Flush();
		iLocalFile.Close();
		}
	}

CCmdWget::CCmdWget() : CCommandBase(CCommandBase::EManualComplete)
	{
	}

const TDesC& CCmdWget::Name() const
	{
	_LIT(KName, "wget");
	return KName;
	}

void CCmdWget::DoRunL()
	{
	// sanity check parameters
	if (!iUrl || iUrl->Des().Length() > KMaxName)
		{
		if (iVerbose)
			{
			PrintError(KErrArgument, _L("Url not specified or is too large."));
			}
		User::Leave(KErrArgument);
		}		
	if ((iUsername && iUsername->Des().Length() > KMaxName) || (iPassword && iPassword->Des().Length() > KMaxName))
		{
		if (iVerbose)
			{
			PrintError(KErrArgument, _L("Username or Password is too large."));
			}
		User::Leave(KErrArgument);
		}

	if (iUploadFilename.Length())
		{
		iPostData = ETrue; // Keep this member variable because lots of code assumes it
		}

	LaunchConnectionL(); // start a connection - KErrNone returned if successfully started or a compatible connection already exists
		
	if (iPostData)
		{
		// http post
		PrepareUploadFileL();
		}
	else
		{
		// http get
		PrepareDownloadFileL(); // prep. the local file to receive the downloaded data
		}
	User::LeaveIfError(iLocalFile.Size(iLocalFileSize));
	ConfigureHTTPL(); // setup http session & configure with connection parameters
	LaunchHTTPTransactionL(); // launch a http transaction
	}

void CCmdWget::ArgumentsL(RCommandArgumentList& aArguments)
	{
	_LIT(KArg1, "url");
	aArguments.AppendStringL(iUrl, KArg1);
	}

void CCmdWget::OptionsL(RCommandOptionList& aOptions)
	{
	_LIT(KOptContinue, "continue");
	aOptions.AppendBoolL(iContinue, KOptContinue);

	_LIT(KOptDestinationFilename, "downloadfile");
	aOptions.AppendFileNameL(iDestinationFilename, KOptDestinationFilename);

	_LIT(KOptSourceFilename, "uploadfile");
	aOptions.AppendFileNameL(iUploadFilename, KOptSourceFilename);

	_LIT(KOptIapId, "iap");
	aOptions.AppendIntL(iIapId, KOptIapId);

	_LIT(KOptUsername, "username");
	aOptions.AppendStringL(iUsername, KOptUsername);

	_LIT(KOptPassword, "password");
	aOptions.AppendStringL(iPassword, KOptPassword);
	
	_LIT(KOptVerbose, "verbose");
	aOptions.AppendBoolL(iVerbose, KOptVerbose);
	}

//
// CCmdWget::PrepareDownloadFileL
// determine the destination file & path and open a handle to a local file
//
void CCmdWget::PrepareDownloadFileL()
	{
	ASSERT(iUrl);
	ASSERT(!iPostData);
	TFileName fname;
	RFs& fs = FsL(); 
	fname.Zero();
	if (iDestinationFilename.Length() <= 0)
		{
		// use current path
		fname.Append(Env().Pwd());
		
		// use the same name as the file we're intending to download
		TChar dirMarker('/');
		TInt mark = iUrl->Des().LocateReverse(dirMarker);
		if (mark <= 0)
			{
			User::Leave(KErrNotFound);
			}
		fname.Append(iUrl->Des().Mid(++mark)); // increment mark to step over the '/' 
		}
	else 
		{
		fname.Copy(iDestinationFilename); // user has specified a local file name
		}
	if (BaflUtils::FileExists(fs, fname))
		{
		if (iContinue)
			{
			// open the file & append any new data to what's already in there
			User::LeaveIfError(iLocalFile.Open(fs, fname, EFileShareExclusive));
			}
		else
			{
			// open the file & replace old data with new data
			User::LeaveIfError(iLocalFile.Replace(fs, fname, EFileShareExclusive));
			}
		}
	else
		{
		// create the file for the first time
		User::LeaveIfError(iLocalFile.Create(fs, fname, EFileShareExclusive));
		}
	}

//
// CCmdWget::PrepareUploadFileL
// prep. the local file containing data for upload to the web server
//
void CCmdWget::PrepareUploadFileL()
	{
	ASSERT(iUrl);
	ASSERT(iPostData);
	
	User::LeaveIfError(iLocalFile.Open(FsL(), iUploadFilename, EFileRead | EFileShareReadersOnly));
		
	
	}

//
// CCmdWget::LaunchConnectionL
// establish a connection to the network
//
void CCmdWget::LaunchConnectionL()
	{
	User::LeaveIfError(iSocketServ.Connect());
	User::LeaveIfError(iConnection.Open(iSocketServ));
	
	TInt connError = KErrNone;	
	TCommDbConnPref prefs;
	if (iIapId > 0)
		{
		prefs.SetIapId(iIapId);
		prefs.SetDialogPreference(ECommDbDialogPrefDoNotPrompt);
		connError = iConnection.Start(prefs);
		}
	else
		{
		connError = iConnection.Start();
		}
	if (iVerbose && (connError != KErrNone))
		{
		PrintError(connError, _L("Unable to establish network connection"));
		User::Leave(connError);
		}	
	}

//
// CCmdWget::ConfigureHTTPL
//
void CCmdWget::ConfigureHTTPL()
	{
	iSession.OpenL(KHttpProtocol);
	iSessionIsOpen = ETrue;
	iSession.SetSessionEventCallback(this);
	RStringPool pool = iSession.StringPool();
	RHTTPConnectionInfo conInf = iSession.ConnectionInfo();
	conInf.SetPropertyL(pool.StringF(HTTP::EHttpSocketServ, RHTTPSession::GetTable()), THTTPHdrVal(iSocketServ.Handle()));
	conInf.SetPropertyL(pool.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable()), THTTPHdrVal(reinterpret_cast<TInt> (&iConnection)));
	InstallAuthenticationL(iSession);
	}

//
// CCmdWget::LaunchHTTPTransactionL
// launches a new http transaction
//
void CCmdWget::LaunchHTTPTransactionL()
	{
	// create the transaction
	TBuf8<KMaxName> url8;
	url8.Copy(iUrl->Des());
	TUriParser8 uri;
	User::LeaveIfError(uri.Parse(url8));
	RStringPool pool = iSession.StringPool();
	RStringF method = pool.StringF(HTTP::EGET,RHTTPSession::GetTable());
	if (iPostData)
		{
		method = pool.StringF(HTTP::EPOST,RHTTPSession::GetTable());
		}
	iTransaction = iSession.OpenTransactionL(uri, *this, method);
	
	// some transaction configuration is required for posting data
	if (iPostData)
		{
		RHTTPRequest req = iTransaction.Request();
		req.SetBody(*this); // ccmdwget to supply the data for posting
		THTTPHdrVal length(OverallDataSize());
		THTTPHdrVal type(pool.StringF(HTTP::ETextAny, RHTTPSession::GetTable()));
		RHTTPHeaders hdr = req.GetHeaderCollection();
		hdr.SetFieldL(pool.StringF(HTTP::EContentLength, RHTTPSession::GetTable()), length);
		hdr.SetFieldL(pool.StringF(HTTP::EContentType, RHTTPSession::GetTable()), type);
		}
	
	// launch it
	iTransaction.SubmitL();	
	}

//
// CCmdWget::GetCredentialsL
// request from the underlying HTTP session to provide it with some authentication credentials (if present)
//
TBool CCmdWget::GetCredentialsL(const TUriC8& /*aURI*/, RString aRealm, RStringF /*aAuthenticationType*/, RString& aUsername, RString& aPassword)
	{
	TBool present = EFalse;
	RStringPool pl = aRealm.Pool();
	if (iUsername)
		{
		present = ETrue;
		TBuf8<KMaxName> user;
		user.Copy(iUsername->Des());
		aUsername = pl.OpenStringL(user);
		}
	if (iPassword)
		{
		present = ETrue;
		TBuf8<KMaxName> pwd;
		pwd.Copy(iPassword->Des());
		aPassword = pl.OpenStringL(pwd);
		}
	return present;
	}
	
//
// CCmdWget::MHFSessionRunL
// handles any up-calls from the http session
//
void CCmdWget::MHFSessionRunL(const THTTPSessionEvent& aEvent)
	{
	switch (aEvent.iStatus)
		{
		case THTTPSessionEvent::EConnect:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Connect\r\n"));
				}
			}
		break;
		
		case THTTPSessionEvent::EConnectedOK:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Connected OK\r\n"));
				}
			}
		break;
		
		case THTTPSessionEvent::EDisconnect:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Disconnect\r\n"));
				}
			}
		break;
		
		case THTTPSessionEvent::EConnectedWithReducedCapabilities:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Connected with reduced capabilities\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EDisconnected:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Disconnected\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EAuthenticatedOK:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Authenticated OK\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EAuthenticationFailure:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Authentication failure\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EConnectionTimedOut:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Connection timed out\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::ENotConnected:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Not connected\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EExceptionInfo:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Exception info\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::ERedirected:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Redirected\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EAlreadyConnecting:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Already connecting\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EAlreadyConnected:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Already connected\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EAlreadyDisconnecting:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Already disconnecting\r\n"));
				}
			
			}
		break;

		case THTTPSessionEvent::EAlreadyDisconnected:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Already disconnected\r\n"));
				}
			
			}
		break;

		default:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Session Event unrecgonised\r\n"));
				}
			}
		break;
		};
	}

//
// CCmdWget::MHFSessionRunError
// handles an error related up-calls 
TInt CCmdWget::MHFSessionRunError(TInt aError, const THTTPSessionEvent& /*aEvent*/)
	{
	if (iVerbose)
		{
		PrintError(aError, _L("HTTP Session Error"));
		}
	return KErrNone;
	}
	
//
// CCmdWget::MHFRunL
// handles any up-calls regarding a current transaction
//
void CCmdWget::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
	{
	if (aTransaction.Id() != iTransaction.Id())
		{
		if (iVerbose)
			{
			PrintError(KErrUnknown, _L("HTTP Transaction Id is invalid"));
			}
		User::Leave(KErrUnknown);
		}
	switch (aEvent.iStatus)
		{
		case THTTPEvent::EGotResponseHeaders:
			{
			if (iVerbose)
				{
				DumpRespHeadersL(aTransaction);
				}
			}
		break;
		
		case THTTPEvent::EGotResponseBodyData:
			{
			RHTTPResponse response = aTransaction.Response();
			if (iVerbose)
				{
				Printf(_L("HTTP <%d> received\r\n"), response.StatusCode());
				}
			switch (response.StatusCode())
				{
				// informational codes (1xx)
				case HTTPStatus::EContinue:
				case HTTPStatus::ESwitchingProtocols:
				break;
				
				// successful codes (2xx)
				case HTTPStatus::EOk:
					{
					MHTTPDataSupplier* body = aTransaction.Response().Body();
					TPtrC8 data;
					TBool last = body->GetNextDataPart(data);
					if (!iPostData)
						{
						User::LeaveIfError(iLocalFile.Write(data));
						if (last)
							{
							iLocalFile.Flush();
							}
						if (iVerbose)
							{
							Printf(_L("bytes: %d\r\n"), data.Length());
							}
						}
					else
						{
						TBuf<512> woop;
						woop.Copy(data);
						Stdout().Write(woop);
						}
					body->ReleaseData();
					}
				break;
				
				case HTTPStatus::ECreated:
				case HTTPStatus::EAccepted:
				case HTTPStatus::ENonAuthoritativeInfo:
				case HTTPStatus::ENoContent:
				case HTTPStatus::EResetContent:
				break;
				
				case HTTPStatus::EPartialContent:
					{
					// TODO - byte range support
					}
				break;
				
				// Redirection codes (3xx)
				case HTTPStatus::EMultipleChoices:
				case HTTPStatus::EMovedPermanently:
				case HTTPStatus::EFound:
				case HTTPStatus::ESeeOther:
				case HTTPStatus::ENotModified:
				case HTTPStatus::EUseProxy:
				case HTTPStatus::EUnused:
				case HTTPStatus::ETemporaryRedirect:
					if (iVerbose)
						{
						Printf(_L("Redirecting\r\n"));
						}
					
				break;
				
				// Client error codes (4xx)
				case HTTPStatus::EBadRequest:
				case HTTPStatus::EUnauthorized:
				case HTTPStatus::EPaymentRequired:
				case HTTPStatus::EForbidden:
				case HTTPStatus::ENotFound:
				case HTTPStatus::EMethodNotAllowed:
				case HTTPStatus::ENotAcceptable:
				case HTTPStatus::EProxyAuthenticationRequired:
				case HTTPStatus::ERequestTimeout:
				case HTTPStatus::EConflict:
				case HTTPStatus::EGone:
				case HTTPStatus::ELengthRequired:
				case HTTPStatus::EPreconditionFailed:
				case HTTPStatus::ERequestEntityTooLarge:
				case HTTPStatus::ERequestURITooLong:
				case HTTPStatus::EUnsupportedMediaType:
				case HTTPStatus::ERequestedRangeNotSatisfiable:
				case HTTPStatus::EExpectationFailed:
					{
					if (iVerbose)
						{
						PrintError(KErrGeneral, _L("HTTP client error"));
						}
					Complete(KErrGeneral);
					}
				break;
				
				// Server error codes (5xx)
				case HTTPStatus::EInternalServerError:
				case HTTPStatus::ENotImplemented:
				case HTTPStatus::EBadGateway:
				case HTTPStatus::EServiceUnavailable:
				case HTTPStatus::EGatewayTimeout:
				case HTTPStatus::EHTTPVersionNotSupported:
					{
					if (iVerbose)
						{
						PrintError(KErrGeneral, _L("HTTP Remote Server Error"));
						}
					Complete(KErrGeneral);
					}
				break;

				default:
					Complete(KErrGeneral);
				break;
				};
			}
		break;
		
		case THTTPEvent::EResponseComplete:
			{
			if (iVerbose)
				{
				Printf(_L("HTTP Response complete.\r\n"));
				}
			}
		break;
		
		case THTTPEvent::ESucceeded:
			{
			if (iVerbose)
				{
				Printf(_L("Download transaction complete.\r\n"));
				}
			Complete(KErrNone);
			}
		break;
		
		case THTTPEvent::EFailed:
			{
			if (iVerbose)
				{
				Printf(_L("Download transaction failed. Aborting\r\n"));
				}
			Complete(KErrGeneral);			
			}
		break;
		
		case THTTPEvent::ERedirectedPermanently:
		case THTTPEvent::ERedirectedTemporarily:
			{
			if (iVerbose)
				{
				Printf(_L("Redirecting\r\n"));
				}
			}
		break;
		
		case THTTPEvent::ERedirectRequiresConfirmation:
			{
			if (iVerbose)
				{
				Printf(_L("Redirect requires confirmation. Aborting\r\n"));
				}
			Complete(KErrAbort);
			}
		break;
		
		default:
			{
			if (iVerbose)
				{
				Printf(_L("Transaction result: <%d>\r\n"), aEvent.iStatus);
				}
			if (aEvent.iStatus < 0)
				{
				Complete(aEvent.iStatus);
				}
			}
		break;
		};
	}
	
//
// CCmdWget::MHFRunError
// handles any error related up-calls from an underlying transaction in progress
//
TInt CCmdWget::MHFRunError(TInt aError, RHTTPTransaction /* aTransaction */, const THTTPEvent& /* aEvent */)
	{
	if (iVerbose)
		{
		PrintError(aError, _L("HTTP Transaction error"));
		}
	return KErrNone;
	}
	
//
// CCmdWget::DumpRespHeadersL
// dump the transaction response headers
//
void CCmdWget::DumpRespHeadersL(RHTTPTransaction& aTransaction)
	{
	RHTTPResponse resp = aTransaction.Response();
	RStringPool strP = aTransaction.Session().StringPool();
	RHTTPHeaders hdr = resp.GetHeaderCollection();
	THTTPHdrFieldIter it = hdr.Fields();

	TBuf<KMaxHeaderNameLen>  fieldName16;
	TBuf<KMaxHeaderValueLen> fieldVal16;

	while (it.AtEnd() == EFalse)
		{
		RStringTokenF fieldName = it();
		RStringF fieldNameStr = strP.StringF(fieldName);
		THTTPHdrVal fieldVal;
		if (hdr.GetField(fieldNameStr,0,fieldVal) == KErrNone)
			{
			const TDesC8& fieldNameDesC = fieldNameStr.DesC();
			fieldName16.Copy(fieldNameDesC.Left(KMaxHeaderNameLen));
			switch (fieldVal.Type())
				{
			case THTTPHdrVal::KTIntVal:
				Printf(_L("%S: %d\r\n"), &fieldName16, fieldVal.Int());
				break;
			case THTTPHdrVal::KStrFVal:
				{
				RStringF fieldValStr = strP.StringF(fieldVal.StrF());
				const TDesC8& fieldValDesC = fieldValStr.DesC();
				fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen));
				Printf(_L("%S: %S\r\n"), &fieldName16, &fieldVal16);
				}
				break;
			case THTTPHdrVal::KStrVal:
				{
				RString fieldValStr = strP.String(fieldVal.Str());
				const TDesC8& fieldValDesC = fieldValStr.DesC();
				fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen));
				Printf(_L("%S: %S\r\n"), &fieldName16, &fieldVal16);
				}
				break;
			case THTTPHdrVal::KDateVal:
				{
				TDateTime date = fieldVal.DateTime();
				TBuf<40> dateTimeString;
				TTime t(date);
				t.FormatL(dateTimeString,KDateFormat);
				Printf(_L("%S: %S\r\n"), &fieldName16, &dateTimeString);
				} 
				break;
			default:
				Printf(_L("%S: <unrecognised value type>\r\n"), &fieldName16);
				break;
				};

			// Display realm for WWW-Authenticate header
			RStringF wwwAuth = strP.StringF(HTTP::EWWWAuthenticate,RHTTPSession::GetTable());
			if (fieldNameStr == wwwAuth)
				{
				// check the auth scheme is 'basic'
				RStringF basic = strP.StringF(HTTP::EBasic,RHTTPSession::GetTable());
				RStringF realm = strP.StringF(HTTP::ERealm,RHTTPSession::GetTable());
				THTTPHdrVal realmVal;
				if ((fieldVal.StrF() == basic) && 
					(!hdr.GetParam(wwwAuth, realm, realmVal)))
					{
					RStringF realmValStr = strP.StringF(realmVal.StrF());
					fieldVal16.Copy(realmValStr.DesC());
					Printf(_L("Realm is: %S\r\n"), &fieldVal16);
					}
				}
			}
		++it;
		}	
	}

//
// MHTTPDataSupplier methods
//

//
// CCmdWget::GetNextDataPart
// reads a chunk of data from the source file
// 
TBool CCmdWget::GetNextDataPart(TPtrC8& aDataPart)
	{
	ASSERT(iPostData);
	ASSERT(iLocalFile.SubSessionHandle() > 0);
		
	TBool result = EFalse;
	
	// calc read length
	TInt readlength = KDefaultChunkSize;
	if ((iLocalFileSize - iLocalBytesRead) < readlength)
		{
		readlength = iLocalFileSize - iLocalBytesRead;
		}
	
	// if necessary, return previous chunk (per the SDK's MHTTPDataSupplier documentation)
	if (!iReleaseDataCalled && (iPrevData.Length() > 0))
		{
		iReleaseDataCalled = EFalse;
		aDataPart.Set(iPrevData);
		return result;
		}
	
	// read another chunk of the data & pass it back
	TInt error = iLocalFile.Read(iPrevData, readlength);
	if (error != KErrNone)
		{
		if (iVerbose)
			{
			PrintError(error, _L("Unable to read data from local file."));
			}
		Complete(error);
		return result;
		}
	aDataPart.Set(iPrevData);
	iReleaseDataCalled = EFalse;
	iLocalBytesRead += readlength;
	if (iLocalBytesRead >= iLocalFileSize)
		{
		ASSERT((iLocalBytesRead == iLocalFileSize)); // else our math has gone wrong somewhere 
		result = ETrue;
		}
	return result;
	}

//
// CCmdWget::ReleaseData
// tell the http transaction we have more data available
//
void CCmdWget::ReleaseData()
	{
	ASSERT(iPostData);
	iReleaseDataCalled = ETrue;
	if ((iLocalFileSize - iLocalBytesRead) > 0)
		{
		// more data to come
		TRAPD(error, iTransaction.NotifyNewRequestBodyPartL());
		if (error != KErrNone)
			{
			if (iVerbose)
				{
				PrintError(error, _L("Unable to release data."));
				}
			Complete(error);
			}
		}
	}

//
// CCmdWget::OverallDataSize
// return the total size of the data we're to upload to the server
//
TInt CCmdWget::OverallDataSize()
	{
	ASSERT(iPostData);
	ASSERT(iLocalFile.SubSessionHandle() > 0);
	return iLocalFileSize;
	}

//
// CCmdWget::Reset
// Reset the data supply to the beginning of the local file
// 
TInt CCmdWget::Reset()
	{
	ASSERT(iPostData);
	ASSERT(iLocalFile.SubSessionHandle() > 0);
	TInt pos = 0;
	TInt error = iLocalFile.Seek(ESeekStart, pos); // not interested in pos
	return error;
	}


EXE_BOILER_PLATE(CCmdWget)
