| 2 |      1 | /*
 | 
|  |      2 | * Copyright (c) 2007-2010 Sebastian Brannstrom, Lars Persson, EmbedDev AB
 | 
|  |      3 | *
 | 
|  |      4 | * All rights reserved.
 | 
|  |      5 | * This component and the accompanying materials are made available
 | 
|  |      6 | * under the terms of the License "Eclipse Public License v1.0"
 | 
|  |      7 | * which accompanies this distribution, and is available
 | 
|  |      8 | * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 | 
|  |      9 | *
 | 
|  |     10 | * Initial Contributors:
 | 
|  |     11 | * EmbedDev AB - initial contribution.
 | 
|  |     12 | *
 | 
|  |     13 | * Contributors:
 | 
|  |     14 | *
 | 
|  |     15 | * Description:
 | 
|  |     16 | *
 | 
|  |     17 | */
 | 
|  |     18 | 
 | 
|  |     19 | // HttpEventHandler.cpp
 | 
|  |     20 | #include <e32debug.h>
 | 
|  |     21 | #include <httperr.h>
 | 
|  |     22 | 
 | 
|  |     23 | #include "HttpEventHandler.h"
 | 
|  |     24 | #include "bautils.h"
 | 
|  |     25 | #include "Httpclient.h"
 | 
|  |     26 | 
 | 
|  |     27 | void CHttpEventHandler::ConstructL()
 | 
|  |     28 | 	{
 | 
|  |     29 | 	//iVerbose = ETrue;	
 | 
|  |     30 | 	}
 | 
|  |     31 | 
 | 
|  |     32 | 
 | 
|  |     33 | CHttpEventHandler::CHttpEventHandler(CHttpClient* aClient, MHttpClientObserver &aCallbacks, RFs& aFs): 
 | 
|  |     34 | 	iFileServ(aFs), iHttpClient(aClient), iCallbacks(aCallbacks)
 | 
|  |     35 | 	{
 | 
|  |     36 | 	}
 | 
|  |     37 | 
 | 
|  |     38 | 
 | 
|  |     39 | CHttpEventHandler::~CHttpEventHandler()
 | 
|  |     40 | 	{	
 | 
|  |     41 | 	}
 | 
|  |     42 | 
 | 
|  |     43 | 
 | 
|  |     44 | CHttpEventHandler* CHttpEventHandler::NewLC(CHttpClient* aClient, MHttpClientObserver &aCallbacks, RFs& aFs)
 | 
|  |     45 | 	{
 | 
|  |     46 | 	CHttpEventHandler* me = new(ELeave)CHttpEventHandler(aClient, aCallbacks, aFs);
 | 
|  |     47 | 	CleanupStack::PushL(me);
 | 
|  |     48 | 	me->ConstructL();
 | 
|  |     49 | 	return me;
 | 
|  |     50 | 	}
 | 
|  |     51 | 
 | 
|  |     52 | 
 | 
|  |     53 | CHttpEventHandler* CHttpEventHandler::NewL(CHttpClient* aClient, MHttpClientObserver &aCallbacks, RFs& aFs)
 | 
|  |     54 | 	{
 | 
|  |     55 | 	CHttpEventHandler* me = NewLC(aClient, aCallbacks, aFs);
 | 
|  |     56 | 	CleanupStack::Pop(me);
 | 
|  |     57 | 	return me;
 | 
|  |     58 | 	}
 | 
|  |     59 | 
 | 
|  |     60 | void CHttpEventHandler::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
 | 
