/* -*- 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 "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are Copyright (C) 1998
 * Netscape Communications Corporation.  All Rights Reserved.
 */

#include "nsRepository.h"
#include "nsITimer.h"
#include "nsNetService.h"
#include "nsNetStream.h"
#include "nsNetFile.h"
#include "prefapi.h"
#include "mkprefs.h"
extern "C" {
#include "mkutils.h"
#include "mkgeturl.h"
#include "mktrace.h"
#include "mkstream.h"
#include "cvchunk.h"
#include "httpurl.h"
} /* end of extern "C" */

#include "netcache.h"
#include "cookies.h"
#include "plstr.h"

#include "nsString.h"
#include "nsIProtocolConnection.h"

#ifdef XP_PC
#include <windows.h>
static HINSTANCE g_hInst = NULL;
#endif

/*
** Define TIMEBOMB_ON for beta builds.
** Undef TIMEBOMB_ON for release builds.
*/
/*#define TIMEBOMB_ON*/
#undef TIMEBOMB_ON

/*
** After this date all hell breaks loose
*/
#ifdef TIMEBOMB_ON
#define TIME_BOMB_TIME          917856001	/* 2/01/98 + 1 secs */
#define TIME_BOMB_WARNING_TIME  915955201	/* 1/10/98 + 1 secs */
#else
#define TIME_BOMB_TIME          -1
#define TIME_BOMB_WARNING_TIME  -1
#endif


// Declare the nsFile struct here so it's state is initialized before
// we initialize netlib.
#ifdef NS_NET_FILE

#include "nsNetFile.h"
#ifdef XP_MAC
static nsNetFileInit* netFileInit = nsnull;
#else
static nsNetFileInit netFileInit;
#endif

#endif // NS_NET_FILE
// End nsFile specific

/* XXX: Legacy definitions... */
// Global count of active urls from mkgeturl.c
extern "C" int NET_TotalNumberOfProcessingURLs;

extern "C" HTTP_Version DEFAULT_VERSION;

extern "C" void net_AddrefContext(MWContext* window_id);
MWContext *new_stub_context(URL_Struct *URL_s);
void free_stub_context(MWContext *window_id);
static void bam_exit_routine(URL_Struct *URL_s, int status, MWContext *window_id);

#if defined(XP_WIN) && !defined(NETLIB_THREAD)
nsresult PerformNastyWindowsAsyncDNSHack(URL_Struct* URL_s, nsIURL* aURL);
#endif /* XP_WIN && !NETLIB_THREAD */

char *mangleResourceIntoFileURL(const char* aResourceFileName) ;
extern nsIStreamListener* ns_NewStreamListenerProxy(nsIStreamListener* aListener);

extern "C" {
#if defined(XP_WIN) || defined(XP_OS2)
extern char *XP_AppCodeName;
extern char *XP_AppVersion;
extern char *XP_AppName;
extern char *XP_AppLanguage;
extern char *XP_AppPlatform;
#else
extern const char *XP_AppCodeName;
extern const char *XP_AppVersion;
extern const char *XP_AppName;
extern const char *XP_AppLanguage;
extern const char *XP_AppPlatform;
#endif

} /* end of extern "C" */

static NS_DEFINE_IID(kIProtocolConnectionIID,  NS_IPROTOCOLCONNECTION_IID);


