1 /*****
2 * example_4.c : XmHTML XmNformCallback & HTTP.c demonstration
3 *
4 * This file Version	$Revision: 1.1 $
5 *
6 * Creation date:		Tue Oct 21 01:33:18 GMT+0100 1997
7 * Last modification: 	$Date: 1997/10/23 00:28:33 $
8 * By:					$Author: newt $
9 * Current State:		$State: Exp $
10 *
11 * Author:				newt
12 *
13 * Copyright (C) 1994-1997 By Richard Offer (offer@sgi.com)
14 *
15 * This file is part of the XmHTML Widget Library
16 *
17 * This library is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU [Library] General Public
19 * License as published by the Free Software Foundation; either
20 * version 2 of the License, or (at your option) any later version.
21 *
22 * This library is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25 * Library General Public License for more details.
26 *
27 * You should have received a copy of the GNU [Library] General Public
28 * License along with this library; if not, write to the Free
29 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 *
31 * Note from the author:
32 *
33 *	Most of the code is straight-forward, however since the only real way to
34 *	test the forms support is from a live web server you need some HTTP support.
35 *	I've written some code (you'll find it in HTTP.c) that is fairly flakey,
36 *	but at least it should be easy to rip it out and add something better
37 *	(there's only a single API call loadHTTPUrl() and a couple of
38 *	allocation/cleaning up routines. At the least you can use the HTTP code
39 *	as the basis for your own...
40 *
41 *	I'm not pretending that this code is anywhere near production quality, and
42 *	I don't intend to use it for my broswer app, so giving anyone else anytype
43 *	of warrenty would be pretty stupid of me.
44 *
45 *	I will be tidying this up as I continue to develop my broswer...
46 *
47 *	Its also only been tested on Irix 6.{4,5}.
48 *
49 *	Before you try and use this with the accompanying html file
50 *	(test-pages/form-test.html) you need to install the file form-test.pl in
51 *	the cgi-bin directory of your web server web server---you may need to be
52 *	root to do this.
53 *
54 *	Don't forget to change the first line in the form-test.pl script to point
55 *	to the location of your perl binary
56 *
57 *****/
58 /*****
59 * ChangeLog
60 * $Log: example_4.c,v $
61 * Revision 1.1  1997/10/23 00:28:33  newt
62 * Initial Revision
63 *
64 *****/
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <unistd.h>
69 #include <signal.h>
70 #include <netinet/in.h>
71 #include <errno.h>
72 #include <sys/types.h>
73 #include <sys/stat.h>
74 #include <dirent.h>
75 #include <Xm/XmAll.h>
76 
77 #include <XmHTML/XmHTML.h>
78 
79 /* The Header file for my HTTP implementation */
80 #include <http/HTTP.h>
81 
82 /*** External Function Prototype Declarations ***/
83 
84 /*** Public Variable Declarations ***/
85 
86 /*** Private Datatype Declarations ****/
87 
88 /*** Private Function Prototype Declarations ****/
89 static XmImageInfo *loadImage(Widget w, String url);
90 static void loadFile(Widget w, XtPointer client_data, XtPointer call_data);
91 static void loadURLToWidget(Widget html, String url, HTTPNamedValues * formdata,
92 	HTTPLoadMethod method);
93 static void cleanCache(void);
94 
95 /** various callbacks **/
96 static void formCB(Widget w, XtPointer loadWidget,
97 	XmHTMLFormCallbackStruct * form_data);
98 static void anchorCB(Widget w, XtPointer loadWidget,
99 	XmHTMLAnchorPtr href_data);
100 static void destroyCB(Widget w, XtPointer client, XtPointer call);
101 
102 static void quitCB(Widget w, XtPointer client, XtPointer call);
103 static void loadCB(Widget w, XtPointer client, XtPointer call);
104 
105 /*** Private Variable Declarations ***/
106 static String appFallbackResources[] =
107 {
108 "*fontList:               *-adobe-helvetica-bold-r-*-*-*-120-*-*-p-*-*-*",
109 	"*sourceView.width:       550",
110 	"*sourceView.height:      500",
111 	"*XmHTML.width:           575",
112 	"*XmHTML.height:          600",
113 	NULL};
114 
115 Widget TopLevel;
116 Widget xmHTML;
117 XtAppContext AppContext;
118 
119 /*****
120 * Change this to change the application class of the examples
121 *****/
122 #define APP_CLASS               "HTMLDemos"
123 
124 /*****
125 * Name:
126 * Return Type:
127 * Description:
128 * In:
129 *
130 * Returns:
131 *
132 *****/
133 static void
loadFile(Widget w,XtPointer client_data,XtPointer call_data)134 loadFile(Widget w, XtPointer client_data, XtPointer call_data)
135 {
136 	FILE *file;
137 	char *filename;
138 	int size;
139 	static String content;
140 	XmFileSelectionBoxCallbackStruct *cbs;
141 
142 	XtUnmanageChild((Widget)client_data);
143 
144 	cbs = (XmFileSelectionBoxCallbackStruct*)call_data;
145 
146 	XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);
147 
148 	if (filename == NULL)
149 		return;
150 
151 	/* open the given file */
152 	if ((file = fopen(filename, "r")) == NULL) {
153 		perror(filename);
154 		return;
155 	}
156 	/* see how large this file is */
157 	fseek(file, 0, SEEK_END);
158 	size = ftell(file);
159 	rewind(file);
160 
161 	/* allocate a buffer large enough to contain the entire file */
162 	if ((content = malloc(size + 1)) == NULL) {
163 		fprintf(stderr, "malloc failed for %i bytes\n", size);
164 		exit(1);
165 	}
166 	/* now read the contents of this file */
167 	if ((fread(content, 1, size, file)) != size)
168 		printf("Warning: did not read entire file!\n");
169 
170 	content[size] = '\0';
171 	fclose(file);
172 
173 	XmHTMLTextSetString(xmHTML, content);
174 }
175 
176 static void
loadURLToWidget(Widget html,String url,HTTPNamedValues * formdata,HTTPLoadMethod method)177 loadURLToWidget(Widget html, String url, HTTPNamedValues * formdata,
178 	HTTPLoadMethod method)
179 {
180 	HTTPRequest *req = newHTTPRequest();
181 	char *userData = NULL;
182 
183 	XtVaGetValues(html, XmNuserData, &userData, NULL);
184 
185 	if(!HTTPAbsoluteURL(url))
186 		req->url = HTTPFindAbsoluteURL(url, userData);
187 	else
188 		req->url = NewString(url);
189 
190 	req->type = HTTPLoadToString;
191 	req->method = method;
192 
193 	if(url)
194 	{
195 		req->form_data = formdata;
196 
197 		loadHTTPURL(NULL, req, NULL);
198 		if(req->ret == HTTPSuccess)
199 		{
200 			if(req->url)
201 			{
202 				char *new_url = NewString(req->url);
203 
204 				XtVaSetValues(html, XmNuserData, new_url, NULL);
205 				if(userData)	/* can be NULL! */
206 					free(userData);
207 
208 				/*
209 				* Since this is form data, we want to unescape any
210 				* escapes contained in the response string.
211 				*/
212 				HTTPUnescapeResponse(req->out_data);
213 
214 				/* set it */
215 				XmHTMLTextSetString(html, (String) req->out_data);
216 			}
217 		}
218 		else
219 			HTTPError("Request failed", req->ret);
220 	}
221 	/* Clean up the request */
222 	deleteHTTPRequest(req);
223 }
224 
225 static void
formCB(Widget w,XtPointer loadWidget,XmHTMLFormCallbackStruct * form_data)226 formCB(Widget w, XtPointer loadWidget, XmHTMLFormCallbackStruct * form_data)
227 {
228 
229 	int i;
230 	HTTPLoadMethod method = form_data->method == 0 ? HTTPGET : HTTPPOST;
231 	HTTPNamedValues *form = NULL;
232 
233 	/* we don't support the pipe method */
234 	if(form_data->method == XmHTML_FORM_PIPE)
235 		return;
236 
237 	/*
238 	 * the form data is freed inside the deleteHTTPRequest() function
239 	 * (called from loadURLToWidget())
240 	 */
241 	form = (HTTPNamedValues *) calloc(form_data->ncomponents + 1,
242 									  sizeof(HTTPNamedValues));
243 
244 	/* we don't pass a length so loop until we see a NULL name */
245 	for(i = 0; i < form_data->ncomponents; i++)
246 	{
247 		form[i].name = NewString(form_data->components[i].name);
248 		form[i].value = NewString(form_data->components[i].value);
249 	}
250 	form[i].name = NULL;
251 	form[i].value = NULL;
252 
253 	loadURLToWidget(loadWidget, form_data->action, form, method);
254 }
255 
256 static void
anchorCB(Widget w,XtPointer loadWidget,XmHTMLAnchorPtr href_data)257 anchorCB(Widget w, XtPointer loadWidget, XmHTMLAnchorPtr href_data)
258 {
259 	/* see if we have been called with a valid reason */
260 	if (href_data->reason != XmCR_ACTIVATE)
261 		return;
262 
263 	switch (href_data->url_type)
264 	{
265 		case ANCHOR_FILE_LOCAL:
266 		case ANCHOR_HTTP:
267 			loadURLToWidget(loadWidget, href_data->href, NULL, HTTPGET);
268 			break;
269 		case ANCHOR_JUMP:
270 			{
271 				int id;
272 				if((id = XmHTMLAnchorGetId(loadWidget, href_data->href)) != -1)
273 				{
274 					/* let XmHTML jump and mark as visited */
275 					href_data->doit = True;
276 					href_data->visited = True;
277 					return;
278 				};
279 			}
280 			break;
281 		case ANCHOR_UNKNOWN:
282 		default:
283 			fprintf(stderr, "don't handle this type of url: %d %s\n",
284 				href_data->url_type, href_data->href);
285 			break;
286 	}
287 }
288 
289 static void
destroyCB(Widget w,XtPointer client,XtPointer call)290 destroyCB(Widget w, XtPointer client, XtPointer call)
291 {
292 	void *userData = NULL;
293 
294 	XtVaGetValues(w, XmNuserData, &userData, NULL);
295 
296 	if(userData)
297 		free((char *)userData);
298 }
299 
300 void
cleanCache(void)301 cleanCache(void)
302 {
303 	DIR *dir;
304 	struct dirent *fileent;
305 
306 	/*****
307 	* Clean out the image cache dir, mainly I do this to force a re-load of the
308 	* images when I next start the app
309 	*****/
310 	dir = opendir(".cache");
311 
312 	if (dir == NULL)
313 	{
314 		if(mkdir(".cache", S_IRWXU) != 0)
315 		{
316 			printf("cannot create image cache dir\n");
317 			exit(1);
318 		} else
319 			dir = opendir(".cache");
320 	}
321 	while((fileent = readdir(dir)) != NULL)
322 	{
323 		/* ignore dot files (there shouldn't be any) */
324 		if (strncmp(fileent->d_name, ".", 1))
325 		{
326 			char filename[256];
327 			sprintf(filename, "%s/%s", ".cache", fileent->d_name);
328 			printf("removing %s\n", filename);
329 			unlink(filename);
330 		}
331 	}
332 	if(dir)
333 		closedir(dir);
334 }
335 
336 static void
destroyXmHTMLCB(Widget w,XtPointer client,XtPointer call)337 destroyXmHTMLCB(Widget w, XtPointer client, XtPointer call)
338 {
339 	XtDestroyWidget(xmHTML);
340 }
341 
342 static void
quitCB(Widget w,XtPointer client,XtPointer call)343 quitCB(Widget w, XtPointer client, XtPointer call)
344 {
345 	cleanCache();
346 
347 #if (XtSpecificationRelease <= 5)
348 	XtDestroyApplicationContext(AppContext);
349 	exit(EXIT_SUCCESS);
350 #else
351 	/*****
352 	* The 'approved' method for killing X11R6 apps, previous versions just use
353 	* exit(0).
354 	*****/
355 	XtAppSetExitFlag(AppContext);
356 #endif
357 
358 }
359 
360 static void
loadCB(Widget w,XtPointer client,XtPointer call)361 loadCB(Widget w, XtPointer client, XtPointer call)
362 {
363 
364 	static Widget fsb = NULL;
365 	Arg args[2];
366 	XmString xmstr;
367 
368 	if(fsb == NULL)
369 	{
370 		xmstr = XmStringCreateLocalized("*.html");
371 		XtSetArg(args[0], XmNpattern, xmstr);
372 
373 		fsb = XmCreateFileSelectionDialog(TopLevel, "fileOpen", args, 1);
374 
375 		XmStringFree(xmstr);
376 
377 		XtAddCallback(fsb, XmNokCallback,
378 					  (XtCallbackProc) loadFile, fsb);
379 		XtAddCallback(fsb, XmNcancelCallback,
380 					  (XtCallbackProc) XtUnmanageChild, NULL);
381 	}
382 	XtManageChild(fsb);
383 }
384 
385 int
main(int argc,char ** argv)386 main(int argc, char **argv)
387 {
388 
389 	Widget form, menubar, pane, quit, home, dest;
390 
391 	XtSetLanguageProc(NULL, NULL, NULL);
392 
393 	/* clean the cache to force all images to be re-loaded */
394 	cleanCache();
395 
396 	/* sessionShellWidgetClass only from R6 on */
397 #if (XtSpecificationRelease <= 5)
398 	TopLevel = XtVaAppInitialize(&AppContext, APP_CLASS, NULL, 0,
399 				&argc, argv, appFallbackResources, NULL, NULL);
400 #else
401 	TopLevel = XtVaOpenApplication(&AppContext, APP_CLASS, NULL, 0,
402 				&argc, argv, appFallbackResources, sessionShellWidgetClass,
403 				NULL);
404 #endif
405 
406 	form = XtVaCreateWidget("topForm", xmFormWidgetClass, TopLevel,
407 				NULL);
408 
409 	menubar = XmCreateMenuBar(form, "menuBar", NULL, 0);
410 	pane = XmCreatePulldownMenu(menubar, "FilePane", NULL, 0);
411 
412 	XtVaCreateManagedWidget("File", xmCascadeButtonGadgetClass, menubar,
413 			XmNsubMenuId, pane,
414 			NULL);
415 
416 	home = XtVaCreateManagedWidget("load", xmPushButtonGadgetClass, pane, NULL);
417 	dest = XtVaCreateManagedWidget("destroy", xmPushButtonGadgetClass, pane,
418 			NULL);
419 	quit = XtVaCreateManagedWidget("Quit", xmPushButtonGadgetClass, pane, NULL);
420 
421 	XtAddCallback(quit, XmNactivateCallback, (XtCallbackProc)quitCB,
422 		(XtPointer)NULL);
423 	XtAddCallback(dest, XmNactivateCallback, (XtCallbackProc)destroyXmHTMLCB,
424 		(XtPointer)NULL);
425 
426 
427 	XtVaSetValues(menubar,
428 		XmNtopAttachment, XmATTACH_FORM,
429 		XmNleftAttachment, XmATTACH_FORM,
430 		XmNrightAttachment, XmATTACH_FORM,
431 		NULL);
432 
433 
434 	xmHTML = XtVaCreateManagedWidget("HTML", xmHTMLWidgetClass, form,
435 				XmNtopAttachment, XmATTACH_WIDGET,
436 				XmNtopWidget, menubar,
437 				XmNleftAttachment, XmATTACH_FORM,
438 				XmNrightAttachment, XmATTACH_FORM,
439 				XmNbottomAttachment, XmATTACH_FORM,
440 #ifdef HAVE_GIF_CODEC
441 				XmNdecodeGIFProc, decodeGIFImage,
442 #endif
443 				XmNimageProc, loadImage,
444 				XmNenableBadHTMLWarnings, False,
445 				XmNuserData, NULL,
446 				NULL);
447 
448 
449 	XtAddCallback(home, XmNactivateCallback, (XtCallbackProc)loadCB,
450 		(XtPointer)NULL);
451 	XtAddCallback(xmHTML, XmNdestroyCallback, (XtCallbackProc)destroyCB,
452 		(XtPointer)xmHTML);
453 	XtAddCallback(xmHTML, XmNactivateCallback, (XtCallbackProc)anchorCB,
454 		(XtPointer)xmHTML);
455 	XtAddCallback(xmHTML, XmNformCallback, (XtCallbackProc)formCB,
456 		(XtPointer)xmHTML);
457 
458 	XtManageChild(menubar);
459 	XtManageChild(form);
460 
461 	/* as I check (ha) all read and write return values, ignore SIGPIPE */
462 	signal(SIGPIPE, SIG_IGN);
463 
464 	XtRealizeWidget(TopLevel);
465 	XtAppMainLoop(AppContext);
466 
467 	/*****
468 	* if we are using X11R6 then the mainloop is exited without calling exit(),
469 	* so we have time for a proper clean up
470 	*****/
471 #if (XtSpecificationRelease == 6)
472 	XtDestroyApplicationContext(AppContext);
473 #endif
474 
475 	return(0);
476 }
477 
478 /*****
479 * While based on the loadImage routines in the other example, this one gets
480 * the image from a web server and stores in a disk cache
481 *****/
482 static XmImageInfo*
loadImage(Widget w,String url)483 loadImage(Widget w, String url)
484 {
485 	XmImageInfo *image = NULL;
486 	int i;
487 	char filename[1024];
488 	char *userData;
489 	HTTPRequest *req = newHTTPRequest();
490 
491 	if (url != NULL)
492 	{
493 		char *hostname, *file;
494 		struct stat buf;
495 
496 		XtVaGetValues(w, XmNuserData, &userData, NULL);
497 
498 		if(!HTTPAbsoluteURL(url))
499 			req->url = HTTPFindAbsoluteURL(url, userData);
500 		else
501 			req->url = NewString(url);
502 
503 		parseURL(req->url, PARSE_HOSTNAME | PARSE_FILENAME,
504 				 NULL, NULL, NULL, &hostname, NULL, &file);
505 		sprintf(filename, ".cache/%s%s", hostname, file);
506 		freeURL(PARSE_HOSTNAME | PARSE_FILENAME,
507 				NULL, NULL, NULL, hostname, 0, file);
508 
509 		/* when stored in the cache, convert path names to a valid filename */
510 		for (i = 7; i < strlen(filename); i++)
511 		{
512 			if (filename[i] == '/')
513 				filename[i] = '@';
514 		}
515 
516 		if(stat(filename, &buf) != 0 && errno == ENOENT)
517 		{
518 			req->type = HTTPLoadToFile;
519 			req->in_data = NewString(filename);
520 			loadHTTPURL(NULL, req, NULL);
521 			if(req->ret != HTTPSuccess)
522 			{
523 				/* last ditch attempt to try and load it locally */
524 				image = XmHTMLImageDefaultProc(w, req->url, NULL, 0);
525 			}else
526 				image = XmHTMLImageDefaultProc(w, filename, NULL, 0);
527 
528 		}else
529 			image = XmHTMLImageDefaultProc(w, filename, NULL, 0);
530 
531 	}
532 	deleteHTTPRequest(req);
533 	return(image);
534 }
535