|  |     61 | 	{
 | 
|  |     62 | 	switch (aEvent.iStatus)
 | 
|  |     63 | 		{
 | 
|  |     64 | 		case THTTPEvent::EGotResponseHeaders:
 | 
|  |     65 | 			{
 | 
|  |     66 | 			// HTTP response headers have been received. We can determine now if there is
 | 
|  |     67 | 			// going to be a response body to save.
 | 
|  |     68 | 			RHTTPResponse resp = aTransaction.Response();
 | 
|  |     69 | 			iLastStatusCode = resp.StatusCode();
 | 
|  |     70 | 			RStringF statusStr = resp.StatusText();
 | 
|  |     71 | 			TBuf<32> statusStr16;
 | 
|  |     72 | 			statusStr16.Copy(statusStr.DesC());
 | 
|  |     73 | 			DP2("Status: %d (%S)", iLastStatusCode, &statusStr16);
 | 
|  |     74 | 
 | 
|  |     75 | 			// Dump the headers if we're being verbose
 | 
|  |     76 | 			//DumpRespHeadersL(aTransaction);
 | 
|  |     77 | 
 | 
|  |     78 | 			// Determine if the body will be saved to disk
 | 
|  |     79 | 			iSavingResponseBody = ETrue;
 | 
|  |     80 | 			TBool cancelling = EFalse;
 | 
|  |     81 | 			if (resp.HasBody() && (iLastStatusCode >= 200) && (iLastStatusCode < 300) && (iLastStatusCode != 204))
 | 
|  |     82 | 				{
 | 
|  |     83 | 				//iBytesDownloaded = 0;
 | 
|  |     84 | 				TInt dataSize = resp.Body()->OverallDataSize();
 | 
|  |     85 | 				if (dataSize >= 0) {
 | 
|  |     86 | 					DP1("Response body size is %d", dataSize);
 | 
|  |     87 | 					iBytesTotal = dataSize;	
 | 
|  |     88 | 				} else {
 | 
|  |     89 | 					DP("Response body size is unknown");
 | 
|  |     90 | 					iBytesTotal = -1;
 | 
|  |     91 | 				}
 | 
|  |     92 | 				iCallbacks.DownloadInfo(iHttpClient, dataSize);
 | 
|  |     93 | 
 | 
|  |     94 | 				cancelling = EFalse;
 | 
|  |     95 | 				}
 | 
|  |     96 | 
 | 
|  |     97 | 			// If we're cancelling, must do it now..
 | 
|  |     98 | 			if (cancelling)
 | 
|  |     99 | 				{
 | 
|  |    100 | 				DP("Transaction Cancelled");
 | 
|  |    101 | 				aTransaction.Close();
 | 
|  |    102 | 				iHttpClient->ClientRequestCompleteL(KErrCancel);
 | 
|  |    103 | 				}
 | 
|  |    104 | 			else if (iSavingResponseBody) // If we're saving, then open a file handle for the new file
 | 
|  |    105 | 				{
 | 
|  |    106 | 				iFileServ.Parse(iFileName, iParsedFileName);
 | 
|  |    107 | 				TInt valid = iFileServ.IsValidName(iFileName);
 | 
|  |    108 | 				if (!valid)
 | 
|  |    109 | 					{
 | 
|  |    110 | 					DP("The specified filename is not valid!.");
 | 
|  |    111 | 					iSavingResponseBody = EFalse;
 | 
|  |    112 | 					}
 | 
|  |    113 | 				else
 | 
|  |    114 | 					{
 | 
|  |    115 | 					if (iContinue) {
 | 
|  |    116 | 						TInt err = iRespBodyFile.Open(iFileServ, iParsedFileName.FullName(),EFileWrite);
 | 
|  |    117 | 						if (err)
 | 
|  |    118 | 							{
 | 
|  |    119 | 							DP("There was an error opening file");
 | 
|  |    120 | 							iSavingResponseBody = EFalse;
 | 
|  |    121 | 							User::Leave(err);
 | 
|  |    122 | 							} else {
 | 
|  |    123 | 							int pos = -KByteOverlap;
 | 
|  |    124 | 							if((err=iRespBodyFile.Seek(ESeekEnd, pos)) != KErrNone) {
 | 
|  |    125 | 								DP("Failed to set position!");
 | 
|  |    126 | 								User::Leave(err);
 | 
|  |    127 | 							}
 | 
|  |    128 | 							iBytesDownloaded = (pos > 0) ? pos : 0;
 | 
|  |    129 | 							iBytesTotal += iBytesDownloaded;
 | 
|  |    130 | 							DP1("Total bytes is now %u", iBytesTotal);
 | 
|  |    131 | 							DP1("Seeking end: %d", pos);
 | 
|  |    132 | 							}
 | 
|  |    133 | 					} else {
 | 
|  |    134 | 						TInt err = iRespBodyFile.Replace(iFileServ,
 | 
|  |    135 | 														 iParsedFileName.FullName(),
 | 
|  |    136 | 														 EFileWrite);
 | 
|  |    137 | 						if (err)
 | 
|  |    138 | 							{
 | 
|  |    139 | 							DP("There was an error replacing file");
 | 
|  |    140 | 							iSavingResponseBody = EFalse;
 | 
|  |    141 | 							User::Leave(err);
 | 
|  |    142 | 							}
 | 
|  |    143 | 						}
 | 
|  |    144 | 					}
 | 
|  |    145 | 				}
 | 
|  |    146 | 
 | 
|  |    147 | 			} break;
 | 
|  |    148 | 		case THTTPEvent::EGotResponseBodyData:
 | 
|  |    149 | 			{
 | 
|  |    150 | 			// Get the body data supplier
 | 
|  |    151 | 			iRespBody = aTransaction.Response().Body();
 | 
|  |    152 | 
 | 
|  |    153 | 			// Some (more) body data has been received (in the HTTP response)
 | 
|  |    154 | 			//DumpRespBody(aTransaction);
 | 
|  |    155 | 			//DP1("Saving: %d", iSavingResponseBody);
 | 
|  |    156 | 			// Append to the output file if we're saving responses
 | 
|  |    157 | 			if (iSavingResponseBody)
 | 
|  |    158 | 				{
 | 
|  |    159 | 				TPtrC8 bodyData;
 | 
|  |    160 | 				iRespBody->GetNextDataPart(bodyData);
 | 
|  |    161 | 				iBytesDownloaded += bodyData.Length();
 | 
|  |    162 | 				TInt error = iRespBodyFile.Write(bodyData);
 | 
|  |    163 | 				
 | 
|  |    164 | 				// on writing error we close connection 
 | 
|  |    165 | 				if (error != KErrNone) {
 | 
|  |    166 | 					//aTransaction.Close();
 | 
|  |    167 | 					iCallbacks.FileError(error);
 | 
|  |    168 | 					iHttpClient->ClientRequestCompleteL(error);
 | 
|  |    169 | 					return;
 | 
|  |    170 | 				}
 | 
|  |    171 | 
 | 
|  |    172 | 				if (!iSilent) {
 | 
|  |    173 | 					iCallbacks.Progress(iHttpClient, iBytesDownloaded, iBytesTotal);
 | 
|  |    174 | 				}
 | 
|  |    175 | 				}
 | 
|  |    176 | 
 | 
|  |    177 | 			// Done with that bit of body data
 | 
|  |    178 | 			iRespBody->ReleaseData();
 | 
|  |    179 | 			} break;
 | 
|  |    180 | 		case THTTPEvent::EResponseComplete:
 | 
|  |    181 | 			{
 | 
|  |    182 | 			// The transaction's response is complete
 | 
|  |    183 | 
 | 
|  |    184 | 			DP("Transaction Complete");
 | 
|  |    185 | 			DP("Closing file");
 | 
|  |    186 | 			iRespBodyFile.Close();
 | 
|  |    187 | 			} break;
 | 
|  |    188 | 		case THTTPEvent::ESucceeded:
 | 
|  |    189 | 			{
 | 
|  |    190 | 			DP("Transaction Successful");
 | 
|  |    191 | 			aTransaction.Close();
 | 
|  |    192 | 			iHttpClient->ClientRequestCompleteL(KErrNone);
 | 
|  |    193 | 			} break;
 | 
|  |    194 | 		case THTTPEvent::EFailed:
 | 
|  |    195 | 			{
 | 
|  |    196 | 			DP("Transaction Failed");
 | 
|  |    197 | 			aTransaction.Close();
 | 
|  |    198 | 			
 | 
|  |    199 | 			if(iLastStatusCode == HTTPStatus::EOk || iLastStatusCode == HTTPStatus::ECreated || iLastStatusCode == HTTPStatus::EAccepted)
 | 
|  |    200 | 				{
 | 
|  |    201 | 				iLastStatusCode = KErrNone;
 | 
|  |    202 | 				}
 | 
|  |    203 | 			
 | 
|  |    204 | 			iHttpClient->ClientRequestCompleteL(iLastStatusCode);
 | 
|  |    205 | 			} break;
 | 
|  |    206 | 		case THTTPEvent::ERedirectedPermanently:
 | 
|  |    207 | 			{
 | 
|  |    208 | 			DP("Permanent Redirection");
 | 
|  |    209 | 			} break;
 | 
|  |    210 | 		case THTTPEvent::ERedirectedTemporarily:
 | 
|  |    211 | 			{
 | 
|  |    212 | 			DP("Temporary Redirection");
 | 
|  |    213 | 			} break;
 | 
|  |    214 | 		default:
 | 
|  |    215 | 			{
 | 
|  |    216 | 			DP1("<unrecognised event: %d>", aEvent.iStatus);
 | 
|  |    217 | 			// close off the transaction if it's an error
 | 
|  |    218 | 			if (aEvent.iStatus < 0)
 | 
|  |    219 | 				{
 | 
|  |    220 | 				aTransaction.Close();
 | 
|  |    221 | 				iHttpClient->ClientRequestCompleteL(aEvent.iStatus);
 | 
|  |    222 | 				}
 | 
|  |    223 | 			} break;
 | 
|  |    224 | 		}
 | 
|  |    225 | 	}
 | 