nsNetlibService::nsNetlibService()
{
    NS_INIT_REFCNT();

    /*
      m_stubContext = new_stub_context();
    */
    mPollingTimer = nsnull;
    
    mNetlibThread = new nsNetlibThread();
    if (nsnull != mNetlibThread) {
        mNetlibThread->Start();
    }

    /* Setup our default prefs. Eventually these will come out of a default
     * all.js file, but, for now each module needs to address their own 
     * default settings. */

    PREF_SetDefaultIntPref(pref_proxyType, 3);
    PREF_SetDefaultCharPref(pref_proxyACUrl, "");
    PREF_SetDefaultCharPref(pref_socksServer, "");
    PREF_SetDefaultIntPref(pref_socksPort, 0);
    PREF_SetDefaultCharPref(pref_proxyFtpServer, "");
    PREF_SetDefaultIntPref(pref_proxyFtpPort, 0);
    PREF_SetDefaultCharPref(pref_proxyGopherServer, "");
    PREF_SetDefaultIntPref(pref_proxyGopherPort, 0);
    PREF_SetDefaultCharPref(pref_proxyHttpServer, "");
    PREF_SetDefaultIntPref(pref_proxyHttpPort, 0);
    PREF_SetDefaultCharPref(pref_proxyNewsServer, "");
    PREF_SetDefaultIntPref(pref_proxyNewsPort, 0);
    PREF_SetDefaultCharPref(pref_proxyWaisServer, "");
    PREF_SetDefaultIntPref(pref_proxyWaisPort, 0);
    PREF_SetDefaultCharPref(pref_proxyNoProxiesOn, "");
    PREF_SetDefaultCharPref(pref_padPacURL, "");
    PREF_SetDefaultCharPref(pref_scriptName, "");

    PREF_SetDefaultIntPref("timebomb.expiration_time",TIME_BOMB_TIME);
    PREF_SetDefaultIntPref("timebomb.warning_time",   TIME_BOMB_WARNING_TIME);

    // XXX: Where should the defaults really come from
    XP_AppCodeName = PL_strdup("Mozilla");
    XP_AppName     = PL_strdup("Netscape");

    /* 
     * XXX: Some of these should come from resources and/or 
     * platform-specific code. 
     */
    XP_AppLanguage = PL_strdup("en");
#ifdef XP_WIN
    XP_AppPlatform = PL_strdup("Win95");
#elif defined(XP_MAC)
    XP_AppPlatform = PL_strdup("MacPPC");
#elif defined(XP_UNIX)
    /* XXX: Need to differentiate between various Unisys */
    XP_AppPlatform = PL_strdup("Unix");
#endif

    /* XXXXX TEMPORARY TESTING HACK XXXXX */
    char buf[64];
    char *ver = PR_GetEnv("NG_REQUEST_VER");
    /* Build up the appversion. */
    sprintf(buf, "%s [%s] (%s; I)",
        (ver ? ver : "4.05"), 
        XP_AppLanguage, 
        XP_AppPlatform);
    if (XP_AppVersion)
        PR_Free((char *)XP_AppVersion);
    XP_AppVersion = PL_strdup(buf);
}


NS_DEFINE_IID(kINetServiceIID, NS_INETSERVICE_IID);
NS_IMPL_THREADSAFE_ISUPPORTS(nsNetlibService,kINetServiceIID);


nsNetlibService::~nsNetlibService()
{
    TRACEMSG(("nsNetlibService is being destroyed...\n"));

    /*
      if (NULL != m_stubContext) {
        free_stub_context((MWContext *)m_stubContext);
        m_stubContext = NULL;
      }
    */

    NS_IF_RELEASE(mPollingTimer);

    if (nsnull != mNetlibThread) {
        mNetlibThread->Stop();
        delete mNetlibThread;
    }
}



void nsNetlibService::SetupURLStruct(nsIURL *aUrl, URL_Struct *aURL_s) 
{
  nsresult rv;
  nsILoadAttribs* loadAttribs = aUrl->GetLoadAttribs();


  /* If this url has load attributes, setup the underlying url struct
   * accordingly. */
  if (loadAttribs) {
    nsURLLoadType loadType;
    nsURLReloadType reloadType;
    PRUint32 localIP;

    NS_VERIFY_THREADSAFE_INTERFACE(loadAttribs);

    rv = loadAttribs->GetReloadType(&reloadType);
    if (NS_FAILED(rv)) {
      reloadType = nsURLReload;
    }
    if ((reloadType == nsURLReloadBypassProxy) ||
        (reloadType == nsURLReloadBypassCacheAndProxy)) {
      PRBool bBypassProxy;

      rv = loadAttribs->GetBypassProxy(&bBypassProxy);
      if (NS_FAILED(rv)) {
        bBypassProxy = PR_FALSE;
      }
      aURL_s->bypassProxy = bBypassProxy;
    }
    /* Set the NET_ReloadMethod to correspond with what we've 
     * been asked to do.
     *
     * 0 = nsURLReload (normal)
     * 1 = nsURLReloadBypassCache
     * 2 = nsURLReloadBypassProxy
     * 3 = nsURLReloadBypassCacheAndProxy
     */
    if ((reloadType == nsURLReloadBypassCache) ||
        (reloadType == nsURLReloadBypassCacheAndProxy)) {
      aURL_s->force_reload = NET_SUPER_RELOAD;
    } else {
      aURL_s->force_reload = NET_NORMAL_RELOAD;
    }

    rv = loadAttribs->GetLoadType(&loadType);
    if (NS_FAILED(rv)) {
      loadType = nsURLLoadNormal;
    }
    if (loadType == nsURLLoadBackground) {
      aURL_s->load_background = PR_TRUE;
    } else {
      aURL_s->load_background = PR_FALSE;
    }

    rv = loadAttribs->GetLocalIP(&localIP);
    if (NS_FAILED(rv)) {
      localIP = 0;
    }
    aURL_s->localIP = localIP;

    NS_RELEASE(loadAttribs);
  }
}

