/*****
* example_4.c : XmHTML XmNformCallback & HTTP.c demonstration
*
* This file Version	$Revision: 1.1 $
*
* Creation date:		Tue Oct 21 01:33:18 GMT+0100 1997
* Last modification: 	$Date: 1997/10/23 00:28:33 $
* By:					$Author: newt $
* Current State:		$State: Exp $
*
* Author:				newt
*
* Copyright (C) 1994-1997 By Richard Offer (offer@sgi.com)
*
* This file is part of the XmHTML Widget Library
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU [Library] General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU [Library] General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Note from the author:
*
*	Most of the code is straight-forward, however since the only real way to 
*	test the forms support is from a live web server you need some HTTP support.
*	I've written some code (you'll find it in HTTP.c) that is fairly flakey, 
*	but at least it should be easy to rip it out and add something better 
*	(there's only a single API call loadHTTPUrl() and a couple of 
*	allocation/cleaning up routines. At the least you can use the HTTP code 
*	as the basis for your own...
* 
*	I'm not pretending that this code is anywhere near production quality, and 
*	I don't intend to use it for my broswer app, so giving anyone else anytype 
*	of warrenty would be pretty stupid of me.
* 
*	I will be tidying this up as I continue to develop my broswer...
* 
*	Its also only been tested on Irix 6.{4,5}.
* 
*	Before you try and use this with the accompanying html file 
*	(test-pages/form-test.html) you need to install the file form-test.pl in 
*	the cgi-bin directory of your web server web server---you may need to be 
*	root to do this.
* 
*	Don't forget to change the first line in the form-test.pl script to point 
*	to the location of your perl binary
*
*****/
/*****
* ChangeLog 
* $Log: example_4.c,v $
* Revision 1.1  1997/10/23 00:28:33  newt
* Initial Revision
*
*****/ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <Xm/XmAll.h>

#include <XmHTML/XmHTML.h>

/* The Header file for my HTTP implementation */
#include <http/HTTP.h>

/*** External Function Prototype Declarations ***/

/*** Public Variable Declarations ***/

/*** Private Datatype Declarations ****/

/*** Private Function Prototype Declarations ****/
static XmImageInfo *loadImage(Widget w, String url);
static void loadFile(Widget w, XtPointer client_data, XtPointer call_data);
static void loadURLToWidget(Widget html, String url, HTTPNamedValues * formdata,
	HTTPLoadMethod method);
static void cleanCache(void);

/** various callbacks **/
static void formCB(Widget w, XtPointer loadWidget,
	XmHTMLFormCallbackStruct * form_data);
static void anchorCB(Widget w, XtPointer loadWidget,
	XmHTMLAnchorPtr href_data);
static void destroyCB(Widget w, XtPointer client, XtPointer call);

static void quitCB(Widget w, XtPointer client, XtPointer call);
static void loadCB(Widget w, XtPointer client, XtPointer call);

/*** Private Variable Declarations ***/
static String appFallbackResources[] =
{
"*fontList:               *-adobe-helvetica-bold-r-*-*-*-120-*-*-p-*-*-*",
	"*sourceView.width:       550",
	"*sourceView.height:      500",
	"*XmHTML.width:           575",
	"*XmHTML.height:          600",
	NULL};

Widget TopLevel;
Widget xmHTML;
XtAppContext AppContext;

/*****
* Change this to change the application class of the examples
*****/
#define APP_CLASS               "HTMLDemos"

