/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ // network.cpp : implementation file // #include "stdafx.h" #include "cxnet1.h" ///////////////////////////////////////////////////////////////////////////// // Context functions XP_Bool CNetworkCX::Confirm(MWContext *pContext, const char *pConfirmMessage) { // Always return true, we don't want any dialogs coming up. // Don't call the base. // This will cause the prompt functions to be called multiple times as // needed since both the netlib and this object cache different // passwords. return(TRUE); } void CNetworkCX::Alert(MWContext *pContext, const char *pMessage) { // Hand over this in the error string, even if it's not an error. // Can't have dialogs popping up everywhere. m_lFlags |= m_ERRS; m_csErrorMessage = pMessage; // Do not call the base or a dialog comes up. } char *CNetworkCX::Prompt(MWContext *pContext, const char *pPrompt, const char *pDefault) { // So, LJM tells me that this function is used to query for a username. // Do that here. // Only if not already asked for during this load. if((m_lFlags & m_USER) == 0) { SetUsernameRequested(); return(AllocUsername()); } return(NULL); } char *CNetworkCX::PromptPassword(MWContext *pContext, const char *pMessage) { // The caller should have proactively set the password before an Open(). // If not, then they will have to figure it out. // Mark that a password was required for the transfer. // Only if not already asked for during this load. if((m_lFlags & m_PASS) == 0) { SetPasswordRequested(); return(AllocPassword()); } return(NULL); } XP_Bool CNetworkCX::PromptUsernameAndPassword(MWContext *pContext, const char *pMessage, char **ppUsername, char **ppPassword) { // Initialize. *ppPassword = NULL; *ppUsername = NULL; // If both have already been asked for, don't continue. if((m_lFlags & m_USER) != 0 && (m_lFlags & m_PASS) != 0) { return(FALSE); } // Prompt for both username and password. // Default values are given, and if not already specified by the caller, then we can just use them. // Set that both a username and a password were required for the transfer. SetPasswordRequested(); SetUsernameRequested(); // Copy and set any user/password values that the controller has preemptively set. char *pPass = AllocPassword(); if(pPass != NULL) { *ppPassword = pPass; } char *pUser = AllocUsername(); if(pUser != NULL) { *ppUsername = pUser; } return(TRUE); } XP_Bool CNetworkCX::ShowAllNewsArticles(MWContext *pContext) { // Set to show all the news articles. return(GetFlagShowAllNews()); } XP_Bool CNetworkCX::UseFancyFTP(MWContext *pContext) { // Check to see if we are to use Fancy FTP. return(GetFlagFancyFTP()); } XP_Bool CNetworkCX::UseFancyNewsgroupListing(MWContext *pContext) { // See if we should use full newsgroup listings with descriptions. return(GetFlagFancyNews()); } #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CNetworkCX #ifndef _AFXDLL #undef new #endif IMPLEMENT_DYNCREATE(CNetworkCX, CCmdTarget) #ifndef _AFXDLL #define new DEBUG_NEW #endif CNetworkCX::CNetworkCX() { EnableAutomation(); // To keep the application running as long as an OLE automation // object is active, the constructor calls AfxOleLockApp. AfxOleLockApp(); // Set the type of context that we are. m_cxType = Network; GetContext()->type = MWContextOleNetwork; // Initialize our URL property, other members won't work unless this is defined. m_pUrlData = NULL; // Our buffer is initially empty. ASSERT(m_cplBuffers.IsEmpty()); // We're done loading any stream, as none have started. m_bStreamComplete = TRUE; // We don't want to initially show all news articles. m_bShowAllNews = FALSE; // We do want to use Fancy News and Fancy FTP by default. m_bFancyNews = TRUE; m_bFancyFTP = TRUE; // We've no current status flags. m_lFlags = m_OK; // Username and password should be empty initially anyhow. TRACE("Netscape.Network.1 started\n"); } CNetworkCX::~CNetworkCX() { // To terminate the application when all objects created with // with OLE automation, the destructor calls AfxOleUnlockApp. AfxOleUnlockApp(); TRACE("Netscape.Network.1 ended\n"); } void CNetworkCX::OnFinalRelease() { // When the last reference for an automation object is released // OnFinalRelease is called. This implementation deletes the // object. Add additional cleanup required for your object before // deleting it from memory. // If we have a currently loading stream, we need to do cleanup there. if(m_pUrlData != NULL) { Close(); } DestroyContext(); } char *CNetworkCX::AllocUsername() { // See if we even have a username to allocate. if(m_csUsername.IsEmpty() == TRUE) { return(NULL); } return(XP_STRDUP(m_csUsername)); } char *CNetworkCX::AllocPassword() { // See if we even have a password to allocate. if(m_csPassword.IsEmpty() == TRUE) { return(NULL); } return(XP_STRDUP(m_csPassword)); } BEGIN_MESSAGE_MAP(CNetworkCX, CCmdTarget) //{{AFX_MSG_MAP(CNetworkCX) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP() BEGIN_DISPATCH_MAP(CNetworkCX, CCmdTarget) //{{AFX_DISPATCH_MAP(CNetworkCX) DISP_PROPERTY_EX(CNetworkCX, "Username", GetUsername, SetUsername, VT_BSTR) DISP_PROPERTY_EX(CNetworkCX, "Password", GetPassword, SetPassword, VT_BSTR) DISP_PROPERTY_EX(CNetworkCX, "FlagShowAllNews", GetFlagShowAllNews, SetFlagShowAllNews, VT_BOOL) DISP_PROPERTY_EX(CNetworkCX, "FlagFancyFTP", GetFlagFancyFTP, SetFlagFancyFTP, VT_BOOL) DISP_PROPERTY_EX(CNetworkCX, "FlagFancyNews", GetFlagFancyNews, SetFlagFancyNews, VT_BOOL) DISP_FUNCTION(CNetworkCX, "Close", Close, VT_EMPTY, VTS_NONE) DISP_FUNCTION(CNetworkCX, "Read", Read, VT_I2, VTS_PBSTR VTS_I2) DISP_FUNCTION(CNetworkCX, "GetStatus", GetStatus, VT_I4, VTS_NONE) DISP_FUNCTION(CNetworkCX, "Open", Open, VT_BOOL, VTS_BSTR VTS_I2 VTS_BSTR VTS_I4 VTS_BSTR) DISP_FUNCTION(CNetworkCX, "GetErrorMessage", GetErrorMessage, VT_BSTR, VTS_NONE) DISP_FUNCTION(CNetworkCX, "GetServerStatus", GetServerStatus, VT_I2, VTS_NONE) DISP_FUNCTION(CNetworkCX, "GetContentLength", GetContentLength, VT_I4, VTS_NONE) DISP_FUNCTION(CNetworkCX, "GetContentType", GetContentType, VT_BSTR, VTS_NONE) DISP_FUNCTION(CNetworkCX, "GetContentEncoding", GetContentEncoding, VT_BSTR, VTS_NONE) DISP_FUNCTION(CNetworkCX, "GetExpires", GetExpires, VT_BSTR, VTS_NONE) DISP_FUNCTION(CNetworkCX, "GetLastModified", GetLastModified, VT_BSTR, VTS_NONE) DISP_FUNCTION(CNetworkCX, "Resolve", Resolve, VT_BSTR, VTS_BSTR VTS_BSTR) DISP_FUNCTION(CNetworkCX, "IsFinished", IsFinished, VT_BOOL, VTS_NONE) DISP_FUNCTION(CNetworkCX, "BytesReady", BytesReady, VT_I2, VTS_NONE) //}}AFX_DISPATCH_MAP END_DISPATCH_MAP() IMPLEMENT_OLECREATE(CNetworkCX, "Netscape.Network.1", 0xef5f7050, 0x385a, 0x11ce, 0x81, 0x93, 0x0, 0x20, 0xaf, 0x18, 0xf9, 0x5) ///////////////////////////////////////////////////////////////////////////// // CNetworkCX message handlers BOOL CNetworkCX::Open(LPCTSTR pURL, short iMethod, LPCTSTR pPostData, long lPostDataSize, LPCTSTR pPostHeaders) { // Reset our status flags. m_lFlags = m_OK; m_csErrorMessage.Empty(); // See if we're busy. if(winfeInProcessNet == TRUE) { m_lFlags |= m_BUSY; return(FALSE); } // If we're handling a request already, shut it down. if(m_pUrlData != NULL) { Close(); } // Create the URL we want to load. m_pUrlData = NET_CreateURLStruct(pURL, NET_DONT_RELOAD); // Create the ncapi data for the URL. // Don't let it free off the URL in the exit routine. // This is done in close. // A pointer to the class is saved in the URL struct, and will // be freed off in FE_DeleteUrlData. CNcapiUrlData *pDontCare = new CNcapiUrlData(this, m_pUrlData); pDontCare->DontFreeUrl(); // Set the method for the load. // GET 0 // POST 1 // HEAD 3 m_pUrlData->method = iMethod; // Contruct the post stuff. if(pPostData != NULL && strlen(pPostData) != 0 && m_pUrlData->method == 1) { m_pUrlData->post_data = (char *)XP_ALLOC(lPostDataSize); memcpy(m_pUrlData->post_data, pPostData, CASTSIZE_T(lPostDataSize)); m_pUrlData->post_data_size = lPostDataSize; if(pPostHeaders != NULL && strlen(pPostHeaders) != 0) { m_pUrlData->post_headers = (char *)XP_ALLOC(strlen(pPostHeaders) + 1); memcpy(m_pUrlData->post_headers, pPostHeaders, strlen(pPostHeaders) + 1); } else { m_pUrlData->post_headers = strdup("Content-type: application/x-www-form-urlencoded"); } StrAllocCat(m_pUrlData->post_headers, CRLF); // Manually add the content-length. // This was done automatically in versions of Netscape prior to 2.0. char aBuffer[1024]; sprintf(aBuffer, "Content-length: %ld", lPostDataSize); StrAllocCat(m_pUrlData->post_headers, aBuffer); StrAllocCat(m_pUrlData->post_headers, CRLF); } // Finally, set that the stream is not yet completed. m_bStreamComplete = FALSE; // Need to request a URL for a particular format out, in our special context. // Any errors should be caught in the exit routine. GetUrl(m_pUrlData, FO_CACHE_AND_OLE_NETWORK); return TRUE; } short CNetworkCX::Read(BSTR FAR* pBuffer, short iAmount) { // First, check for end of file condition. if(m_cplBuffers.IsEmpty() && m_bStreamComplete == TRUE) { return(-1); } // If the buffer's empty, we should just return right now. if(m_cplBuffers.IsEmpty()) { return(0); } // We'll want to either copy over the amount of data they are asking for, // or copy over how much data we have in the buffer, // whichever is less. // To do this, we need to figure out the amount of data in the buffers first // entry only. // Get the first entry out. CNetBuffer *pNetBuffer = (CNetBuffer *)m_cplBuffers.GetHead(); // See how much data we should be handling here. int iBuffer = pNetBuffer->m_iSize - pNetBuffer->m_iHead; ASSERT(iBuffer); iAmount = min(iBuffer, iAmount); if(iAmount <= 0) { return(0); } // Copy over that amount from our buffer. #if !defined(_UNICODE) && !defined(OLE2ANSI) && defined(MSVC4) // Need to encode unicode ourselves. MultiByteToWideChar(CP_ACP, 0, (pNetBuffer->m_pData + pNetBuffer->m_iHead), iAmount, *pBuffer, sizeof(OLECHAR) * iAmount); #ifdef DEBUG // Here's some extra goodies to make sure the conversion isn't lossy, as we can also transfer binary data. // Can we reverse it correctly? char *pCompare = new char[iAmount]; int iConvert = WideCharToMultiByte(CP_ACP, 0, *pBuffer, iAmount, pCompare, iAmount, NULL, NULL); ASSERT(iConvert); // Was the conversion correct? int iCompare = memcmp(pCompare, (pNetBuffer->m_pData + pNetBuffer->m_iHead), iAmount); ASSERT(!iCompare); delete [] pCompare; #endif #else memcpy(*pBuffer, (pNetBuffer->m_pData + pNetBuffer->m_iHead), iAmount); #endif pNetBuffer->m_iHead += iAmount; ASSERT(pNetBuffer->m_iHead <= pNetBuffer->m_iSize); // See if we should get rid of the buffer entry if completely read now. if(pNetBuffer->m_iHead == pNetBuffer->m_iSize) { // Get rid of it. m_cplBuffers.RemoveHead(); delete pNetBuffer; } // Return the amount read. return(iAmount); } void CNetworkCX::Close() { // Destroy the URL, if we have one. if(m_pUrlData != NULL) { // We really should check for reentrancy, but can't, as this event could happen beyond our control, such // as the controlling application deleting their automation object. // Only do this if we are actually loading, as netlib has probably unregistered this otherwise... if(m_bStreamComplete == FALSE) { BOOL bOld = winfeInProcessNet; winfeInProcessNet = TRUE; NET_InterruptStream(m_pUrlData); winfeInProcessNet = bOld; } NET_FreeURLStruct(m_pUrlData); m_pUrlData = NULL; } // Reset the end of the buffer, our stutus, et al. CNetBuffer *pDelMe; while(m_cplBuffers.IsEmpty() == FALSE) { pDelMe = (CNetBuffer *)m_cplBuffers.RemoveHead(); delete pDelMe; } m_lFlags = m_OK; m_csErrorMessage.Empty(); m_bStreamComplete = TRUE; } BSTR CNetworkCX::GetUsername() { // Return what the current username is. // Up to caller to free the information. return m_csUsername.AllocSysString(); } void CNetworkCX::SetUsername(LPCTSTR lpszNewValue) { // Modify the current username to be something new. if(lpszNewValue == NULL) { m_csUsername.Empty(); return; } m_csUsername = lpszNewValue; } BSTR CNetworkCX::GetPassword() { // Return the current password setting. // Up to caller to free the information. return m_csPassword.AllocSysString(); } void CNetworkCX::SetPassword(LPCTSTR lpszNewValue) { // Set the password to something new. if(lpszNewValue == NULL) { m_csPassword.Empty(); return; } m_csPassword = lpszNewValue; } long CNetworkCX::GetStatus() { // Give them the current status flags since the last open occurred. return(m_lFlags); } BOOL CNetworkCX::GetFlagShowAllNews() { return m_bShowAllNews; } void CNetworkCX::SetFlagShowAllNews(BOOL bNewValue) { m_bShowAllNews = bNewValue; } BOOL CNetworkCX::GetFlagFancyFTP() { return m_bFancyFTP; } void CNetworkCX::SetFlagFancyFTP(BOOL bNewValue) { m_bFancyFTP = bNewValue; } BOOL CNetworkCX::GetFlagFancyNews() { return m_bFancyNews; } void CNetworkCX::SetFlagFancyNews(BOOL bNewValue) { m_bFancyNews = bNewValue; } int CNetworkCX::StreamWrite(const char *pWriteData, int32 lLength) { // if we don't have a url, then don't do this. if(m_pUrlData == NULL) { return(-1); } // We should never get called to copy over more data than is reported by StreamReady. // However, we don't care anymore. // Allocate a new buffer in which to store the data. ASSERT(lLength <= NETBUFSIZE); CNetBuffer *pNewBuf = new CNetBuffer(CASTINT(lLength)); memcpy(pNewBuf->m_pData, pWriteData, pNewBuf->m_iSize); // Add it to the tail of our buffer list. m_cplBuffers.AddTail((void *)pNewBuf); return(MK_DATA_LOADED); } unsigned int CNetworkCX::StreamReady() { // If we don't have a URL, we won't take data. if(m_pUrlData == NULL) { return(0); } // If we already have n entries in our buffered data, don't do this. // We can however take more if the netlib screws up due to this new // buffer system. if(m_cplBuffers.IsEmpty() == FALSE && m_cplBuffers.GetCount() >= 10) { return(0); } // Return our max allowed size, don't really care if they fulfill this // completely. return(NETBUFSIZE); } void CNetworkCX::StreamComplete() { // Function not utilized. // Stream complete considered to be the URL exit routine. } void CNetworkCX::StreamAbort(int iStatus) { // Function not utilized. // Stream complete considered to be the URL exit routine. } void CNetworkCX::GetUrlExitRoutine(URL_Struct *pUrl, int iStatus, MWContext *pContext) { // URL is done loading. m_bStreamComplete = TRUE; // Check for internal loading errors. if(iStatus != MK_DATA_LOADED) { m_lFlags |= m_INTL; } else if(pUrl->server_status / 100 != 2 && pUrl->server_status / 100 != 3 && iStatus == MK_DATA_LOADED && pUrl->server_status != 0) { m_lFlags |= m_SRVR; } // If we have any error message what so ever, then we will save it here and set that an error occurred. if(m_lFlags & (m_SRVR | m_INTL)) { if(pUrl->error_msg != NULL) { if(strlen(pUrl->error_msg) != 0) { m_lFlags |= m_ERRS; m_csErrorMessage = pUrl->error_msg; } } } // Call the base. CStubsCX::GetUrlExitRoutine(pUrl, iStatus, pContext); } BSTR CNetworkCX::GetErrorMessage() { // Simply return any error message that we currently have. return m_csErrorMessage.AllocSysString(); } extern "C" { NET_StreamClass *nfe_OleStream(int iFormatOut, void *pDataObj, URL_Struct *pUrlData, MWContext *pContext) { // Return a new stream class, pass the object along for the ride. return(NET_NewStream("Netscape_Network_1", nfe_StreamWrite, nfe_StreamComplete, nfe_StreamAbort, nfe_StreamReady, CX2VOID(pContext->fe.cx, CNetworkCX), pContext)); } int nfe_StreamWrite(NET_StreamClass *stream, const char *pWriteData, int32 lLength) { void *pDataObj=stream->data_object; CNetworkCX *pOle = VOID2CX(pDataObj, CNetworkCX); // Have our object handle it. return(pOle->StreamWrite(pWriteData, lLength)); } void nfe_StreamComplete(NET_StreamClass *stream) { void *pDataObj=stream->data_object; CNetworkCX *pOle = VOID2CX(pDataObj, CNetworkCX); // Have our object handle it. pOle->StreamComplete(); } void nfe_StreamAbort(NET_StreamClass *stream, int iStatus) { void *pDataObj=stream->data_object; CNetworkCX *pOle = VOID2CX(pDataObj, CNetworkCX); // Have our object handle it. pOle->StreamAbort(iStatus); } unsigned int nfe_StreamReady(NET_StreamClass *stream) { void *pDataObj=stream->data_object; CNetworkCX *pOle = VOID2CX(pDataObj, CNetworkCX); // Have our object handle it. return(pOle->StreamReady()); } }; short CNetworkCX::GetServerStatus() { // Don't do this if we don't have a URL. if(m_pUrlData == NULL) { return(-1); } else if(m_bStreamComplete != TRUE) { // Also can't do this unless the load is done. return(-1); } return(m_pUrlData->server_status); } long CNetworkCX::GetContentLength() { // Don't do this if we don't have a URL. if(m_pUrlData == NULL) { return(-1); } return(m_pUrlData->content_length); } BSTR CNetworkCX::GetContentType() { // Don't do this if we don't have a URL. if(m_pUrlData == NULL) { return(NULL); } else if(m_pUrlData->content_type == NULL) { return(NULL); } else if(strlen(m_pUrlData->content_type) == 0) { return(NULL); } CString s = m_pUrlData->content_type; return s.AllocSysString(); } BSTR CNetworkCX::GetContentEncoding() { // Don't do this if we don't have a URL. if(m_pUrlData == NULL) { return(NULL); } else if(m_pUrlData->content_encoding == NULL) { return(NULL); } else if(strlen(m_pUrlData->content_encoding) == 0) { return(NULL); } CString s = m_pUrlData->content_encoding; return s.AllocSysString(); } BSTR CNetworkCX::GetExpires() { // Don't do this if we don't have a URL. if(m_pUrlData == NULL) { return(NULL); } else if(m_pUrlData->expires == (time_t)0) { return(NULL); } char *pTime = ctime(&(m_pUrlData->expires)); if(pTime == NULL) { return(NULL); } CString s = pTime; return s.AllocSysString(); } BSTR CNetworkCX::GetLastModified() { // Don't do this if we don't have a URL. if(m_pUrlData == NULL) { return(NULL); } else if(m_pUrlData->last_modified == (time_t)0) { return(NULL); } char *pTime = ctime(&(m_pUrlData->last_modified)); if(pTime == NULL) { return(NULL); } CString s = pTime; return s.AllocSysString(); } BSTR CNetworkCX::Resolve(LPCTSTR pBase, LPCTSTR pRelative) { // Have the netlib resolve the url stuff for us. char *cpURL = NET_MakeAbsoluteURL((char *)pBase, (char *)pRelative); if(cpURL == NULL) { return(NULL); } CString s = cpURL; XP_FREE(cpURL); return s.AllocSysString(); } BOOL CNetworkCX::IsFinished() { // Check for end of file condition. if(m_cplBuffers.IsEmpty() && m_bStreamComplete == TRUE) { return(TRUE); } return(FALSE); } short CNetworkCX::BytesReady() { // Check to see if there's any load ready. if(m_cplBuffers.IsEmpty() && m_bStreamComplete == TRUE) { return(0); } // Return the number of bytes we are currently ready to dish out. CNetBuffer *pReady = (CNetBuffer *)m_cplBuffers.GetHead(); int iBuffer = pReady->m_iSize - pReady->m_iHead; ASSERT(iBuffer); return(iBuffer); }