nsresult nsNetlibService::OpenStream(nsIURL *aUrl, 
                                     nsIStreamListener *aConsumer)
{
    URL_Struct *URL_s;
    nsConnectionInfo *pConn;
    nsIProtocolConnection *pProtocol;
    nsresult result;
    nsIStreamListener* consumer;

    if ((NULL == aConsumer) || (NULL == aUrl)) {
        return NS_FALSE;
    }

#if defined(NETLIB_THREAD)
    consumer = ns_NewStreamListenerProxy(aConsumer);
    if (nsnull == consumer) {
        return NS_FALSE;
    }
#else
    consumer = aConsumer;
#endif

    /* Create the nsConnectionInfo object... */
    pConn = new nsConnectionInfo(aUrl, NULL, consumer);
    if (NULL == pConn) {
        return NS_FALSE;
    }
    NS_ADDREF(pConn);

    /* 
     * XXX: Rewrite the resource: URL into a file: URL
     */
    if (PL_strcmp(aUrl->GetProtocol(), "resource") == 0) {
      char* fileName;

      fileName = mangleResourceIntoFileURL(aUrl->GetFile());
      aUrl->Set(fileName);
      PR_Free(fileName);
    } 

    /* Create the URLStruct... */

    URL_s = NET_CreateURLStruct(aUrl->GetSpec(), NET_NORMAL_RELOAD);
    if (NULL == URL_s) {
        NS_RELEASE(pConn);
        return NS_FALSE;
    }

    SetupURLStruct(aUrl, URL_s);

    /*
     * Attach the Data Consumer to the URL_Struct.
     *
     * Both the Data Consumer and the URL_Struct are freed in the 
     * bam_exit_routine(...)
     * 
     * The Reference count on pConn is already 1 so no AddRef() is necessary.
     */
    URL_s->fe_data = pConn;

    /*
     * Give the protocol a chance to initialize any URL_Struct fields...
     *
     * XXX:  Currently the return value form InitializeURLInfo(...) is 
     *       ignored...  Should the connection abort if it fails?
     */
    result = aUrl->QueryInterface(kIProtocolConnectionIID, (void**)&pProtocol);
    if (NS_OK == result) {
        pProtocol->InitializeURLInfo(URL_s);
        NS_RELEASE(pProtocol);
    }
    
    /* Create a new Context and set its reference count to one.. */
    MWContext *stubContext = new_stub_context(URL_s);
    net_AddrefContext(stubContext);

    /* Check for timebomb...*/
#ifdef TIMEBOMB_ON
    NET_CheckForTimeBomb(stubContext);
#endif /* TIMEBOMB_ON */

    /* Start the URL load... */
    NET_GetURL (URL_s,                      /* URL_Struct      */
                FO_CACHE_AND_NGLAYOUT,      /* FO_Present_type */
                (MWContext *)stubContext,   /* MWContext       */
                bam_exit_routine);          /* Exit routine... */

    /* Remember, the URL_s may have been freed ! */

    /* 
     * Start the network timer to call NET_PollSockets(...) until the
     * URL has been completely loaded...
     */
    SchedulePollingTimer();
    return NS_OK;
}