|  |    226 | 
 | 
|  |    227 | TInt CHttpEventHandler::MHFRunError(TInt aError, RHTTPTransaction /*aTransaction*/, const THTTPEvent& /*aEvent*/)
 | 
|  |    228 | 	{
 | 
|  |    229 | 	DP1("MHFRunError fired with error code %d", aError);
 | 
|  |    230 | 
 | 
|  |    231 | 	return KErrNone;
 | 
|  |    232 | 	}
 | 
|  |    233 | 
 | 
|  |    234 | void CHttpEventHandler::SetSaveFileName(const TDesC &fName, TBool aContinue)
 | 
|  |    235 | 	{
 | 
|  |    236 | 	iFileName.Copy(fName);
 | 
|  |    237 | 	iContinue = aContinue;
 | 
|  |    238 | 	}
 | 
|  |    239 | 
 | 
|  |    240 | void CHttpEventHandler::DumpRespHeadersL(RHTTPTransaction& aTrans)
 | 
|  |    241 | 	{
 | 
|  |    242 | 	RHTTPResponse resp = aTrans.Response();
 | 
|  |    243 | 	RStringPool strP = aTrans.Session().StringPool();
 | 
|  |    244 | 	RHTTPHeaders hdr = resp.GetHeaderCollection();
 | 
|  |    245 | 	THTTPHdrFieldIter it = hdr.Fields();
 | 
|  |    246 | 
 | 
|  |    247 | 	TBuf<KMaxHeaderNameLen>  fieldName16;
 | 
|  |    248 | 	TBuf<KMaxHeaderValueLen> fieldVal16;
 | 
|  |    249 | 
 | 
|  |    250 | 	while (it.AtEnd() == EFalse)
 | 
|  |    251 | 		{
 | 
|  |    252 | 		RStringTokenF fieldName = it();
 | 
|  |    253 | 		RStringF fieldNameStr = strP.StringF(fieldName);
 | 
|  |    254 | 		THTTPHdrVal fieldVal;
 | 
|  |    255 | 		if (hdr.GetField(fieldNameStr,0,fieldVal) == KErrNone)
 | 
|  |    256 | 			{
 | 
|  |    257 | 			const TDesC8& fieldNameDesC = fieldNameStr.DesC();
 | 
|  |    258 | 			fieldName16.Copy(fieldNameDesC.Left(KMaxHeaderNameLen));
 | 
|  |    259 | 			switch (fieldVal.Type())
 | 
|  |    260 | 				{
 | 
|  |    261 | 			case THTTPHdrVal::KTIntVal:
 | 
|  |    262 | 				DP2("%S: %d", &fieldName16, fieldVal.Int());
 | 
|  |    263 | 				break;
 | 
|  |    264 | 			case THTTPHdrVal::KStrFVal:
 | 
|  |    265 | 				{
 | 
|  |    266 | 				RStringF fieldValStr = strP.StringF(fieldVal.StrF());
 | 
|  |    267 | 				const TDesC8& fieldValDesC = fieldValStr.DesC();
 | 
|  |    268 | 				fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen));
 | 
|  |    269 | 				DP2("%S: %S", &fieldName16, &fieldVal16);
 | 
|  |    270 | 				}
 | 
|  |    271 | 				break;
 | 
|  |    272 | 			case THTTPHdrVal::KStrVal:
 | 
|  |    273 | 				{
 | 
|  |    274 | 				RString fieldValStr = strP.String(fieldVal.Str());
 | 
|  |    275 | 				const TDesC8& fieldValDesC = fieldValStr.DesC();
 | 
|  |    276 | 				fieldVal16.Copy(fieldValDesC.Left(KMaxHeaderValueLen));
 | 
|  |    277 | 				DP2("%S: %S", &fieldName16, &fieldVal16);
 | 
|  |    278 | 				}
 | 
|  |    279 | 				break;
 | 
|  |    280 | 			case THTTPHdrVal::KDateVal:
 | 
|  |    281 | 				{
 | 
|  |    282 | 				TDateTime date = fieldVal.DateTime();
 | 
|  |    283 | 				} 
 | 
|  |    284 | 				break;
 | 
|  |    285 | 			default:
 | 
|  |    286 | 				DP1("%S: <unrecognised value type>", &fieldName16);
 | 
|  |    287 | 				break;
 | 
|  |    288 | 				}
 | 
|  |    289 | 
 | 
|  |    290 | 			// Display realm for WWW-Authenticate header
 | 
|  |    291 | 			RStringF wwwAuth = strP.StringF(HTTP::EWWWAuthenticate,RHTTPSession::GetTable());
 | 
|  |    292 | 			if (fieldNameStr == wwwAuth)
 | 
|  |    293 | 				{
 | 
|  |    294 | 				// check the auth scheme is 'basic'
 | 
|  |    295 | 				RStringF basic = strP.StringF(HTTP::EBasic,RHTTPSession::GetTable());
 | 
|  |    296 | 				RStringF realm = strP.StringF(HTTP::ERealm,RHTTPSession::GetTable());
 | 
|  |    297 | 				THTTPHdrVal realmVal;
 | 
|  |    298 | 				if ((fieldVal.StrF() == basic) && 
 | 
|  |    299 | 					(!hdr.GetParam(wwwAuth, realm, realmVal)))
 | 
|  |    300 | 					{
 | 
|  |    301 | 					RStringF realmValStr = strP.StringF(realmVal.StrF());
 | 
|  |    302 | 					fieldVal16.Copy(realmValStr.DesC());
 | 
|  |    303 | 					DP1("Realm is: %S", &fieldVal16);
 | 
|  |    304 | 					}
 | 
|  |    305 | 				}
 | 
|  |    306 | 			}
 | 
