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