nsresult nsNetlibService::OpenBlockingStream(nsIURL *aUrl, 
                                              nsIStreamListener *aConsumer,
                                              nsIInputStream **aNewStream)
{
    URL_Struct *URL_s;
    nsConnectionInfo *pConn;
    nsNetlibStream *pBlockingStream;
    nsIProtocolConnection *pProtocol;
    nsIStreamListener* consumer = nsnull;
    nsresult result;

    if (NULL == aNewStream) {
        return NS_FALSE;
    }

    if (NULL != aUrl) {
#if defined(NETLIB_THREAD)
        if (nsnull != aConsumer) {
            consumer = ns_NewStreamListenerProxy(aConsumer);
            if (nsnull == consumer) {
                goto loser;
            }
        }
#else
        consumer = aConsumer;
#endif

        /* Create the blocking stream... */
        pBlockingStream = new nsBlockingStream();

        if (NULL == pBlockingStream) {
            goto loser;
        }
        /*
         * AddRef the new stream in anticipation of returning it... This will
         * keep it alive :-)
         */
        NS_ADDREF(pBlockingStream);

        /* Create the nsConnectionInfo object... */
        pConn = new nsConnectionInfo(aUrl, pBlockingStream, consumer);
        if (NULL == pConn) {
            NS_RELEASE(pBlockingStream);
            goto loser;
        }
        NS_ADDREF(pConn);

        /* 
         * XXX: Rewrite the resource: URL into a file: URL
         */
        if (PL_strcmp(aUrl->GetProtocol(), "resource") == 0) {
            char* fileName;

            fileName = mangleResourceIntoFileURL(aUrl->GetFile());
            aUrl->Set(fileName);
            PR_Free(fileName);
        } 

        /* Create the URLStruct... */

        URL_s = NET_CreateURLStruct(aUrl->GetSpec(), NET_NORMAL_RELOAD);
        if (NULL == URL_s) {
            NS_RELEASE(pBlockingStream);
            NS_RELEASE(pConn);
            goto loser;
        }

        SetupURLStruct(aUrl, URL_s);

#if defined(XP_WIN) && !defined(NETLIB_THREAD)
        /*
         * When opening a blocking HTTP stream, perform a synchronous DNS 
         * lookup now, to avoid netlib from doing an async lookup later
         * and causing a deadlock!
         */
        result = PerformNastyWindowsAsyncDNSHack(URL_s, aUrl);
        if (NS_OK != result) {
            NET_FreeURLStruct(URL_s);
            NS_RELEASE(pBlockingStream);
            NS_RELEASE(pConn);
            goto loser;
        }
#endif /* XP_WIN && !NETLIB_THREAD */

        /*
         * Attach the ConnectionInfo object to the URL_Struct.
         *
         * Both the ConnectionInfo and the URL_Struct are freed in the 
         * bam_exit_routine(...)
         * The Reference count on pConn is already 1 so no AddRef() is 
         * necessary.
         */
        URL_s->fe_data = pConn;

        /*
         * Give the protocol a chance to initialize any URL_Struct fields...
         *
         * XXX:  Currently the return value form InitializeURLInfo(...) is 
         *       ignored...  Should the connection abort if it fails?
         */
         result = aUrl->QueryInterface(kIProtocolConnectionIID, (void**)&pProtocol);
         if (NS_OK == result) {
             pProtocol->InitializeURLInfo(URL_s);
             NS_RELEASE(pProtocol);
         }

/*        printf("+++ Loading %s\n", aUrl); */

      /* Create a new Context and set its reference count to one.. */
        MWContext *stubContext = new_stub_context(URL_s);
        net_AddrefContext(stubContext);

        /* Check for timebomb...*/
#ifdef TIMEBOMB_ON
        NET_CheckForTimeBomb(stubContext);
#endif /* TIMEBOMB_ON */

        /* Start the URL load... */
        NET_GetURL (URL_s,                      /* URL_Struct      */
                    FO_CACHE_AND_NGLAYOUT,      /* FO_Present_type */
                    (MWContext *)  stubContext, /* MWContext       */
                    bam_exit_routine);          /* Exit routine... */

        /* Remember, the URL_s may have been freed ! */

        /* 
         * Start the network timer to call NET_PollSockets(...) until the
         * URL has been completely loaded...
         */
        SchedulePollingTimer();

        *aNewStream = pBlockingStream;
        return NS_OK;
    }

loser:
    *aNewStream = NULL;
    return NS_FALSE;
}

NS_IMETHODIMP
nsNetlibService::InterruptStream(nsIURL* aURL)
{
  nsIProtocolConnection *pProtocol;
  nsresult rv = NS_OK;

  if (nsnull == aURL) {
    rv = NS_ERROR_NULL_POINTER;
    goto done;
  }

  rv = aURL->QueryInterface(kIProtocolConnectionIID, (void**)&pProtocol);
   if (NS_SUCCEEDED(rv)) {
     URL_Struct* URL_s;
     int status = -1;

     pProtocol->GetURLInfo(&URL_s);
     if (nsnull != URL_s) {
       status = NET_InterruptStream(URL_s);
     }
     NS_RELEASE(pProtocol);

     if (status != 0) {
       rv = NS_ERROR_FAILURE;
     }
   }

done:
  return rv;
}


