Milán Major

netscape-revival

1 branch
Code

lib/libnet/tests/testnet.c

#include "xp.h"
#include "memory.h"
#include "net.h"
#include "structs.h"
#include "ctxtfunc.h"

#include "xp_core.h"
#include "xpgetstr.h"

#include "common.h"
#include "netwrap.h"

#ifdef XP_WIN

/*  DNS object cache management
 */

typedef struct DNS_OBJECT__
{
    struct DNS_OBJECT__ *m_next;
    HANDLE m_handle;
    struct hostent *m_hostent;
    BOOL m_finished;
    int m_error;
    char *m_host;
}
    DNS_OBJECT;

DNS_OBJECT *firstDnsObject = 0;

DNS_OBJECT *GetDnsObject(char *host)
{
    DNS_OBJECT *dnsObject;

    dnsObject = firstDnsObject;

    while (dnsObject)
    {
        if (0 == stricmp(dnsObject->m_host, host))
            return dnsObject;

        dnsObject = dnsObject->m_next;
    }

    return 0;
}

DNS_OBJECT *GetDnsObjectUsingHandle(HANDLE handle)
{
    DNS_OBJECT *dnsObject;

    dnsObject = firstDnsObject;

    while (dnsObject)
    {
        /*  Handles may be reused, so check for objects that are not finished
         */
        if ((!dnsObject->m_finished) && (handle == dnsObject->m_handle))
            return dnsObject;

        dnsObject = dnsObject->m_next;
    }

    return 0;
}

DNS_OBJECT *AddDnsObject(char *host)
{
    DNS_OBJECT *dnsObject;

    dnsObject = GetDnsObject(host);
    if (dnsObject) return dnsObject;

    dnsObject = (DNS_OBJECT *)XP_ALLOC(sizeof(DNS_OBJECT));

    dnsObject->m_next = firstDnsObject;
    firstDnsObject = dnsObject;

    dnsObject->m_hostent = (struct hostent *)XP_CALLOC(1, MAXGETHOSTSTRUCT * sizeof(char));
    dnsObject->m_host = XP_STRDUP(host);
    dnsObject->m_error = 0;
    dnsObject->m_finished = FALSE;

    return dnsObject;
}

void FreeDnsObject(char *host)
{
    DNS_OBJECT *dnsObject, *prevDnsObject;

    dnsObject = firstDnsObject;
    prevDnsObject = 0;

    while (dnsObject)
    {
        if (0 == stricmp(dnsObject->m_host, host))
        {
            if (prevDnsObject) prevDnsObject->m_next = dnsObject->m_next;
            else firstDnsObject = dnsObject->m_next;

            if (dnsObject->m_hostent) XP_FREE(dnsObject->m_hostent);
            if (dnsObject->m_host) XP_FREE(dnsObject->m_host);
            XP_FREE(dnsObject);

            return;
        }

        prevDnsObject = dnsObject;
        dnsObject = dnsObject->m_next;
    }
}

void FreeAllDnsObjects(void)
{
    while (firstDnsObject)
    {
        DNS_OBJECT *nextDnsObject;

        nextDnsObject = firstDnsObject->m_next;

        if (firstDnsObject->m_hostent) XP_FREE(firstDnsObject->m_hostent);
        if (firstDnsObject->m_host) XP_FREE(firstDnsObject->m_host);
        XP_FREE(firstDnsObject);

        firstDnsObject = nextDnsObject;
    }

    firstDnsObject = 0;
}

/*
 *  DNS notification window (Windows only)
 */

#define DNSWINDOWMESSAGE    (WM_USER + 1000)
#define DNSWINDOWMESSAGE1   (WM_USER + 1001)

HWND DNSWindow = 0;