|  |    307 | 		++it;
 | 
|  |    308 | 		}
 | 
|  |    309 | 	}
 | 
|  |    310 | 
 | 
|  |    311 | void CHttpEventHandler::DumpRespBody(RHTTPTransaction& aTrans)
 | 
|  |    312 | 	{
 | 
|  |    313 | 	MHTTPDataSupplier* body = aTrans.Response().Body();
 | 
|  |    314 | 	TPtrC8 dataChunk;
 | 
|  |    315 | 	TBool isLast = body->GetNextDataPart(dataChunk);
 | 
|  |    316 | 	DumpIt(dataChunk);
 | 
|  |    317 | 	if (isLast)
 | 
|  |    318 | 		DP("Got last data chunk.");
 | 
|  |    319 | 	}
 | 
|  |    320 | 
 | 
|  |    321 | 
 | 
|  |    322 | void CHttpEventHandler::DumpIt(const TDesC8& aData)
 | 
|  |    323 | //Do a formatted dump of binary data
 | 
|  |    324 | 	{
 | 
|  |    325 | 	// Iterate the supplied block of data in blocks of cols=80 bytes
 | 
|  |    326 | 	const TInt cols=16;
 | 
|  |    327 | 	TInt pos = 0;
 | 
|  |    328 | 	TBuf<KMaxFileName - 2> logLine;
 | 
|  |    329 | 	TBuf<KMaxFileName - 2> anEntry;
 | 
|  |    330 | 	const TInt dataLength = aData.Length();
 | 
|  |    331 | 
 | 
|  |    332 | 	while (pos < dataLength)
 | 
|  |    333 | 		{
 | 
|  |    334 | 		//start-line hexadecimal( a 4 digit number)
 | 
|  |    335 | 		anEntry.Format(TRefByValue<const TDesC>_L("%04x : "), pos);
 | 
|  |    336 | 		logLine.Append(anEntry);
 | 
|  |    337 | 
 | 
|  |    338 | 		// Hex output
 | 
|  |    339 | 		TInt offset;
 | 
|  |    340 | 		for (offset = 0; offset < cols; ++offset)
 | 
|  |    341 | 			{
 | 
|  |    342 | 			if (pos + offset < aData.Length())
 | 
|  |    343 | 				{
 | 
|  |    344 | 				TInt nextByte = aData[pos + offset];
 | 
|  |    345 | 				anEntry.Format(TRefByValue<const TDesC>_L("%02x "), nextByte);
 | 
|  |    346 | 				logLine.Append(anEntry);
 | 
|  |    347 | 				}
 | 
|  |    348 | 			else
 | 
|  |    349 | 				{
 | 
|  |    350 | 				//fill the remaining spaces with blanks untill the cols-th Hex number 
 | 
|  |    351 | 				anEntry.Format(TRefByValue<const TDesC>_L("   "));
 | 
|  |    352 | 				logLine.Append(anEntry);
 | 
|  |    353 | 				}
 | 
|  |    354 | 			}
 | 
|  |    355 | 			anEntry.Format(TRefByValue<const TDesC>_L(": "));
 | 
|  |    356 | 			logLine.Append(anEntry);
 | 
|  |    357 | 
 | 
|  |    358 | 		// Char output
 | 
|  |    359 | 		for (offset = 0; offset < cols; ++offset)
 | 
|  |    360 | 			{
 | 
|  |    361 | 			if (pos + offset < aData.Length())
 | 
|  |    362 | 				{
 | 
|  |    363 | 				TInt nextByte = aData[pos + offset];
 | 
|  |    364 | 				if ((nextByte >= ' ') && (nextByte <= '~'))
 | 
|  |    365 | 					{
 | 
|  |    366 | 					anEntry.Format(TRefByValue<const TDesC>_L("%c"), nextByte);
 | 
|  |    367 | 					logLine.Append(anEntry);
 | 
|  |    368 | 					}
 | 
|  |    369 | 				else
 | 
|  |    370 | 					{
 | 
|  |    371 | 					anEntry.Format(TRefByValue<const TDesC>_L("."));
 | 
|  |    372 | 					logLine.Append(anEntry);
 | 
|  |    373 | 					}
 | 
|  |    374 | 				}
 | 
|  |    375 | 			else
 | 
|  |    376 | 				{
 | 
|  |    377 | 				anEntry.Format(TRefByValue<const TDesC>_L(" "));
 | 
|  |    378 | 				logLine.Append(anEntry);
 | 
|  |    379 | 				}
 | 
|  |    380 | 			}
 | 
|  |    381 | 			logLine.Zero();
 | 
|  |    382 | 
 | 
|  |    383 | 		// Advance to next  byte segment (1 seg= cols)
 | 
|  |    384 | 		pos += cols;
 | 
|  |    385 | 		}
 | 
|  |    386 | 	}
 | 
|  |    387 | 
 | 
|  |    388 | void CHttpEventHandler::SetSilent(TBool aSilent)
 | 
|  |    389 | 	{
 | 
|  |    390 | 	iSilent = aSilent;
 | 
|  |    391 | 	}
 | 
|  |    392 | 
 | 
|  |    393 | void CHttpEventHandler::CloseSaveFile()
 | 
|  |    394 | {
 | 
|  |    395 | 	if(iRespBody != NULL)
 | 
|  |    396 | 	{		
 | 
|  |    397 | 		if(iRespBodyFile.SubSessionHandle() != 0)
 | 
|  |    398 | 			{
 | 
|  |    399 | 			TInt size;
 | 
|  |    400 | 			iRespBodyFile.Size(size);
 | 
|  |    401 | 			DP2("Closing file at size %d, bytes downloaded %d", size, iBytesDownloaded);
 | 
|  |    402 | 			iRespBodyFile.Close();
 | 
|  |    403 | 			}
 | 
|  |    404 | 	}
 | 
|  |    405 | }
 | 
|  |    406 | 
 |