NS_IMETHODIMP
nsNetlibService::GetCookieString(nsIURL *aURL, nsString& aCookie)
{
    // XXX How safe is it to create a stub context without a URL_Struct?
    MWContext *stubContext = new_stub_context(nsnull);
    
    const char *spec = aURL->GetSpec();
    char *cookie = NET_GetCookie(stubContext, (char *)spec);

    if (nsnull != cookie) {
        aCookie.SetString(cookie);
        PR_FREEIF(cookie);
    }
    else {
        aCookie.SetString("");
    }

    free_stub_context(stubContext);
    return NS_OK;
}

NS_IMETHODIMP
nsNetlibService::SetCookieString(nsIURL *aURL, const nsString& aCookie)
{
    // XXX How safe is it to create a stub context without a URL_Struct?
    MWContext *stubContext = new_stub_context(nsnull);
    
    const char *spec = aURL->GetSpec();
    char *cookie = aCookie.ToNewCString();

    NET_SetCookieString(stubContext, (char *)spec, cookie);

    delete []cookie;
    free_stub_context(stubContext);
    return NS_OK;
}

#ifdef SingleSignon
NS_IMETHODIMP
nsNetlibService::SI_RememberSignonData
        (char* URLName, LO_FormSubmitData *submit) {
    ::SI_RememberSignonData(URLName, submit);
    return NS_OK;
}

NS_IMETHODIMP
nsNetlibService::SI_RestoreSignonData
        (char* URLName, char* name, char** value) {
    ::SI_RestoreSignonData(URLName, name, value);
    return NS_OK;
}

#endif

NS_IMETHODIMP
nsNetlibService::GetProxyHTTP(nsString& aProxyHTTP) {
    char *proxy = nsnull;
    int32 port;
    char outBuf[MAXHOSTNAMELEN + 8];
    *outBuf = '\0';

    if ( PREF_OK != PREF_CopyCharPref(pref_proxyHttpServer,&proxy) ) {
        return NS_FALSE;
    }

    if ( PREF_OK != PREF_GetIntPref(pref_proxyHttpPort,&port) ) {
        PR_FREEIF(proxy);
        return NS_FALSE;
    }

    sprintf(outBuf,"%s:%d", proxy, port);
    PR_FREEIF(proxy);
    aProxyHTTP.SetString(outBuf);

    return NS_OK;
}

NS_IMETHODIMP
nsNetlibService::SetProxyHTTP(nsString& aProxyHTTP) {
    nsresult rv = NS_OK;
    PRInt32 port;
    nsString nsSPort;
    char *csPort = nsnull;
    char *proxy = nsnull;
    nsString nsSProxy;
    PRInt32 colonIdx;
    PRUnichar colon = ':';

    if (aProxyHTTP.Length() < 1) {
        NET_SelectProxyStyle(PROXY_STYLE_NONE);
        return NS_OK;
    }

    if ( (colonIdx = aProxyHTTP.Find(colon)) < 0 )
        return NS_FALSE;

    aProxyHTTP.Left(nsSProxy, colonIdx);
    aProxyHTTP.Mid(nsSPort, colonIdx+1, aProxyHTTP.Length() - colonIdx);

    proxy = nsSProxy.ToNewCString();
    if (!proxy)
        return NS_FALSE;
    csPort = nsSPort.ToNewCString();
    if (!csPort) {
        delete proxy;
        return NS_FALSE;
    }

    port = atoi(csPort);

    if ( PREF_OK != PREF_SetCharPref(pref_proxyHttpServer, proxy) ) {
        rv = NS_FALSE;
    }
    if ( PREF_OK != PREF_SetIntPref(pref_proxyHttpPort, port) ) {
        rv = NS_FALSE;
    }
    delete proxy;
    delete csPort;

    NET_SelectProxyStyle(PROXY_STYLE_MANUAL);

    return rv;
}

NS_IMETHODIMP
nsNetlibService::GetHTTPOneOne(PRBool& aOneOne) {
    aOneOne = (ONE_POINT_ONE == DEFAULT_VERSION);
    return NS_OK;
}

NS_IMETHODIMP
nsNetlibService::SetHTTPOneOne(PRBool aSendOneOne) {
    if (aSendOneOne)
        DEFAULT_VERSION = ONE_POINT_ONE;
    else
        DEFAULT_VERSION = ONE_POINT_O;
    return NS_OK;
}

NS_IMETHODIMP    
nsNetlibService::GetAppCodeName(nsString& aAppCodeName)
{
  aAppCodeName.SetString(XP_AppCodeName);
  return NS_OK;
}
 