/*****
* Name:
* Return Type:
* Description:
* In:
*
* Returns:
*
*****/
static void 
loadFile(Widget w, XtPointer client_data, XtPointer call_data)
{
	FILE *file;
	char *filename;
	int size;
	static String content;
	XmFileSelectionBoxCallbackStruct *cbs;

	XtUnmanageChild((Widget)client_data);

	cbs = (XmFileSelectionBoxCallbackStruct*)call_data;

	XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);

	if (filename == NULL)
		return;

	/* open the given file */
	if ((file = fopen(filename, "r")) == NULL) {
		perror(filename);
		return;
	}
	/* see how large this file is */
	fseek(file, 0, SEEK_END);
	size = ftell(file);
	rewind(file);

	/* allocate a buffer large enough to contain the entire file */
	if ((content = malloc(size + 1)) == NULL) {
		fprintf(stderr, "malloc failed for %i bytes\n", size);
		exit(1);
	}
	/* now read the contents of this file */
	if ((fread(content, 1, size, file)) != size)
		printf("Warning: did not read entire file!\n");

	content[size] = '\0';
	fclose(file);

	XmHTMLTextSetString(xmHTML, content);
}

static void
loadURLToWidget(Widget html, String url, HTTPNamedValues * formdata,
	HTTPLoadMethod method)
{
	HTTPRequest *req = newHTTPRequest();
	char *userData = NULL;

	XtVaGetValues(html, XmNuserData, &userData, NULL);

	if(!HTTPAbsoluteURL(url))
		req->url = HTTPFindAbsoluteURL(url, userData);
	else
		req->url = NewString(url);

	req->type = HTTPLoadToString;
	req->method = method;

	if(url)
	{
		req->form_data = formdata;

		loadHTTPURL(NULL, req, NULL);
		if(req->ret == HTTPSuccess)
		{
			if(req->url)
			{
				char *new_url = NewString(req->url);

				XtVaSetValues(html, XmNuserData, new_url, NULL);
				if(userData)	/* can be NULL! */
					free(userData);

				/*
				* Since this is form data, we want to unescape any
				* escapes contained in the response string.
				*/
				HTTPUnescapeResponse(req->out_data);

				/* set it */
				XmHTMLTextSetString(html, (String) req->out_data);
			}
		}
		else
			HTTPError("Request failed", req->ret);
	}
	/* Clean up the request */
	deleteHTTPRequest(req);
}

static void
formCB(Widget w, XtPointer loadWidget, XmHTMLFormCallbackStruct * form_data)
{

	int i;
	HTTPLoadMethod method = form_data->method == 0 ? HTTPGET : HTTPPOST;
	HTTPNamedValues *form = NULL;

	/* we don't support the pipe method */
	if(form_data->method == XmHTML_FORM_PIPE)
		return;

	/*
	 * the form data is freed inside the deleteHTTPRequest() function 
	 * (called from loadURLToWidget())
	 */
	form = (HTTPNamedValues *) calloc(form_data->ncomponents + 1,
									  sizeof(HTTPNamedValues));

	/* we don't pass a length so loop until we see a NULL name */
	for(i = 0; i < form_data->ncomponents; i++)
	{
		form[i].name = NewString(form_data->components[i].name);
		form[i].value = NewString(form_data->components[i].value);
	}
	form[i].name = NULL;
	form[i].value = NULL;

	loadURLToWidget(loadWidget, form_data->action, form, method);
}

static void
anchorCB(Widget w, XtPointer loadWidget, XmHTMLAnchorPtr href_data)
{
	/* see if we have been called with a valid reason */
	if (href_data->reason != XmCR_ACTIVATE)
		return;

	switch (href_data->url_type)
	{
		case ANCHOR_FILE_LOCAL:
		case ANCHOR_HTTP:
			loadURLToWidget(loadWidget, href_data->href, NULL, HTTPGET);
			break;
		case ANCHOR_JUMP:
			{
				int id;
				if((id = XmHTMLAnchorGetId(loadWidget, href_data->href)) != -1)
				{
					/* let XmHTML jump and mark as visited */
					href_data->doit = True;
					href_data->visited = True;
					return;
				};
			}
			break;
		case ANCHOR_UNKNOWN:
		default:
			fprintf(stderr, "don't handle this type of url: %d %s\n",
				href_data->url_type, href_data->href);
			break;
	}
}

