gecko-dev/cmd/winfe/cxnet1.cpp
1998-03-28 02:44:41 +00:00

734 lines
20 KiB
C++

/* -*- 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);
}