NS_IMETHODIMP
nsNetlibService::GetAppVersion(nsString& aAppVersion)
{
  aAppVersion.SetString(XP_AppVersion);
  return NS_OK;
}
 
NS_IMETHODIMP
nsNetlibService::GetAppName(nsString& aAppName)
{
  aAppName.SetString(XP_AppName);
  return NS_OK;
}
 
NS_IMETHODIMP
nsNetlibService::GetLanguage(nsString& aLanguage)
{
  aLanguage.SetString(XP_AppLanguage);
  return NS_OK;
}
 
NS_IMETHODIMP    
nsNetlibService::GetPlatform(nsString& aPlatform)
{
  aPlatform.SetString(XP_AppPlatform);
  return NS_OK;
}

NS_IMETHODIMP
nsNetlibService::GetUserAgent(nsString& aUA)
{
    char buf[64];
    PR_snprintf(buf, 64, "%.100s/%.90s", XP_AppCodeName, XP_AppVersion);
    aUA.SetString(buf);
    return NS_OK;
}

NS_IMETHODIMP
nsNetlibService::SetCustomUserAgent(nsString aCustom)
{
    PRInt32 inIdx;
    PRUnichar inChar = ']';

    if (!XP_AppVersion || (0 >= aCustom.Length()) )
        return NS_FALSE;

    nsString newAppVersion = XP_AppVersion;

    inIdx = newAppVersion.Find(inChar);
    if (0 > inIdx) {
        newAppVersion.Insert(aCustom, inIdx + 1);
    }

    PR_Free((char *)XP_AppVersion);
    XP_AppVersion = newAppVersion.ToNewCString();
    return NS_OK;

}

void nsNetlibService::SchedulePollingTimer()
{
#if !defined(NETLIB_THREAD)
    // If a timer is already active, then do not create another...
    if (nsnull == mPollingTimer) {
        if (NS_OK == NS_NewTimer(&mPollingTimer)) {
            mPollingTimer->Init(nsNetlibService::NetPollSocketsCallback, this, 1000 / 50);
        }
    }
#endif /* ! NETLIB_THREAD */
}


void nsNetlibService::CleanupPollingTimer(nsITimer* aTimer) 
{
#if !defined(NETLIB_THREAD)
    NS_PRECONDITION((aTimer == mPollingTimer), "Unknown Timer...");

    NS_RELEASE(mPollingTimer);
#endif /* ! NETLIB_THREAD */
}


#if !defined(NETLIB_THREAD)
void nsNetlibService::NetPollSocketsCallback(nsITimer* aTimer, void* aClosure)
{
    nsNetlibService* inet = (nsNetlibService*)aClosure;

    NS_PRECONDITION((nsnull != inet), "Null pointer");
    if (nsnull != inet) {
        (void) NET_PollSockets();

        inet->CleanupPollingTimer(aTimer);
        // Keep scheduling callbacks as long as there are URLs to process...
///        if (0 < NET_TotalNumberOfProcessingURLs) {
            inet->SchedulePollingTimer();
///        }
    }
}

#if defined(XP_WIN)
/*
 * This routine is used to avoid a (hopefully temporary) problem which netlib
 * has when processing blocking HTTP streams.  
 * 
 * When ASYNC_DNS is enabled, all DNS lookups go through the main message pump,
 * unfortunately, this is not possible when processing a blocking stream, so 
 * we deadlock :-(
 *
 * To avoid this deadlock, we synchronously resolve the hostname and store it
 * in the little known IPAddressString field of the URL_Struct.  This prevents
 * netlib from doing an Async DNS lookup later...
 */
nsresult PerformNastyWindowsAsyncDNSHack(URL_Struct *URL_s, nsIURL* aURL)
{
    PRHostEnt hpbuf;
    char dbbuf[PR_NETDB_BUF_SIZE];
    PRStatus err;
    nsresult rv = NS_OK;

    /* Only attempt to resolve the hostname for HTTP URLs... */
    if (0 == PL_strcasecmp("http", aURL->GetProtocol())) {
        /* Perform a synchronous DNS lookup... */
        err = PR_GetHostByName(aURL->GetHost(), dbbuf, sizeof(dbbuf),  &hpbuf);
        if (PR_SUCCESS == err) {
            int a,b,c,d;
            unsigned char *pc;

            pc = (unsigned char *) hpbuf.h_addr_list[0];

            a = (int) *pc;
            b = (int) *(++pc);
            c = (int) *(++pc);
            d = (int) *(++pc);
            URL_s->IPAddressString = PR_smprintf("%d.%d.%d.%d", a,b,c,d);
        } else {
            /* 
             * If we fail to resolve a host on a HTTP connection, then 
             * abort the connection to prevent a deadlock...
             */
            rv = NS_ERROR_FAILURE;
        }
    }
    return rv;
}
#endif /* XP_WIN */
#endif /* !NETLIB_THREAD */


