00001 /* 00002 !concept {:name => "Multipart HTTP posting"} 00003 */ 00004 00005 #include "up_poster_epoc.hpp" 00006 00007 #if __FEATURE_UPLOADER__ 00008 00009 #include "cf_query.h" // for username 00010 00011 #include "common/assertions.h" 00012 #include "common/epoc-time.h" 00013 #include "common/logging.h" 00014 #include "common/sh_utils.h" 00015 00016 #include <commdb.h> 00017 #include <cdbpreftable.h> 00018 #include <commdbconnpref.h> 00019 #include <es_enum.h> 00020 #include <f32file.h> 00021 00022 CTOR_IMPL_CPosterAo; 00023 00024 void CPosterAo::ConstructL() 00025 { 00026 iFileDataSupplier = new (ELeave) CFileDataSupplier; 00027 00028 LEAVE_IF_ERROR_OR_SET_SESSION_OPEN(iSocketServ, iSocketServ.Connect()); 00029 LEAVE_IF_ERROR_OR_SET_SESSION_OPEN(iConnection, iConnection.Open(iSocketServ)); 00030 00031 TCommDbConnPref connPref; 00032 connPref.SetDialogPreference(ECommDbDialogPrefDoNotPrompt); 00033 connPref.SetIapId(iIapId); 00034 User::LeaveIfError(iConnection.Start(connPref)); 00035 00036 iHttpSession.OpenL(); SET_SESSION_OPEN(iHttpSession); 00037 00038 // Set our RSocketServ and RConnection to the RHTTPSession instance. 00039 RStringPool strPool = iHttpSession.StringPool(); 00040 RHTTPConnectionInfo connInfo = iHttpSession.ConnectionInfo(); 00041 connInfo.SetPropertyL(strPool.StringF(HTTP::EHttpSocketServ, RHTTPSession::GetTable()), 00042 THTTPHdrVal(iSocketServ.Handle())); 00043 connInfo.SetPropertyL(strPool.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable()), 00044 THTTPHdrVal(REINTERPRET_CAST(TInt, &(iConnection)))); 00045 } 00046 00047 CPosterAo::~CPosterAo() 00048 { 00049 SESSION_CLOSE_IF_OPEN(iHttpTransaction); 00050 SESSION_CLOSE_IF_OPEN(iHttpSession); 00051 SESSION_CLOSE_IF_OPEN(iConnection); 00052 SESSION_CLOSE_IF_OPEN(iSocketServ); 00053 delete iFileDataSupplier; 00054 } 00055 00056 // ---------------------------------------------------------------------------- 00057 // CClientEngine::SetHeaderL() 00058 // 00059 // Used to set header value to HTTP request 00060 // ---------------------------------------------------------------------------- 00061 void CPosterAo::SetHeaderL(RHTTPHeaders aHeaders, 00062 TInt aHdrField, 00063 const TDesC8& aHdrValue) 00064 { 00065 RStringF valStr = iHttpSession.StringPool().OpenFStringL(aHdrValue); 00066 CleanupClosePushL(valStr); 00067 THTTPHdrVal val(valStr); 00068 aHeaders.SetFieldL(iHttpSession.StringPool(). 00069 StringF(aHdrField, 00070 RHTTPSession::GetTable()), val); 00071 CleanupStack::PopAndDestroy(); // valStr 00072 } 00073 00074 void CPosterAo::PostComplete(TInt errCode) 00075 { 00076 if (iState != EActive) return; 00077 logh(); 00078 assert(iFileDataSupplier); 00079 iFileDataSupplier->CloseFile(); // to allow deletion 00080 iState = EDone; 00081 iObserver.PosterEvent(errCode); 00082 } 00083 00084 void CPosterAo::PostFileL(const TDesC8& aUri, 00085 const TDesC& aFileName) 00086 { 00087 assert(iState == EReady); 00088 assert(iFileDataSupplier); 00089 iFileDataSupplier->OpenL(aFileName); 00090 SetBoundary(iFileDataSupplier->Boundary()); 00091 PostGenericL(aUri, *iFileDataSupplier); 00092 } 00093 00094 // ---------------------------------------------------------------------------- 00095 // CClientEngine::IssueHTTPPostL() 00096 // 00097 // Start a new HTTP POST transaction. 00098 // ---------------------------------------------------------------------------- 00099 00100 void CPosterAo::PostGenericL(const TDesC8& aUri, 00101 MDataSupplier& aDataSupplier) 00102 { 00103 // Parse string to URI 00104 TUriParser8 uri; 00105 uri.Parse(aUri); 00106 00107 // Get request method string for HTTP POST 00108 RStringF method = iHttpSession.StringPool().StringF(HTTP::EPOST, 00109 RHTTPSession::GetTable()); 00110 00111 // Open transaction with previous method and parsed URI. This class will 00112 // receive transaction events in MHFRunL and MHFRunError. 00113 SESSION_CLOSE_IF_OPEN(iHttpTransaction); 00114 iHttpTransaction = iHttpSession.OpenTransactionL(uri, *this, method); 00115 SET_SESSION_OPEN(iHttpTransaction); 00116 00117 aDataSupplier.SetHttpTransaction(&iHttpTransaction); 00118 00119 // Set headers for request; user agent, accepted content type and body's 00120 // content type. 00121 RHTTPHeaders hdr = iHttpTransaction.Request().GetHeaderCollection(); 00122 _LIT8(KConnectionClose, "close"); 00123 SetHeaderL(hdr, HTTP::EConnection, KConnectionClose); 00124 00125 _LIT8(KContentTypeStart, "multipart/form-data, boundary="); 00126 TBuf8<KMultiPartBoundaryMaxLen + 30> contentType; 00127 contentType.Copy(KContentTypeStart); 00128 contentType.Append(iBoundary); 00129 SetHeaderL(hdr, HTTP::EContentType, contentType); 00130 00131 // Set this class as an data supplier. Inherited MHTTPDataSupplier methods 00132 // are called when framework needs to send body data. 00133 iHttpTransaction.Request().SetBody(aDataSupplier); 00134 00135 // Submit the transaction. After this the framework will give transaction 00136 // events via MHFRunL and MHFRunError. 00137 iHttpTransaction.SubmitL(); 00138 00139 iState = EActive; 00140 } 00141 00142 // ---------------------------------------------------------------------------- 00143 // CClientEngine::MHFRunL() 00144 // 00145 // Inherited from MHTTPTransactionCallback 00146 // Called by framework to pass transaction events. 00147 // ---------------------------------------------------------------------------- 00148 void CPosterAo::MHFRunL(RHTTPTransaction aTransaction, 00149 const THTTPEvent& aEvent) 00150 { 00151 TInt eventStatus = aEvent.iStatus; 00152 00153 switch (eventStatus) 00154 { 00155 case THTTPEvent::EGotResponseHeaders: 00156 { 00157 // HTTP response headers have been received. Use 00158 // aTransaction.Response() to get the response. However, it's 00159 // not necessary to do anything with the response when this 00160 // event occurs. 00161 // 00162 // We really only want the response code (to know whether the 00163 // post succeeded), but we shall read the complete response 00164 // nonetheless. 00165 00166 // Get HTTP status code from header (e.g. 200) 00167 RHTTPResponse resp = aTransaction.Response(); 00168 iHttpStatus = resp.StatusCode(); 00169 logg("HTTP status %d", iHttpStatus); 00170 00171 // xxx if we got a redirect we should really make a new 00172 // request, see http://mobbler.googlecode.com/ 00173 00174 /* 00175 // Get status text (e.g. "OK") 00176 TBuf<KStatustextBufferSize> statusText; 00177 statusText.Copy(resp.StatusText().DesC()); 00178 */ 00179 } 00180 break; 00181 00182 case THTTPEvent::EGotResponseBodyData: 00183 { 00184 // Part (or all) of response's body data received. Use 00185 // aTransaction.Response().Body()->GetNextDataPart() to get 00186 // the actual body data. 00187 // 00188 // According to http://mikie.iki.fi/wordpress/?p=48, consuming 00189 // the body is NOT optional. 00190 00191 // Get the body data supplier 00192 MHTTPDataSupplier* body = aTransaction.Response().Body(); 00193 TPtrC8 dataChunk; 00194 00195 // GetNextDataPart() returns ETrue, if the received part is the last 00196 // one. 00197 TBool isLast = body->GetNextDataPart(dataChunk); 00198 logg("body data chunk of %d bytes received", dataChunk.Length()); 00199 00200 // NOTE: isLast may not be ETrue even if last data part received. 00201 // (e.g. multipart response without content length field) 00202 // Use EResponseComplete to reliably determine when body is completely 00203 // received. 00204 if (isLast) 00205 { 00206 //logt("apparently the last chunk of the response"); 00207 } 00208 00209 // Always remember to release the body data. 00210 body->ReleaseData(); 00211 } 00212 break; 00213 00214 case THTTPEvent::EResponseComplete: 00215 { 00216 // Indicates that header & body of response is completely received. 00217 // No further action here needed. 00218 logt("R_HTTP_TX_COMPLETE"); 00219 } 00220 break; 00221 00222 case THTTPEvent::ESucceeded: 00223 { 00224 // Indicates that transaction succeeded. 00225 logt("R_HTTP_TX_SUCCESSFUL"); 00226 // Some codes might even indicate a permanent error, but it is 00227 // possible for such errors to appear as we do tweaking on the 00228 // server side. 00229 PostComplete((iHttpStatus == 200) ? POSTER_SUCCESS : POSTER_TRANSIENT_FAILURE); 00230 } 00231 break; 00232 00233 case THTTPEvent::EFailed: 00234 { 00235 // Transaction completed with failure. 00236 logt("R_HTTP_TX_FAILED"); 00237 PostComplete(POSTER_TRANSIENT_FAILURE); // guessing 00238 } 00239 break; 00240 00241 default: 00242 { 00243 logg("THTTPEvent (%d)", eventStatus); 00244 // Any negative value presumably is a Symbian error, otherwise 00245 // we are just guessing here. And for progress events it is 00246 // just wrong to even complete the post. 00247 PostComplete((eventStatus < 0) ? eventStatus : POSTER_TRANSIENT_FAILURE); 00248 } 00249 break; 00250 } 00251 } 00252 00253 // ---------------------------------------------------------------------------- 00254 // CClientEngine::MHFRunError() 00255 // 00256 // Inherited from MHTTPTransactionCallback 00257 // Called by framework when *leave* occurs in handling of transaction event. 00258 // These errors must be handled, or otherwise HTTP-CORE 6 panic is thrown. 00259 // ---------------------------------------------------------------------------- 00260 TInt CPosterAo::MHFRunError(TInt aError, 00261 RHTTPTransaction /*aTransaction*/, 00262 const THTTPEvent& /*aEvent*/) 00263 { 00264 logg("leave %d in HTTP handler", aError); 00265 PostComplete(aError); 00266 return KErrNone; 00267 } 00268 00269 // -------------------------------------------------- 00270 // -------------------------------------------------- 00271 // -------------------------------------------------- 00272 // CFileDataSupplier 00273 // -------------------------------------------------- 00274 // -------------------------------------------------- 00275 // -------------------------------------------------- 00276 00277 CFileDataSupplier::~CFileDataSupplier() 00278 { 00279 Close(); 00280 } 00281 00282 void CFileDataSupplier::CloseFile() 00283 { 00284 SESSION_CLOSE_IF_OPEN(iFile); 00285 } 00286 00287 void CFileDataSupplier::Close() 00288 { 00289 CloseFile(); 00290 SESSION_CLOSE_IF_OPEN(iFs); 00291 iPrelude.Close(); 00292 iEpilogue.Close(); 00293 } 00294 00295 // xxx need some metadata support here, probably, unless user identified in some other manner (now all we have is the filename encoded username, but could have a JSON part with that and more, say) 00296 00297 //_LIT8(KPrelude, "-------AaB03xeql7dsxeql7ds\r\nContent-Disposition: form-data; name=\"logdata\"; filename=\"" __USERNAME__ ".db\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n\r\n"); 00298 //_LIT8(KEpilogue, "\r\n-------AaB03xeql7dsxeql7ds\r\nContent-Disposition: form-data; name=\"logdata_submit\"\r\n\r\nUpload\r\n-------AaB03xeql7dsxeql7ds--\r\n"); 00299 00300 _LIT8(KSep, "--"); 00301 _LIT8(KCrLf, "\r\n"); 00302 00303 _LIT8(KBoundary, "-----AaB03xeql7dsxeql7ds"); 00304 00305 void CFileDataSupplier::OpenL(const TDesC& aFileName) 00306 { 00307 Close(); 00308 00309 //logg("lens %d %d %d", KMaxFileName, iFileName.MaxLength(), aFileName.Length()); 00310 iFileName = aFileName; 00311 00312 const gchar* username = get_config_username(); 00313 logg("uploader using username '%s'", username); 00314 00315 iPrelude.CreateMax(200); 00316 iEpilogue.CreateMax(100); 00317 00318 // _LIT8(KPrelude, "-------AaB03xeql7dsxeql7ds\r\nContent-Disposition: form-data; name=\"logdata\"; filename=\"" __USERNAME__ ".db\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n\r\n"); 00319 _LIT8(KPrelude1, "Content-Disposition: form-data; name=\"logdata\"; filename=\""); 00320 _LIT8(KPrelude2, ".db\"\r\nContent-Type: application/octet-stream\r\nContent-Transfer-Encoding: binary\r\n\r\n"); 00321 iPrelude.CopyL(KSep); 00322 iPrelude.AppendL(KBoundary); 00323 iPrelude.AppendL(KCrLf); 00324 iPrelude.AppendL(KPrelude1); 00325 iPrelude.AppendL((const TUint8*)username, strlen(username)); 00326 iPrelude.AppendL(KPrelude2); 00327 00328 // _LIT8(KEpilogue, "\r\n-------AaB03xeql7dsxeql7ds\r\nContent-Disposition: form-data; name=\"logdata_submit\"\r\n\r\nUpload\r\n-------AaB03xeql7dsxeql7ds--\r\n"); 00329 _LIT8(KEpilogueSubmit, "Content-Disposition: form-data; name=\"logdata_submit\"\r\n\r\nUpload\r\n"); 00330 iEpilogue.CopyL(KCrLf); 00331 iEpilogue.AppendL(KSep); 00332 iEpilogue.AppendL(KBoundary); 00333 iEpilogue.AppendL(KCrLf); 00334 iEpilogue.AppendL(KEpilogueSubmit); 00335 iEpilogue.AppendL(KSep); 00336 iEpilogue.AppendL(KBoundary); 00337 iEpilogue.AppendL(KSep); 00338 iEpilogue.AppendL(KCrLf); 00339 00340 LEAVE_IF_ERROR_OR_SET_SESSION_OPEN(iFs, iFs.Connect()); 00341 LEAVE_IF_ERROR_OR_SET_SESSION_OPEN(iFile, iFile.Open(iFs, iFileName, EFileStream|EFileShareAny|EFileRead)); 00342 TInt fileSize = 0; 00343 User::LeaveIfError(iFile.Size(fileSize)); 00344 //iDataLen = KPrelude().Length() + fileSize + KEpilogue().Length(); 00345 iDataLen = iPrelude.Length() + fileSize + iEpilogue.Length(); 00346 iPhase = 0; 00347 } 00348 00349 const TDesC8& CFileDataSupplier::Boundary() const 00350 { 00351 return KBoundary; 00352 } 00353 00354 // It seems that this never gets invoked for the second time even when 00355 // we return EFalse from here, not unless we invoke 00356 // RHTTPTransaction::NotifyNewRequestBodyPartL(). 00357 // 00358 // HttpPostDataSupplier.cpp in the S60 WebKit port reveals that Nokia 00359 // have despaired with this API as well, for example wrt how to handle 00360 // leaves in here. Obviously this API has not been thought out well 00361 // enough to actually robustly support incremental loading from any 00362 // error-prone source. 00363 TBool CFileDataSupplier::GetNextDataPart(TPtrC8& aDataPart) // May leave! 00364 { 00365 //logg("next data part in phase %d", iPhase); 00366 switch (iPhase) 00367 { 00368 case 0: 00369 { 00370 //aDataPart.Set(KPrelude); 00371 aDataPart.Set(iPrelude); 00372 iPhase++; 00373 break; 00374 } 00375 case 1: 00376 { 00377 User::LeaveIfError(iFile.Read(iBuffer)); 00378 //logg("read %d bytes of file data", iBuffer.Length()); 00379 if (iBuffer.Length() > 0) { 00380 aDataPart.Set(iBuffer); 00381 break; 00382 } else { 00383 iPhase++; 00384 // fall through 00385 } 00386 } 00387 case 2: 00388 { 00389 //aDataPart.Set(KEpilogue); 00390 aDataPart.Set(iEpilogue); 00391 iPhase++; 00392 return ETrue; // last part 00393 } 00394 default: 00395 { 00396 assert(0 && "framework error"); 00397 break; 00398 } 00399 } 00400 //logg("non-last data part has length %d", aDataPart.Length()); 00401 return EFalse; 00402 } 00403 00404 void CFileDataSupplier::ReleaseData() // May leave! 00405 { 00406 // Nothing to do here, our buffer is static. 00407 //logt("ReleaseData invoked"); 00408 if (iPhase < 3) 00409 iTransaction->NotifyNewRequestBodyPartL(); 00410 } 00411 00412 TInt CFileDataSupplier::Reset() 00413 { 00414 //logt("Reset invoked"); 00415 iPhase = 0; 00416 TInt pos; // set to new position on return 00417 return iFile.Seek(ESeekStart, pos); 00418 } 00419 00420 TInt CFileDataSupplier::OverallDataSize() 00421 { 00422 //logg("OverallDataSize is %d", iDataLen); 00423 return iDataLen; 00424 } 00425 00426 #endif // __FEATURE_UPLOADER__ 00427 00428 /* 00429 00430 Some of the code in this file has been derived from Nokia sample code, 00431 and such code is used under and covered by the following license: 00432 00433 00434 Copyright © 2006-2008 Nokia Corporation. All rights reserved. 00435 Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. 00436 Java and all Java-based marks are trademarks or registered trademarks of 00437 Sun Microsystems, Inc. Other product and company names mentioned herein may be 00438 trademarks or trade names of their respective owners. 00439 00440 00441 Subject to the conditions below, you may, without charge: 00442 00443 · Use, copy, modify and/or merge copies of this software and 00444 associated documentation files (the "Software") 00445 00446 · Publish, distribute, sub-license and/or sell new software 00447 derived from or incorporating the Software. 00448 00449 00450 00451 This file, unmodified, shall be included with all copies or substantial portions 00452 of the Software that are distributed in source code form. 00453 00454 The Software cannot constitute the primary value of any new software derived 00455 from or incorporating the Software. 00456 00457 Any person dealing with the Software shall not misrepresent the source of the Software. 00458 00459 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 00460 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 00461 PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 00462 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 00463 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 00464 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00465 00466 */ 00467 00468 /** 00469 00470 Modifications made by HIIT to the original source code are covered by 00471 the following license: 00472 00473 00474 Copyright 2009 Helsinki Institute for Information Technology (HIIT) 00475 and the authors. All rights reserved. 00476 00477 Authors: Tero Hasu <tero.hasu@hut.fi> 00478 00479 Permission is hereby granted, free of charge, to any person 00480 obtaining a copy of this software and associated documentation files 00481 (the "Software"), to deal in the Software without restriction, 00482 including without limitation the rights to use, copy, modify, merge, 00483 publish, distribute, sublicense, and/or sell copies of the Software, 00484 and to permit persons to whom the Software is furnished to do so, 00485 subject to the following conditions: 00486 00487 The above copyright notice and this permission notice shall be 00488 included in all copies or substantial portions of the Software. 00489 00490 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00491 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00492 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00493 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 00494 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 00495 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00496 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 00497 SOFTWARE. 00498 00499 **/
ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:56 2011 by Doxygen 1.6.1