Back to index

lightning-sunbird  0.9+nobinonly
nsURLDataCallback.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Adam Lock <adamlock@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "stdafx.h"
00038 
00039 #include <process.h>
00040 
00041 #include "Pluginhostctrl.h"
00042 #include "nsPluginHostCtrl.h"
00043 #include "nsURLDataCallback.h"
00044 
00045 
00047 // nsURLDataCallback
00048 
00049 nsURLDataCallback::nsURLDataCallback() :
00050     m_pOwner(NULL),
00051     m_pNotifyData(NULL),
00052     m_szContentType(NULL),
00053     m_szURL(NULL),
00054     m_nDataPos(0),
00055     m_nDataMax(0),
00056     m_hPostData(NULL),
00057     m_bSaveToTempFile(FALSE),
00058     m_bNotifyOnWrite(TRUE),
00059     m_szTempFileName(NULL),
00060     m_pTempFile(NULL)
00061 {
00062     memset(&m_NPStream, 0, sizeof(m_NPStream));
00063     m_NPStream.ndata = this;
00064 }
00065 
00066 nsURLDataCallback::~nsURLDataCallback()
00067 {
00068     SetPostData(NULL, 0);
00069     SetURL(NULL);
00070     SetContentType(NULL);
00071     if (m_pTempFile)
00072     {
00073         fclose(m_pTempFile);
00074         remove(m_szTempFileName);
00075     }
00076     if (m_szTempFileName)
00077     {
00078         free(m_szTempFileName);
00079     }
00080 }
00081 
00082 void nsURLDataCallback::SetPostData(const void *pData, unsigned long nSize)
00083 {
00084     // Copy the post data into an HGLOBAL so it can be given to the 
00085     // bind object during the call to GetBindInfo
00086     if (m_hPostData)
00087     {
00088         GlobalFree(m_hPostData);
00089         m_hPostData = NULL;
00090     }
00091     if (pData)
00092     {
00093         m_hPostData = GlobalAlloc(GHND, nSize);
00094         if (m_hPostData)
00095         {
00096             void *pPostData = GlobalLock(m_hPostData);
00097             ATLASSERT(pPostData);
00098             memcpy(pPostData, pData, nSize);
00099             GlobalUnlock(m_hPostData);
00100         }
00101     }
00102 }
00103 
00104 HRESULT nsURLDataCallback::OpenURL(nsPluginHostCtrl *pOwner, const TCHAR *szURL, void *pNotifyData, const void *pPostData, unsigned long nPostDataSize)
00105 {
00106     // Create the callback object
00107     CComObject<nsURLDataCallback> *pCallback = NULL;
00108     CComObject<nsURLDataCallback>::CreateInstance(&pCallback);
00109     if (!pCallback)
00110     {
00111         return E_OUTOFMEMORY;
00112     }
00113     pCallback->AddRef();
00114 
00115     // Initialise it
00116     pCallback->SetOwner(pOwner);
00117     pCallback->SetNotifyData(pNotifyData);
00118     if (pPostData && nPostDataSize > 0)
00119     {
00120         pCallback->SetPostData(pPostData, nPostDataSize);
00121     }
00122 
00123     USES_CONVERSION;
00124     pCallback->SetURL(T2CA(szURL));
00125     
00126     // Create an object window on this thread that will be sent messages when
00127     // something happens on the worker thread.
00128     RECT rcPos = {0, 0, 10, 10};
00129     pCallback->Create(HWND_DESKTOP, rcPos);    
00130 
00131     // Start the worker thread
00132     _beginthread(StreamThread, 0, pCallback);
00133 
00134     return S_OK;
00135 }
00136 
00137 void __cdecl nsURLDataCallback::StreamThread(void *pData)
00138 {
00139     HRESULT hr = CoInitialize(NULL);
00140     ATLASSERT(SUCCEEDED(hr));
00141 
00142     CComObject<nsURLDataCallback> *pThis = (CComObject<nsURLDataCallback> *) pData;
00143 
00144     // Open the URL
00145     hr = URLOpenStream(NULL, pThis->m_szURL, 0, static_cast<IBindStatusCallback*>(pThis));
00146     ATLASSERT(SUCCEEDED(hr));
00147 
00148     // Pump messages until WM_QUIT arrives
00149     BOOL bQuit = FALSE;
00150     while (!bQuit)
00151     {
00152         MSG msg;
00153         if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
00154         {
00155             if (GetMessage(&msg, NULL, 0, 0))
00156             {
00157                 DispatchMessage(&msg);
00158             }
00159             else
00160             {
00161                 bQuit = TRUE;
00162             }
00163         }
00164     }
00165 
00166     CoUninitialize();
00167     _endthread();
00168 }
00169 
00171 // Windows message handlers
00172 
00173 LRESULT nsURLDataCallback::OnNPPNewStream(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
00174 {
00175     _NewStreamData *pNewStreamData = (_NewStreamData *) lParam;
00176 
00177     // Notify the plugin that a new stream has been created
00178     if (m_pOwner->m_NPPFuncs.newstream)
00179     {
00180         NPError npres = m_pOwner->m_NPPFuncs.newstream(
00181             pNewStreamData->npp,
00182             pNewStreamData->contenttype,
00183             pNewStreamData->stream,
00184             pNewStreamData->seekable,
00185             pNewStreamData->stype);
00186 
00187         // How does the plugin want its data?
00188         switch (*(pNewStreamData->stype))
00189         {
00190         case NP_NORMAL:
00191             m_bSaveToTempFile = FALSE;
00192             m_bNotifyOnWrite = TRUE;
00193             break;
00194         case NP_ASFILEONLY:
00195             m_bNotifyOnWrite = FALSE;
00196             m_bSaveToTempFile = TRUE;
00197             break;
00198         case NP_ASFILE:
00199             m_bNotifyOnWrite = TRUE;
00200             m_bSaveToTempFile = TRUE;
00201             break;
00202         case NP_SEEK:
00203             // TODO!!!
00204             ATLASSERT(0);
00205             break;
00206         }
00207     }
00208     return 0;
00209 }
00210 
00211 LRESULT nsURLDataCallback::OnNPPDestroyStream(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
00212 {
00213     _DestroyStreamData *pDestroyStreamData = (_DestroyStreamData *) lParam;
00214 
00215     // Tell the plugin the name of the temporary file containing the data
00216     if (m_bSaveToTempFile)
00217     {
00218         // Close the file
00219         if (m_pTempFile)
00220         {
00221             fclose(m_pTempFile);
00222         }
00223 
00224         // Determine whether the plugin should be told the name of the temp
00225         // file depending on whether it completed properly or not.
00226         char *szTempFileName = NULL;
00227         if (pDestroyStreamData->reason == NPRES_DONE &&
00228             m_pTempFile)
00229         {
00230             szTempFileName = m_szTempFileName;
00231         }
00232         
00233         // Notify the plugin
00234         if (m_pOwner->m_NPPFuncs.asfile)
00235         {
00236             m_pOwner->m_NPPFuncs.asfile(
00237                 pDestroyStreamData->npp,
00238                 pDestroyStreamData->stream,
00239                 szTempFileName);
00240         }
00241 
00242         // Remove the file if it wasn't passed into the plugin
00243         if (!szTempFileName ||
00244             !m_pOwner->m_NPPFuncs.asfile)
00245         {
00246             remove(szTempFileName);
00247         }
00248 
00249         // Cleanup strings & pointers
00250         if (m_szTempFileName)
00251         {
00252             free(m_szTempFileName);
00253             m_szTempFileName = NULL;
00254         }
00255         m_pTempFile = NULL;
00256     }
00257 
00258     // Notify the plugin that the stream has been closed
00259     if (m_pOwner->m_NPPFuncs.destroystream)
00260     {
00261         m_pOwner->m_NPPFuncs.destroystream(
00262             pDestroyStreamData->npp,
00263             pDestroyStreamData->stream,
00264             pDestroyStreamData->reason);
00265     }
00266 
00267     return 0;
00268 }
00269 
00270 LRESULT nsURLDataCallback::OnNPPURLNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
00271 {
00272     _UrlNotifyData *pUrlNotifyData = (_UrlNotifyData *) lParam;
00273 
00274     // Notify the plugin that the url has loaded
00275     if (m_pNotifyData && m_pOwner->m_NPPFuncs.urlnotify)
00276     {
00277         m_pOwner->m_NPPFuncs.urlnotify(
00278             pUrlNotifyData->npp,
00279             pUrlNotifyData->url,
00280             pUrlNotifyData->reason,
00281             pUrlNotifyData->notifydata);
00282     }
00283     return 0;
00284 }
00285 
00286 LRESULT nsURLDataCallback::OnNPPWriteReady(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
00287 {
00288     _WriteReadyData *pWriteReadyData = (_WriteReadyData *) lParam;
00289     if (m_pOwner->m_NPPFuncs.writeready &&
00290         m_bNotifyOnWrite)
00291     {
00292         pWriteReadyData->result = m_pOwner->m_NPPFuncs.writeready(
00293             pWriteReadyData->npp,
00294             pWriteReadyData->stream);
00295     }
00296 
00297     return 0;
00298 }
00299 
00300 LRESULT nsURLDataCallback::OnNPPWrite(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
00301 {
00302     _WriteData *pWriteData = (_WriteData *) lParam;
00303 
00304 #ifdef DUMP_STREAM_DATA_AS_HEX
00305     // Dumps out the data to the display so you can see it's correct as its
00306     // fed into the plugin.
00307     const int kLineBreakAfter = 16;
00308     const int kSpaceBreakAfter = 8;
00309     ATLTRACE(_T("nsURLDataCallback::OnNPPWrite()\n"));
00310     for (int i = 0; i < pWriteData->len; i++)
00311     {
00312         TCHAR szLine[100];
00313         TCHAR szTmp[20];
00314         if (i % kLineBreakAfter == 0)
00315         {
00316             _stprintf(szLine, _T("%04x  "), i);
00317         }
00318         unsigned char b = ((unsigned char *) pWriteData->buffer)[i];
00319         _stprintf(szTmp, _T("%02X "), (unsigned int) b);
00320         _tcscat(szLine, szTmp);
00321         
00322         if (i == pWriteData->len - 1 ||
00323             i % kLineBreakAfter == kLineBreakAfter - 1)
00324         {
00325             _tcscat(szLine, _T("\n"));
00326             ATLTRACE(szLine);
00327         }
00328         else if (i % kSpaceBreakAfter == kSpaceBreakAfter - 1)
00329         {
00330             _tcscat(szLine, _T(" "));
00331         }
00332     }
00333     ATLTRACE(_T("--\n"));
00334 #endif
00335 
00336     if (m_bSaveToTempFile)
00337     {
00338         if (!m_szTempFileName)
00339         {
00340             m_szTempFileName = strdup(_tempnam(NULL, "moz"));
00341         }
00342         if (!m_pTempFile)
00343         {
00344             m_pTempFile = fopen(m_szTempFileName, "wb");
00345         }
00346         ATLASSERT(m_pTempFile);
00347         if (m_pTempFile)
00348         {
00349             fwrite(pWriteData->buffer, 1, pWriteData->len, m_pTempFile);
00350         }
00351     }
00352 
00353     if (m_pOwner->m_NPPFuncs.write &&
00354         m_bNotifyOnWrite)
00355     {
00356         int32 nConsumed = m_pOwner->m_NPPFuncs.write(
00357             pWriteData->npp,
00358             pWriteData->stream,
00359             pWriteData->offset,
00360             pWriteData->len,
00361             pWriteData->buffer);
00362         if (nConsumed < 0)
00363         {
00364             // TODO destroy the stream!
00365         }
00366         ATLASSERT(nConsumed == pWriteData->len);
00367     }
00368     return 0;
00369 }
00370 
00371 LRESULT nsURLDataCallback::OnClassCreatePluginInstance(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
00372 {
00373     // Test whether the plugin for this content type exists or not and if not,
00374     // create it right now.
00375     if (!m_pOwner->m_bPluginIsAlive &&
00376         m_pOwner->m_bCreatePluginFromStreamData)
00377     {
00378         if (FAILED(m_pOwner->LoadPluginByContentType(A2CT(m_szContentType))) ||
00379             FAILED(m_pOwner->CreatePluginInstance()))
00380         {
00381             return 1;
00382         }
00383     }
00384     return 0;
00385 }
00386 
00387 LRESULT nsURLDataCallback::OnClassCleanup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
00388 {
00389     DestroyWindow();
00390     Release();
00391     return 0;
00392 }
00393 
00395 // IBindStatusCallback implementation
00396 
00397 HRESULT STDMETHODCALLTYPE nsURLDataCallback::OnStartBinding( 
00398     /* [in] */ DWORD dwReserved,
00399     /* [in] */ IBinding __RPC_FAR *pib)
00400 {
00401     ATLTRACE(_T("nsURLDataCallback::OnStartBinding()\n"));
00402     m_cpBinding = pib;
00403     return S_OK;
00404 }
00405 
00406 HRESULT STDMETHODCALLTYPE nsURLDataCallback::GetPriority( 
00407     /* [out] */ LONG __RPC_FAR *pnPriority)
00408 {
00409     return E_NOTIMPL;
00410 }
00411 
00412 HRESULT STDMETHODCALLTYPE nsURLDataCallback::OnLowResource( 
00413     /* [in] */ DWORD reserved)
00414 {
00415     return S_OK;
00416 }
00417 
00418 HRESULT STDMETHODCALLTYPE nsURLDataCallback::OnProgress( 
00419     /* [in] */ ULONG ulProgress,
00420     /* [in] */ ULONG ulProgressMax,
00421     /* [in] */ ULONG ulStatusCode,
00422     /* [in] */ LPCWSTR szStatusText)
00423 {
00424     switch (ulStatusCode)
00425     {
00426     case BINDSTATUS_BEGINDOWNLOADDATA:
00427     case BINDSTATUS_REDIRECTING:
00428         {
00429             USES_CONVERSION;
00430             SetURL(W2A(szStatusText));
00431         }
00432         break;
00433 
00434     case BINDSTATUS_MIMETYPEAVAILABLE:
00435         {
00436             USES_CONVERSION;
00437             SetContentType(W2A(szStatusText));
00438         }
00439         break;
00440     }
00441 
00442     m_nDataMax = ulProgressMax;
00443     return S_OK;
00444 }
00445 
00446 HRESULT STDMETHODCALLTYPE nsURLDataCallback::OnStopBinding( 
00447     /* [in] */ HRESULT hresult,
00448     /* [unique][in] */ LPCWSTR szError)
00449 {
00450     ATLTRACE(_T("nsURLDataCallback::OnStopBinding()\n"));
00451     if (m_pOwner && m_pOwner->m_bPluginIsAlive)
00452     {
00453         // TODO check for aborted ops and send NPRES_USER_BREAK
00454         NPReason reason = SUCCEEDED(hresult) ? NPRES_DONE : NPRES_NETWORK_ERR;
00455 
00456         // Notify the plugin that the stream has been closed
00457         _DestroyStreamData destroyStreamData;
00458         destroyStreamData.npp = &m_pOwner->m_NPP;
00459         destroyStreamData.stream = &m_NPStream;
00460         destroyStreamData.reason = reason;
00461         SendMessage(WM_NPP_DESTROYSTREAM, 0, (LPARAM) &destroyStreamData);
00462 
00463         // Notify the plugin that the url has loaded
00464         _UrlNotifyData urlNotifyData;
00465         urlNotifyData.npp = &m_pOwner->m_NPP;
00466         urlNotifyData.url = m_szURL;
00467         urlNotifyData.reason = reason;
00468         urlNotifyData.notifydata = m_pNotifyData;
00469         SendMessage(WM_NPP_URLNOTIFY, 0, (LPARAM) &urlNotifyData);
00470     }
00471 
00472     m_cpBinding.Release();
00473 
00474     SendMessage(WM_CLASS_CLEANUP);
00475     PostQuitMessage(0);
00476 
00477     return S_OK;
00478 }
00479 
00480 /* [local] */ HRESULT STDMETHODCALLTYPE nsURLDataCallback::GetBindInfo( 
00481     /* [out] */ DWORD __RPC_FAR *grfBINDF,
00482     /* [unique][out][in] */ BINDINFO __RPC_FAR *pbindinfo)
00483 {
00484     *grfBINDF = BINDF_ASYNCHRONOUS |  BINDF_ASYNCSTORAGE |
00485         BINDF_GETNEWESTVERSION;
00486     
00487     ULONG cbSize = pbindinfo->cbSize;
00488     memset(pbindinfo, 0, cbSize); // zero out structure
00489     pbindinfo->cbSize = cbSize;
00490     if (m_hPostData)
00491     {
00492         pbindinfo->dwBindVerb = BINDVERB_POST;
00493         pbindinfo->stgmedData.tymed = TYMED_HGLOBAL;
00494         pbindinfo->stgmedData.hGlobal = m_hPostData;
00495     }
00496     else
00497     {
00498         pbindinfo->dwBindVerb = BINDVERB_GET;
00499     }
00500 
00501     return S_OK ;
00502 }
00503 
00504 /* [local] */ HRESULT STDMETHODCALLTYPE nsURLDataCallback::OnDataAvailable( 
00505     /* [in] */ DWORD grfBSCF,
00506     /* [in] */ DWORD dwSize,
00507     /* [in] */ FORMATETC __RPC_FAR *pformatetc,
00508     /* [in] */ STGMEDIUM __RPC_FAR *pstgmed)
00509 {
00510     ATLTRACE(_T("nsURLDataCallback::OnDataAvailable()\n"));
00511     if (pstgmed->tymed != TYMED_ISTREAM ||
00512         !pstgmed->pstm)
00513     {
00514         return S_OK;
00515     }
00516     if (!m_pOwner)
00517     {
00518         return S_OK;
00519     }
00520 
00521     // Notify the plugin that a stream has been opened
00522     if (grfBSCF & BSCF_FIRSTDATANOTIFICATION)
00523     {
00524         USES_CONVERSION;
00525 
00526         // Test if there is a plugin yet. If not try and create one for this
00527         // kind of content.
00528         if (SendMessage(WM_CLASS_CREATEPLUGININSTANCE))
00529         {
00530             m_cpBinding->Abort();
00531             return S_OK;
00532         }
00533 
00534         // Tell the plugin that there is a new stream of data
00535         m_NPStream.url = m_szURL;
00536         m_NPStream.end = 0;
00537         m_NPStream.lastmodified = 0;
00538         m_NPStream.notifyData = m_pNotifyData;
00539 
00540         uint16 stype = NP_NORMAL;
00541         _NewStreamData newStreamData;
00542         newStreamData.npp = &m_pOwner->m_NPP;
00543         newStreamData.contenttype = m_szContentType;
00544         newStreamData.stream = &m_NPStream;
00545         newStreamData.seekable = FALSE;
00546         newStreamData.stype = &stype;
00547         SendMessage(WM_NPP_NEWSTREAM, 0, (LPARAM) &newStreamData);
00548     }
00549     if (!m_pOwner->m_bPluginIsAlive)
00550     {
00551         return S_OK;
00552     }
00553 
00554     m_NPStream.end = m_nDataMax;
00555 
00556     ATLTRACE(_T("Data for stream %s (%d of %d bytes are available)\n"), m_szURL, dwSize, m_NPStream.end);
00557 
00558     // Feed the stream data into the plugin
00559     HRESULT hr;
00560     char bData[16384];
00561     while (m_nDataPos < dwSize)
00562     {
00563         ULONG nBytesToRead = dwSize - m_nDataPos;
00564         ULONG nBytesRead = 0;
00565 
00566         if (nBytesToRead > sizeof(bData))
00567         {
00568             nBytesToRead = sizeof(bData);
00569         }
00570 
00571         // How many bytes can the plugin cope with?
00572         _WriteReadyData writeReadyData;
00573         writeReadyData.npp = &m_pOwner->m_NPP;
00574         writeReadyData.stream = &m_NPStream;
00575         writeReadyData.result = nBytesToRead;
00576         SendMessage(WM_NPP_WRITEREADY, 0, (LPARAM) &writeReadyData);
00577         if (nBytesToRead > writeReadyData.result)
00578         {
00579             nBytesToRead = writeReadyData.result;
00580         }
00581 
00582         // Read 'n' feed
00583         ATLTRACE(_T("  Reading %d bytes\n"), (int) nBytesToRead);
00584         hr = pstgmed->pstm->Read(&bData, nBytesToRead, &nBytesRead);
00585 
00586         _WriteData writeData;
00587         writeData.npp = &m_pOwner->m_NPP;
00588         writeData.stream = &m_NPStream;
00589         writeData.offset = m_nDataPos;
00590         writeData.len = nBytesRead;
00591         writeData.buffer = bData;
00592         SendMessage(WM_NPP_WRITE, 0, (LPARAM) &writeData);
00593 
00594         m_nDataPos += nBytesRead;
00595     }
00596 
00597     return S_OK;
00598 }
00599 
00600 HRESULT STDMETHODCALLTYPE nsURLDataCallback::OnObjectAvailable( 
00601     /* [in] */ REFIID riid,
00602     /* [iid_is][in] */ IUnknown __RPC_FAR *punk)
00603 {
00604     return S_OK;
00605 }
00606 
00608 // IAuthenticate implementation
00609 
00610 HRESULT STDMETHODCALLTYPE nsURLDataCallback::Authenticate( 
00611     /* [out] */ HWND __RPC_FAR *phwnd,
00612     /* [out] */ LPWSTR __RPC_FAR *pszUsername,
00613     /* [out] */ LPWSTR __RPC_FAR *pszPassword)
00614 {
00615     ATLTRACE(_T("nsURLDataCallback::Authenticate()\n"));
00616 
00617     // Caller wants to pose a password dialog so get a parent HWND for it.
00618     *phwnd = HWND_DESKTOP;
00619     if (m_pOwner)
00620     {
00621         *phwnd = m_pOwner->m_hWnd;
00622         CComPtr<IWebBrowserApp> cpBrowser;
00623         m_pOwner->GetWebBrowserApp(&cpBrowser);
00624         if (cpBrowser)
00625         {
00626             long hwnd = NULL;
00627             cpBrowser->get_HWND(&hwnd);
00628             if (hwnd)
00629             {
00630                 *phwnd = (HWND) hwnd;
00631             }
00632         }
00633     }
00634 
00635     // Caller will figure these out for itself
00636     *pszUsername = NULL;
00637     *pszPassword = NULL;
00638     
00639     return S_OK;
00640 }