static void
destroyCB(Widget w, XtPointer client, XtPointer call)
{
	void *userData = NULL;

	XtVaGetValues(w, XmNuserData, &userData, NULL);

	if(userData)
		free((char *)userData);
}

void 
cleanCache(void)
{
	DIR *dir;
	struct dirent *fileent;

	/*****
	* Clean out the image cache dir, mainly I do this to force a re-load of the 
	* images when I next start the app 
	*****/
	dir = opendir(".cache");

	if (dir == NULL)
	{
		if(mkdir(".cache", S_IRWXU) != 0)
		{
			printf("cannot create image cache dir\n");
			exit(1);
		} else
			dir = opendir(".cache");
	}
	while((fileent = readdir(dir)) != NULL)
	{
		/* ignore dot files (there shouldn't be any) */
		if (strncmp(fileent->d_name, ".", 1))
		{
			char filename[256];
			sprintf(filename, "%s/%s", ".cache", fileent->d_name);
			printf("removing %s\n", filename);
			unlink(filename);
		}
	}
	if(dir)
		closedir(dir);
}

static void
destroyXmHTMLCB(Widget w, XtPointer client, XtPointer call)
{
	XtDestroyWidget(xmHTML);
}

static void
quitCB(Widget w, XtPointer client, XtPointer call)
{
	cleanCache();

#if (XtSpecificationRelease <= 5)
	XtDestroyApplicationContext(AppContext);
	exit(EXIT_SUCCESS);
#else
	/***** 
	* The 'approved' method for killing X11R6 apps, previous versions just use 
	* exit(0). 
	*****/
	XtAppSetExitFlag(AppContext);
#endif

}

static void
loadCB(Widget w, XtPointer client, XtPointer call)
{

	static Widget fsb = NULL;
	Arg args[2];
	XmString xmstr;

	if(fsb == NULL)
	{
		xmstr = XmStringCreateLocalized("*.html");
		XtSetArg(args[0], XmNpattern, xmstr);

		fsb = XmCreateFileSelectionDialog(TopLevel, "fileOpen", args, 1);

		XmStringFree(xmstr);

		XtAddCallback(fsb, XmNokCallback,
					  (XtCallbackProc) loadFile, fsb);
		XtAddCallback(fsb, XmNcancelCallback,
					  (XtCallbackProc) XtUnmanageChild, NULL);
	}
	XtManageChild(fsb);
}

