up_poster_epoc.cpp

Go to the documentation of this file.
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