LRESULT CALLBACK NWDNSWindowProc(
    HWND        hWnd,
    UINT        message,
    WPARAM      wParam,
    LPARAM      lParam)
{
    switch (message)
    {
        case DNSWINDOWMESSAGE:
        {
            DNS_OBJECT *dnsObject;

            dnsObject = GetDnsObjectUsingHandle((HANDLE)wParam);

            if (dnsObject)
            {
                dnsObject->m_finished = TRUE;
                dnsObject->m_error = WSAGETASYNCERROR(lParam);
            }

            break;
        }

        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
}

BOOL CreateDNSWindow(void)
{
    WNDCLASS windowClass;

    if (DNSWindow) return TRUE;

    windowClass.style = 0;
    windowClass.lpfnWndProc = NWDNSWindowProc;
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hInstance = GetModuleHandle(NULL);
    windowClass.hIcon = NULL;
    windowClass.hCursor = NULL;
    windowClass.hbrBackground = NULL;
    windowClass.lpszMenuName = NULL;
    windowClass.lpszClassName = "__nw_dns_window_class__";

    if (0 == RegisterClass(&windowClass))
        return NULL;

    DNSWindow = CreateWindow("__nw_dns_window_class__", "_nw_dns_window_",
        WS_DISABLED | WS_OVERLAPPED, -1000, -1000, 10, 10,
        NULL, NULL, GetModuleHandle(NULL), NULL);

    return (NULL != DNSWindow);
}

void DestroyDNSWindow(void)
{
    DestroyWindow(DNSWindow);
    DNSWindow = 0;
}


int FE_SetupAsyncSelect(unsigned int sock, MWContext * context)
{
    /* WSAAsyncSelect(sock, DNSWindow, DNSWINDOWMESSAGE1, 0); */
    return 0;
}

int FE_StartAsyncDNSLookup(
        MWContext *context,
        char * host_port,
        void **hoststruct_ptr_ptr,
        int sock)
{

    DNS_OBJECT *dnsObject;
    int result;

    *hoststruct_ptr_ptr = NULL;
    result = MK_WAITING_FOR_LOOKUP;
    
    dnsObject = GetDnsObject(host_port);

    if (!dnsObject)
    {
        /* start the lookup
         */
        dnsObject = AddDnsObject(host_port);
        dnsObject->m_handle = WSAAsyncGetHostByName(DNSWindow, DNSWINDOWMESSAGE,
                host_port, (char *)dnsObject->m_hostent, MAXGETHOSTSTRUCT);
    }
    else if (dnsObject->m_finished)
    {
        if (0 == dnsObject->m_error)
        {
            /*  copy data to hoststruct_ptr_ptr
             */
            *hoststruct_ptr_ptr = dnsObject->m_hostent;
            result = 0;
        }
        else
        {
            /*  DNS lookup failed -- delete object from list
             */
            FreeDnsObject(host_port);
            result = -1;
        }
    }

    return result;

}
#endif /* XP_WIN */

extern int MK_OUT_OF_MEMORY;
extern int XP_TRANSFER_INTERRUPTED;

NW_NetReturnData *NW_CreateReturnDataStruct(void)
{
	return(XP_CALLOC(1, sizeof(NW_NetReturnData)));
}

void NW_FreeNetReturnData(NW_NetReturnData * return_data)
{
	XP_ASSERT(return_data);
	if(!return_data)
		return;

	if(return_data->data)
	  {
		XP_FREE(return_data->data);
		return_data->data = NULL;
	  }

    if (return_data->error_msg)
	  {
        XP_FREE(return_data->error_msg);
		return_data->error_msg = NULL;
	  }

	XP_FREE(return_data);
}

void TestExit(URL_Struct *URL_s, int status, MWContext *window_id)
{
	NW_NetReturnData *return_data = (NW_NetReturnData *)URL_s->fe_data;

	XP_ASSERT(return_data);

    if(return_data)
    {
        return_data->exit_status = status;
        if (URL_s->error_msg)
            return_data->error_msg = XP_STRDUP(URL_s->error_msg);
    }

    /*
	if(URL_s->error_msg)
 	  {  
		FE_Alert(window_id, URL_s->error_msg);
		printf(URL_s->error_msg);
	  }
    */
}

int FE_GetURL (MWContext *context, URL_Struct *url)
{
    /* Pass in context, not &context, right?  CCM */
    return NET_GetURL(url, FO_PRESENT, context, TestExit);
}

#ifdef XP_WIN
char * NOT_NULL (const char *x)
{
    return (char*)x;
}
#endif

typedef struct {
	XP_File   fp;
	char	* filename;
} FileWriteConverterDataObject;

PRIVATE int lw_SaveToDiskWrite (FileWriteConverterDataObject * obj,char* s, int32 l)
{
    XP_FileWrite(s, l, obj->fp); 
    return(1);
}

/* is the stream ready for writeing?
 */
PRIVATE unsigned int lw_SaveToDiskWriteReady (FileWriteConverterDataObject * obj)
{
   return(MAX_WRITE_READY);  /* always ready for writing */ 
}

PRIVATE void lw_SaveToDiskComplete (FileWriteConverterDataObject * obj)
{
    XP_FileClose(obj->fp);

    if(obj->filename)
    	XP_FREE(obj->filename);

    XP_FREE(obj);
    return;
}

PRIVATE void lw_SaveToDiskAbort (FileWriteConverterDataObject * obj, int status)
{
    XP_FileClose(obj->fp);

    if(obj->filename)
      {
        remove(obj->filename);
        XP_FREE(obj->filename);
      }

    return;
}

NET_StreamClass * LW_FileWriteConverter2 (int format_out,
    									void *registration,  
    									URL_Struct * request,
    									MWContext *context) 
{
    NET_StreamClass * new_stream = XP_CALLOC(1, sizeof(NET_StreamClass));
	FileWriteConverterDataObject * data_obj;
	char *filename;

	if(!new_stream)
		return NULL;

	data_obj = XP_CALLOC(1, sizeof(FileWriteConverterDataObject));

	if(!data_obj)
		return NULL;

    new_stream->name           = "FileWriter";
    new_stream->complete       = (MKStreamCompleteFunc) lw_SaveToDiskComplete;
    new_stream->abort          = (MKStreamAbortFunc) lw_SaveToDiskAbort;
    new_stream->put_block      = (MKStreamWriteFunc) lw_SaveToDiskWrite;
    new_stream->is_write_ready = (MKStreamWriteReadyFunc) lw_SaveToDiskWriteReady;
    new_stream->data_object    = data_obj;  /* document info object */
    new_stream->window_id      = context;

	filename = "foo.bar";  /* @#@@@@@ */

    StrAllocCopy(data_obj->filename, filename);

	if(!(data_obj->fp = XP_FileOpen(data_obj->filename, xpTemporary, XP_FILE_WRITE_BIN)))
		return NULL;

    TRACEMSG(("Returning stream from LW_SaveToDiskConverter\n"));
	
    return new_stream;
}

typedef struct {
	char    * data;
	uint32    data_size;
	uint32    malloc_size;
	NW_NetReturnData *return_data;
} MemWriteConverterDataObject;

#define EXPAND_INTERVAL_SIZE 1024

PRIVATE int lw_SaveToMemWrite (MemWriteConverterDataObject * obj,char* s, int32 l)
{ 
	if(obj->malloc_size < obj->data_size+l)
	{
		int32 expand_by;

		/* expand malloc_size */
		if(l > EXPAND_INTERVAL_SIZE)
			expand_by = l;
		else
			expand_by = EXPAND_INTERVAL_SIZE;
		
		if(obj->data_size > 0)
			obj->data = XP_REALLOC(obj->data, (obj->malloc_size+expand_by)*sizeof(char));
		else
			obj->data = XP_ALLOC(expand_by*sizeof(char));
		
		obj->malloc_size += expand_by;
		
		if(!obj->data)
			return(MK_OUT_OF_MEMORY);
	}

	XP_MEMCPY(obj->data+obj->data_size, s, l);
	obj->data_size += l;

    return(1);
}

/* is the stream ready for writeing?
 */
PRIVATE unsigned int lw_SaveToMemWriteReady (MemWriteConverterDataObject * obj)
{
   return(MAX_WRITE_READY);  /* always ready for writing */ 
}

PRIVATE void lw_SaveToMemComplete (MemWriteConverterDataObject * obj)
{
    
	obj->return_data->data = obj->data;
	obj->return_data->size = obj->data_size;

    XP_FREE(obj);
    return;
}

PRIVATE void lw_SaveToMemAbort (MemWriteConverterDataObject * obj, int status)
{

	if(obj->data)
		XP_FREE(obj->data);
	XP_FREE(obj);
    return;
}

NET_StreamClass * LW_MemWriteConverter (int format_out,
    									void *registration,  
    									URL_Struct * URL_s,
    									MWContext *context) 
{
    NET_StreamClass * new_stream = XP_CALLOC(1, sizeof(NET_StreamClass));
	MemWriteConverterDataObject * data_obj;

	if(!new_stream)
		return NULL;

	data_obj = XP_CALLOC(1, sizeof(MemWriteConverterDataObject));

	if(!data_obj)
		return NULL;

    new_stream->name           = "Memory Writer";
    new_stream->complete       = (MKStreamCompleteFunc) lw_SaveToMemComplete;
    new_stream->abort          = (MKStreamAbortFunc) lw_SaveToMemAbort;
    new_stream->put_block      = (MKStreamWriteFunc) lw_SaveToMemWrite;
    new_stream->is_write_ready = (MKStreamWriteReadyFunc) lw_SaveToMemWriteReady;
    new_stream->data_object    = data_obj;  /* document info object */
    new_stream->window_id      = context;

    TRACEMSG(("Returning stream from LW_SaveToMemConverter\n"));

	data_obj->return_data = URL_s->fe_data;

    return new_stream;
}


#define MAKE_FE_FUNCS_PREFIX(f)         LWFE_##f
#define MAKE_FE_FUNCS_EXTERN
#include "mk_cx_fn.h"


#if defined(XP_WIN) || defined(XP_MAC) 
extern char * XP_AppCodeName;    /* Changed to match ns/include/gui.h  CCM */
extern char * XP_AppVersion;
#else
extern const char *XP_AppName, *XP_AppCodeName, *XP_AppVersion;
#endif

void CheckForHtmlError(NW_NetReturnData *return_data, URL_Struct *url_s)
{
    /*  A bunch of checks to guess whether there was an error, even though none
     *  is indicated.
     */

    /*  First check whether there is already an error message. If so we can
     *  just return. Also, if there is no data, we can go on because there is
     *  no text to use as error message.
     */
    if (return_data->error_msg)
        return;

    /*  Now check whether the error status is positive. If so, there may still
     *  be an error. This happens if an HTTP server returns something outside
     *  the 200-299 range.
     */
    if (return_data->exit_status >= 0)
    {
        if (NET_URL_Type(url_s->address) != HTTP_TYPE_URL)
            return;

        if ((url_s->server_status >= 200) && (url_s->server_status <= 299))
            return;

        /*  An HTTP server error has occured. If the data field is not empty,
         *  then we have an html error.
         */
        return_data->exit_status = -1;
        if (return_data->size != 0)
            goto ThereIsAnHtmlError;

        /*  We have to make up an HTTP error message.
         */
        if (url_s->server_status == 0)
            return_data->error_msg =
                    XP_STRDUP("The server did not respond.");
        else
            return_data->error_msg =
                    XP_STRDUP("The server could not complete the operation.");
        return;
    }

    /*  There is an error, but no error message. Check to see if there is
     *  data. If not, we have no text, so we have to make up a message.
     *
     *  This condition can happen when contacting an FTP server. I tried to
     *  HEAD ftp://mocha/pub/site0/ and ended up here.
     */
    if (return_data->size == 0)
    {
        return_data->error_msg =
                XP_STRDUP("Network or file operation failed.");
        return;
    }

ThereIsAnHtmlError:

    /*  Since there is no error message, and there is result data, then
     *  this data is the error message. It needs to be deHTMLized.
     */
    {
        char *p1, *p2, *pEnd;
        int isTag = FALSE;

        return_data->error_msg = XP_ALLOC(return_data->size + 1);

        p1 = return_data->data;
        p2 = return_data->error_msg;

        pEnd = p1 + return_data->size;

        while ((p1 < pEnd) && *p1)
        {
            if (isTag)
            {
                if (*p1 == '>') isTag = FALSE;
            }
            else if (*p1 == '<')
            {
                isTag = TRUE;
            }
            else
            {
                *p2 = *p1;
                p2++;
            }
            p1++;
        }

        *p2 = '\0';
    }
}

int NW_DoNetAction(URL_Struct *url_s, NW_NetReturnData *return_data,
        NW_FrontEndData *fe)
{
    static MWContext context = {NULL};
	int status, type;

    NW_ReportData rd;

    type = NET_URL_Type(url_s->address);
    
    if(type == SECURE_HTTP_TYPE_URL)
    {
        return_data->exit_status = -1;
        return_data->error_msg = XP_STRDUP(
                "Cannot peform this operation with a secure server");
        return -1;
    }

    if(type == HTTP_TYPE_URL && url_s->method == URL_PUT_METHOD)
    {
        /* HTTP servers need a content length in the request
         * add one here.
         */
        XP_ASSERT(url_s->post_headers == NULL);
        url_s->post_headers = PR_smprintf("Content-Length: %ld"CRLF, 
                                          url_s->post_data_size);        
    }
    

	if(!context.funcs)
	  {
		static ContextFuncs  functions;
#ifdef XP_WIN
		WORD version_requested;
		WSADATA winsock_data;

		version_requested = MAKEWORD(1,1);

		status = WSAStartup(version_requested, &winsock_data);

		if(status != 0)
		  {
			printf("could not initialize winsock");
			exit(1);
		  }
#endif
		XP_AppCodeName = XP_STRDUP("Livewire");
		XP_AppVersion = XP_STRDUP(".9");

		NET_InitNetLib(1024*10, 4);

		NET_RegisterContentTypeConverter("*", FO_PRESENT, 0, LW_MemWriteConverter);


		memset( &context, 0, sizeof (context) );
		memset( &functions, 0, sizeof (functions) );

		context.type = MWContextBrowser;

		#define MAKE_FE_FUNCS_PREFIX(f)     LWFE_##f
		#define MAKE_FE_FUNCS_ASSIGN        functions.
		#include "mk_cx_fn.h"

		context.funcs = &functions;

#ifdef XP_WIN
        CreateDNSWindow();
#endif /* XP_WIN */
	  }

    context.fe.generic_data = fe;
    rd.kind = NW_STATUS_CHECK;
    rd.message = 0;

	url_s->fe_data = return_data;

    NET_GetURL(url_s, FO_PRESENT, &context, TestExit);

	while (0 < NET_ProcessNet((NETSOCK)-1, NET_UNKNOWN_FD))
    {
		/* spin loop to process data */
        if (!fe->report(fe->user, &rd))
        {
            NET_InterruptWindow(&context);
            return_data->exit_status = XP_TRANSFER_INTERRUPTED;
            return_data->error_msg = XP_STRDUP(XP_GetString(XP_TRANSFER_INTERRUPTED));
            break;
        }
    }

    CheckForHtmlError(return_data, url_s);

	return(return_data->exit_status);
}

void NW_ShutDown(void)
{
#ifdef XP_WIN
    WSACleanup();
    FreeAllDnsObjects();
#endif /* XP_WIN */

    NET_ShutdownNetLib();
}

#ifndef NW_WRAPPER

int ReportOrCheck(NW_FrontEndData *fe, NW_ReportData *rd)
{
    return 1;
}    

static int
parse_method(char *method_name)
{
	if(!strcasecomp(method_name, "HEAD"))
		return(URL_HEAD_METHOD);
	else if(!strcasecomp(method_name, "PUT"))
		return(URL_PUT_METHOD);
	else if(!strcasecomp(method_name, "DELETE"))
		return(URL_DELETE_METHOD);
	else if(!strcasecomp(method_name, "MOVE"))
		return(URL_MOVE_METHOD);
	else if(!strcasecomp(method_name, "GET"))
		return(URL_GET_METHOD);
	else 
		return(URL_GET_METHOD); /* default */
}

void main(int argc, char ** argv)
{
    URL_Struct *url_s = NULL;
	int status;
    NW_FrontEndData fe;
	NW_NetReturnData * return_data;
	int url_method = URL_GET_METHOD; /* default method */

	PR_Init("mozilla", 24, 1, 0);

	return_data = NW_CreateReturnDataStruct();
	
    fe.report = ReportOrCheck;
    fe.user = 0;

	while(--argc)
	  {
		if(!strcasecomp(argv[argc], "-debug"))
		  {
			MKLib_trace_flag = 2;
		  }
		else if(!strcasecomp(argv[argc], "-method"))
		  {
			url_method = parse_method(argv[argc+1]);
		  }
		else if(NET_URL_Type(argv[argc]))
		  {
			url_s = NET_CreateURLStruct(argv[1], NET_NORMAL_RELOAD);
		  }
	  }
		
	if(!url_s)
		url_s = NET_CreateURLStruct("http://warp/", NET_NORMAL_RELOAD);

	if(!url_s)
		exit(1);

	url_s->method = url_method;

	/*
		*methods*
		url_s->method = URL_GET_METHOD;
		url_s->method = URL_HEAD_METHOD;
		url_s->method = URL_PUT_METHOD;
		url_s->method = URL_DELETE_METHOD;
   		url_s->method = URL_MOVE_METHOD;
	*/

	status = NW_DoNetAction(url_s, return_data, &fe);


	if(url_s->content_type)
		printf("Content type: %s\n", url_s->content_type);

	if(url_s->content_length)
		printf("Content length: %d\n\n", url_s->content_length);

	if(return_data->data)
	  {
		printf("data:\n");
		printf(return_data->data);
	  }

	if(return_data->exit_status < 0 && return_data->error_msg)
		printf("error: %s\n", return_data->error_msg);

	NET_FreeURLStruct(url_s);

	NW_FreeNetReturnData(return_data);

}

#endif  /* LW_WRAPPER */