int
main(int argc, char **argv)
{

	Widget form, menubar, pane, quit, home, dest;

	XtSetLanguageProc(NULL, NULL, NULL);

	/* clean the cache to force all images to be re-loaded */
	cleanCache();

	/* sessionShellWidgetClass only from R6 on */
#if (XtSpecificationRelease <= 5)
	TopLevel = XtVaAppInitialize(&AppContext, APP_CLASS, NULL, 0,
				&argc, argv, appFallbackResources, NULL, NULL);
#else
	TopLevel = XtVaOpenApplication(&AppContext, APP_CLASS, NULL, 0,
				&argc, argv, appFallbackResources, sessionShellWidgetClass,
				NULL);
#endif

	form = XtVaCreateWidget("topForm", xmFormWidgetClass, TopLevel,
				NULL);

	menubar = XmCreateMenuBar(form, "menuBar", NULL, 0);
	pane = XmCreatePulldownMenu(menubar, "FilePane", NULL, 0);

	XtVaCreateManagedWidget("File", xmCascadeButtonGadgetClass, menubar,
			XmNsubMenuId, pane,
			NULL);

	home = XtVaCreateManagedWidget("load", xmPushButtonGadgetClass, pane, NULL);
	dest = XtVaCreateManagedWidget("destroy", xmPushButtonGadgetClass, pane,
			NULL);
	quit = XtVaCreateManagedWidget("Quit", xmPushButtonGadgetClass, pane, NULL);

	XtAddCallback(quit, XmNactivateCallback, (XtCallbackProc)quitCB,
		(XtPointer)NULL);
	XtAddCallback(dest, XmNactivateCallback, (XtCallbackProc)destroyXmHTMLCB,
		(XtPointer)NULL);


	XtVaSetValues(menubar,
		XmNtopAttachment, XmATTACH_FORM,
		XmNleftAttachment, XmATTACH_FORM,
		XmNrightAttachment, XmATTACH_FORM,
		NULL);


	xmHTML = XtVaCreateManagedWidget("HTML", xmHTMLWidgetClass, form,
				XmNtopAttachment, XmATTACH_WIDGET,
				XmNtopWidget, menubar,
				XmNleftAttachment, XmATTACH_FORM,
				XmNrightAttachment, XmATTACH_FORM,
				XmNbottomAttachment, XmATTACH_FORM,
#ifdef HAVE_GIF_CODEC
				XmNdecodeGIFProc, decodeGIFImage,
#endif
				XmNimageProc, loadImage,
				XmNenableBadHTMLWarnings, False,
				XmNuserData, NULL,
				NULL);


	XtAddCallback(home, XmNactivateCallback, (XtCallbackProc)loadCB,
		(XtPointer)NULL);
	XtAddCallback(xmHTML, XmNdestroyCallback, (XtCallbackProc)destroyCB,
		(XtPointer)xmHTML);
	XtAddCallback(xmHTML, XmNactivateCallback, (XtCallbackProc)anchorCB,
		(XtPointer)xmHTML);
	XtAddCallback(xmHTML, XmNformCallback, (XtCallbackProc)formCB,
		(XtPointer)xmHTML);

	XtManageChild(menubar);
	XtManageChild(form);

	/* as I check (ha) all read and write return values, ignore SIGPIPE */
	signal(SIGPIPE, SIG_IGN);

	XtRealizeWidget(TopLevel);
	XtAppMainLoop(AppContext);

	/*****
	* if we are using X11R6 then the mainloop is exited without calling exit(), 
	* so we have time for a proper clean up 
	*****/
#if (XtSpecificationRelease == 6)
	XtDestroyApplicationContext(AppContext);
#endif

	return(0);
}

/*****
* While based on the loadImage routines in the other example, this one gets 
* the image from a web server and stores in a disk cache
*****/
static XmImageInfo*
loadImage(Widget w, String url)
{
	XmImageInfo *image = NULL;
	int i;
	char filename[1024];
	char *userData;
	HTTPRequest *req = newHTTPRequest();

	if (url != NULL)
	{
		char *hostname, *file;
		struct stat buf;

		XtVaGetValues(w, XmNuserData, &userData, NULL);

		if(!HTTPAbsoluteURL(url))
			req->url = HTTPFindAbsoluteURL(url, userData);
		else
			req->url = NewString(url);

		parseURL(req->url, PARSE_HOSTNAME | PARSE_FILENAME,
				 NULL, NULL, NULL, &hostname, NULL, &file);
		sprintf(filename, ".cache/%s%s", hostname, file);
		freeURL(PARSE_HOSTNAME | PARSE_FILENAME,
				NULL, NULL, NULL, hostname, 0, file);

		/* when stored in the cache, convert path names to a valid filename */
		for (i = 7; i < strlen(filename); i++)
		{
			if (filename[i] == '/')
				filename[i] = '@';
		}

		if(stat(filename, &buf) != 0 && errno == ENOENT)
		{
			req->type = HTTPLoadToFile;
			req->in_data = NewString(filename);
			loadHTTPURL(NULL, req, NULL);
			if(req->ret != HTTPSuccess)
			{
				/* last ditch attempt to try and load it locally */
				image = XmHTMLImageDefaultProc(w, req->url, NULL, 0);
			}else
				image = XmHTMLImageDefaultProc(w, filename, NULL, 0);

		}else
			image = XmHTMLImageDefaultProc(w, filename, NULL, 0);

	}
	deleteHTTPRequest(req);
	return(image);
}