// This is global so it can be accessed by the NetFactory (nsNetFactory.cpp)
nsNetlibService *gNetlibService = nsnull;

//
// Class to manage static initialization of the Netlib DLL...
//
struct nsNetlibInit {
  nsNetlibInit() {
    gNetlibService = nsnull;
#if !defined(NETLIB_THREAD)
    /*
     * The netlib thread cannot be created within a static constructor
     * since a runtime library lock is being held by the mozilla thread
     * which results in a deadlock when the netlib thread is started...
     */
    (void) NS_InitINetService();
#endif /* !NETLIB_THREAD */
  }

  ~nsNetlibInit() {
#if !defined(NETLIB_THREAD)
    NS_ShutdownINetService();
#endif /* !NETLIB_THREAD */
    gNetlibService = nsnull;
  }
};

#ifdef XP_MAC
static nsNetlibInit* netlibInit = nsnull;
#else
static nsNetlibInit netlibInit;
#endif

extern "C" {
/*
 * Factory for creating instance of the NetlibService...
 */
NS_NET nsresult NS_NewINetService(nsINetService** aInstancePtrResult,
                                  nsISupports* aOuter)
{
    if (nsnull != aOuter) {
        return NS_ERROR_NO_AGGREGATION;
    }

#ifdef XP_MAC
    // Perform static initialization...
    if (nsnull == netlibInit) {
        netlibInit = new nsNetlibInit;
    }
#endif /* XP_MAC */ // XXX on the mac this never gets shutdown

    // The Netlib Service is created by the nsNetlibInit class...
    if (nsnull == gNetlibService) {
        (void) NS_InitINetService();
///     return NS_ERROR_OUT_OF_MEMORY;
    }

    return gNetlibService->QueryInterface(kINetServiceIID, (void**)aInstancePtrResult);
}

NS_NET nsresult NS_InitINetService()
{
    /* XXX: For now only allow a single instance of the Netlib Service */
    if (nsnull == gNetlibService) {
        gNetlibService = new nsNetlibService();
        if (nsnull == gNetlibService) {
            return NS_ERROR_OUT_OF_MEMORY;
        }
    }

    NS_ADDREF(gNetlibService);
    return NS_OK;
}


NS_NET nsresult NS_ShutdownINetService()
{
    return NS_OK;
}

} /* extern "C" */

/*
 * This is the generic exit routine for all URLs loaded via the new
 * BAM APIs...
 */
static void bam_exit_routine(URL_Struct *URL_s, int status, MWContext *window_id)
{
    TRACEMSG(("bam_exit_routine was called...\n"));

    if (NULL != URL_s) {
        nsConnectionInfo *pConn = (nsConnectionInfo *)URL_s->fe_data;

#ifdef NOISY
        printf("+++ Finished loading %s\n", URL_s->address);
#endif
        PR_ASSERT(pConn);

        /* Release the ConnectionInfo object held in the URL_Struct. */
        if (nsnull != pConn) {
            /* 
             * Normally, the stream is closed when the connection has been
             * completed.  However, if the URL exit proc was called directly
             * by NET_GetURL(...), then the stream may still be around...
             */
            if (nsnull != pConn->pNetStream) {
                pConn->pNetStream->Close();
                NS_RELEASE(pConn->pNetStream);
            }

            /* 
             * If the connection is still marked as active, then
             * notify the Data Consumer that the Binding has completed... 
             * Since the connection is still active, the stream was never
             * closed (or possibly created).  So, the binding has failed...
             */
            if ((nsConnectionActive == pConn->mStatus) && 
                (nsnull != pConn->pConsumer)) {
                nsAutoString status;
                pConn->pConsumer->OnStopBinding(pConn->pURL, NS_BINDING_FAILED, status);
                NS_RELEASE(pConn->pConsumer);
            }

            /* Release the nsConnectionInfo object hanging off of the fe_data */
            URL_s->fe_data = NULL;
            NS_RELEASE(pConn);
        }

        /* Delete the URL_Struct... */
        NET_FreeURLStruct(URL_s);
    }
}

/*
 * Ugly hack to free contexts
 */

extern "C" void net_AddrefContext(MWContext *context)
{
  if (nsnull != context) {
    context->ref_count++;
  }
}

extern "C" void net_ReleaseContext(MWContext *context)
{
 if (context) {
   NS_PRECONDITION((0 < context->ref_count), "Context reference count is bad.");
   if ((0 == --context->ref_count) && (context->modular_data)) {
     free_stub_context(context);
   } else {
     TRACEMSG(("net_ReleaseContext: not releasing non-modular context"));
   }
 }
}


/*
 * Rewrite "resource://" URLs into file: URLs with the path of the 
 * executable prepended to the file path...
 */
char *mangleResourceIntoFileURL(const char* aResourceFileName) 
{
  // XXX For now, resources are not in jar files 
  // Find base path name to the resource file
  char* resourceBase;

#ifdef XP_PC
  // XXX For now, all resources are relative to the .exe file
  resourceBase = (char *)PR_Malloc(_MAX_PATH);;
  DWORD mfnLen = GetModuleFileName(g_hInst, resourceBase, _MAX_PATH);
  // Truncate the executable name from the rest of the path...
  char* cp = strrchr(resourceBase, '\\');
  if (nsnull != cp) {
    *cp = '\0';
  }
  // Change the first ':' into a '|'
  cp = PL_strchr(resourceBase, ':');
  if (nsnull != cp) {
      *cp = '|';
  }
#endif /* XP_PC */

#ifdef XP_UNIX

//
// Obtain the resource: url base from the environment variable
//
// MOZILLA_HOME
//
// Which is the standard place where mozilla stores global (ie, not
// user specific) data
//

#ifndef MAXPATHLEN
#define MAXPATHLEN 1024 // A good guess, i suppose
#endif

#define MOZILLA_HOME "MOZILLA_HOME"

    static char * nsUnixMozillaHomePath = nsnull;

    if (nsnull == nsUnixMozillaHomePath)
    {
      nsUnixMozillaHomePath = PR_GetEnv(MOZILLA_HOME);
    }
    if (nsnull == nsUnixMozillaHomePath)
    {
      static char homepath[MAXPATHLEN];
      FILE* pp;
      if (!(pp = popen("pwd", "r"))) {
#ifdef DEBUG
        printf("RESOURCE protocol error in nsURL::mangeResourceIntoFileURL 1\n");
#endif
        return(nsnull);
      }
      if (fgets(homepath, MAXPATHLEN, pp)) {
        homepath[PL_strlen(homepath)-1] = 0;
      }
      else {
#ifdef DEBUG
        printf("RESOURCE protocol error in nsURL::mangeResourceIntoFileURL 2\n");
#endif
        pclose(pp);
        return(nsnull);
      }
      pclose(pp);
      nsUnixMozillaHomePath = homepath;
    }

	resourceBase = XP_STRDUP(nsUnixMozillaHomePath);
#ifdef DEBUG
    printf("Using '%s' as the resource: base\n", resourceBase);
#endif

#endif /* XP_UNIX */

#ifdef XP_MAC
	resourceBase = XP_STRDUP("usr/local/netscape/bin");
#endif /* XP_MAC */

  // Join base path to resource name
  if (aResourceFileName[0] == '/') {
    aResourceFileName++;
  }
  PRInt32 baseLen = PL_strlen(resourceBase);
  PRInt32 resLen = PL_strlen(aResourceFileName);
  PRInt32 totalLen = 8 + baseLen + 1 + resLen + 1;
  char* fileName = (char *)PR_Malloc(totalLen);
  PR_snprintf(fileName, totalLen, "file:///%s/%s", resourceBase, aResourceFileName);

#ifdef XP_PC
  // Change any backslashes into foreward slashes...
  while ((cp = PL_strchr(fileName, '\\')) != 0) {
    *cp = '/';
    cp++;
  }
#endif /* XP_PC */

  PR_Free(resourceBase);

  return fileName;
}




#ifdef XP_PC

BOOL WINAPI DllMain(HINSTANCE hDllInst,
                    DWORD fdwReason,
                    LPVOID lpvReserved)
{
    BOOL bResult = TRUE;

    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
          {
            // save our instance
            g_hInst = hDllInst;
          }
          break;

        case DLL_PROCESS_DETACH:
            break;

        case DLL_THREAD_ATTACH:
            break;

        case DLL_THREAD_DETACH:
            break;

        default:
            break;
  }

  return (bResult);
}



#endif /* XP_PC */
