1 /*
2 * $LynxId: LYMap.c,v 1.50 2018/03/05 22:32:14 tom Exp $
3 * Lynx Client-side Image MAP Support LYMap.c
4 * ==================================
5 *
6 * Author: FM Foteos Macrides (macrides@sci.wfbr.edu)
7 *
8 */
9
10 #include <HTUtils.h>
11 #include <HTTP.h>
12 #include <HTAnchor.h>
13 #include <HTAccess.h>
14 #include <HTFormat.h>
15 #include <HTParse.h>
16 #include <HTAlert.h>
17 #include <LYUtils.h>
18 #include <LYMap.h>
19 #include <GridText.h>
20 #include <LYGlobalDefs.h>
21 #include <LYKeymap.h>
22 #include <LYCharUtils.h>
23 #include <LYCharSets.h>
24 #include <LYStrings.h>
25
26 #ifdef DIRED_SUPPORT
27 #include <LYUpload.h>
28 #include <LYLocal.h>
29 #endif
30
31 #include <LYexit.h>
32 #include <LYLeaks.h>
33
34 #define NO_MAP_TITLE "[USEMAP]"
35
36 typedef struct _LYMapElement {
37 char *address;
38 char *title;
39 BOOLEAN intern_flag;
40 } LYMapElement;
41
42 typedef struct _LYImageMap {
43 char *address;
44 char *title;
45 HTList *elements;
46 } LYImageMap;
47
48 static HTList *LynxMaps = NULL;
49
50 BOOL LYMapsOnly = FALSE;
51
52 /*
53 * Utility for freeing a list of MAPs.
54 */
ImageMapList_free(HTList * theList)55 void ImageMapList_free(HTList *theList)
56 {
57 LYImageMap *map;
58 LYMapElement *element;
59 HTList *cur = theList;
60 HTList *current;
61
62 if (!cur)
63 return;
64
65 while (NULL != (map = (LYImageMap *) HTList_nextObject(cur))) {
66 FREE(map->address);
67 FREE(map->title);
68 if (map->elements) {
69 current = map->elements;
70 while (NULL !=
71 (element = (LYMapElement *) HTList_nextObject(current))) {
72 FREE(element->address);
73 FREE(element->title);
74 FREE(element);
75 }
76 HTList_delete(map->elements);
77 map->elements = NULL;
78 }
79 FREE(map);
80 }
81 HTList_delete(theList);
82 return;
83 }
84
85 #ifdef LY_FIND_LEAKS
86 /*
87 * Utility for freeing the global list of MAPs. - kw
88 */
LYLynxMaps_free(void)89 static void LYLynxMaps_free(void)
90 {
91 ImageMapList_free(LynxMaps);
92 LynxMaps = NULL;
93 return;
94 }
95 #endif /* LY_FIND_LEAKS */
96
97 /*
98 * We keep two kinds of lists:
99 * - A global list (LynxMaps) shared by MAPs from all documents that
100 * do not have POST data.
101 * - For each response to a POST which contains MAPs, a list specific
102 * to this combination of URL and post_data. It is kept in the
103 * HTParentAnchor structure and is freed when the document is removed
104 * from memory, in the course of normal removal of anchors.
105 * MAPs from POST responses can only be accessed via internal links,
106 * i.e., from within the same document (with the same post_data).
107 * The notion of "same document" is extended, so that LYNXIMGMAP:
108 * and List Page screens are logically part of the document on which
109 * they are based. - kw
110 *
111 * If track_internal_links is false, only the global list will be used
112 * for all MAPs.
113 *
114 */
115
116 /*
117 * Utility for creating an LYImageMap list, if it doesn't exist already, adding
118 * LYImageMap entry structures if needed, and removing any LYMapElements in a
119 * pre-existing LYImageMap entry so that it will have only those from AREA tags
120 * for the current analysis of MAP element content. - FM
121 */
LYAddImageMap(char * address,char * title,HTParentAnchor * node_anchor)122 BOOL LYAddImageMap(char *address,
123 char *title,
124 HTParentAnchor *node_anchor)
125 {
126 LYImageMap *tmp = NULL;
127 LYImageMap *old = NULL;
128 HTList *cur = NULL;
129 HTList *theList = NULL;
130 HTList *curele = NULL;
131 LYMapElement *ele = NULL;
132
133 if (isEmpty(address))
134 return FALSE;
135 if (!(node_anchor && node_anchor->address))
136 return FALSE;
137
138 /*
139 * Set theList to either the global LynxMaps list or, if we are associated
140 * with post data, the specific list. The list is created if it doesn't
141 * already exist. - kw
142 */
143 if (track_internal_links && node_anchor->post_data) {
144 /*
145 * We are handling a MAP element found while parsing node_anchor's
146 * stream of data, and node_anchor has post_data associated and should
147 * therefore represent a POST response, so use the specific list. - kw
148 */
149 theList = node_anchor->imaps;
150 if (!theList) {
151 theList = node_anchor->imaps = HTList_new();
152 }
153 } else {
154 if (!LynxMaps) {
155 LynxMaps = HTList_new();
156 #ifdef LY_FIND_LEAKS
157 atexit(LYLynxMaps_free);
158 #endif
159 }
160 theList = LynxMaps;
161 }
162
163 if (theList) {
164 cur = theList;
165 while (NULL != (old = (LYImageMap *) HTList_nextObject(cur))) {
166 if (old->address == 0) /* shouldn't happen */
167 continue;
168 if (!strcmp(old->address, address)) {
169 FREE(old->address);
170 FREE(old->title);
171 if (old->elements) {
172 curele = old->elements;
173 while (NULL !=
174 (ele = (LYMapElement *) HTList_nextObject(curele))) {
175 FREE(ele->address);
176 FREE(ele->title);
177 FREE(ele);
178 }
179 HTList_delete(old->elements);
180 old->elements = NULL;
181 }
182 break;
183 }
184 }
185 }
186
187 tmp = (old != NULL) ?
188 old : typecalloc(LYImageMap);
189 if (tmp == NULL) {
190 outofmem(__FILE__, "LYAddImageMap");
191 return FALSE;
192 }
193 StrAllocCopy(tmp->address, address);
194 if (non_empty(title))
195 StrAllocCopy(tmp->title, title);
196 if (tmp != old)
197 HTList_addObject(theList, tmp);
198 return TRUE;
199 }
200
201 /*
202 * Utility for adding LYMapElement's to LYImageMap's
203 * in the appropriate list. - FM
204 */
LYAddMapElement(char * map,char * address,char * title,HTParentAnchor * node_anchor,int intern_flag GCC_UNUSED)205 BOOL LYAddMapElement(char *map,
206 char *address,
207 char *title,
208 HTParentAnchor *node_anchor,
209 int intern_flag GCC_UNUSED)
210 {
211 LYMapElement *tmp = NULL;
212 LYImageMap *theMap = NULL;
213 HTList *theList = NULL;
214 HTList *cur = NULL;
215
216 if (isEmpty(map) || isEmpty(address))
217 return FALSE;
218 if (!(node_anchor && node_anchor->address))
219 return FALSE;
220
221 /*
222 * Set theList to either the global LynxMaps list or, if we are associated
223 * with post data, the specific list. The list should already exist, since
224 * this function is only called if the AREA tag we are handling was within
225 * a MAP element in node_anchor's stream of data, so that LYAddImageMap has
226 * been called. - kw
227 */
228 if (track_internal_links && node_anchor->post_data) {
229 /*
230 * We are handling an AREA tag found while parsing node_anchor's stream
231 * of data, and node_anchor has post_data associated and should
232 * therefore represent a POST response, so use the specific list. - kw
233 */
234 theList = node_anchor->imaps;
235 if (!theList) {
236 return FALSE;
237 }
238 } else {
239 if (!LynxMaps)
240 LYAddImageMap(map, NULL, node_anchor);
241 theList = LynxMaps;
242 }
243
244 cur = theList;
245 while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
246 if (!strcmp(theMap->address, map)) {
247 break;
248 }
249 }
250 if (!theMap)
251 return FALSE;
252 if (!theMap->elements)
253 theMap->elements = HTList_new();
254 cur = theMap->elements;
255 while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) {
256 if (!strcmp(tmp->address, address)) {
257 FREE(tmp->address);
258 FREE(tmp->title);
259 HTList_removeObject(theMap->elements, tmp);
260 FREE(tmp);
261 break;
262 }
263 }
264
265 tmp = typecalloc(LYMapElement);
266 if (tmp == NULL) {
267 perror("Out of memory in LYAddMapElement");
268 return FALSE;
269 }
270 StrAllocCopy(tmp->address, address);
271 if (non_empty(title))
272 StrAllocCopy(tmp->title, title);
273 else
274 StrAllocCopy(tmp->title, address);
275 if (track_internal_links)
276 tmp->intern_flag = (BOOLEAN) intern_flag;
277 HTList_appendObject(theMap->elements, tmp);
278
279 CTRACE((tfp,
280 "LYAddMapElement\n\tmap %s\n\taddress %s\n\ttitle %s)\n",
281 NonNull(map), NonNull(address), NonNull(title)));
282
283 return TRUE;
284 }
285
286 /*
287 * Utility for checking whether an LYImageMap entry with a given address
288 * already exists in the LynxMaps structure. - FM
289 */
LYHaveImageMap(char * address)290 BOOL LYHaveImageMap(char *address)
291 {
292 LYImageMap *Map;
293 HTList *cur = LynxMaps;
294
295 if (!(cur && non_empty(address)))
296 return FALSE;
297
298 while (NULL != (Map = (LYImageMap *) HTList_nextObject(cur))) {
299 if (!strcmp(Map->address, address)) {
300 return TRUE;
301 }
302 }
303
304 return FALSE;
305 }
306
307 /*
308 * Fills in a DocAddress structure for getting the HTParentAnchor of the
309 * underlying resource. ALso returns a pointer to that anchor in
310 * *punderlying if we are dealing with POST data. - kw
311 *
312 * address is the address of the underlying resource, i.e., the one
313 * containing the MAP element, the MAP's name appended as
314 * fragment is ignored.
315 * anAnchor is the LYNXIMGMAP: anchor; if it is associated with POST
316 * data, we want the specific list, otherwise the global list.
317 */
fill_DocAddress(DocAddress * wwwdoc,const char * address,HTParentAnchor * anAnchor,HTParentAnchor ** punderlying)318 static void fill_DocAddress(DocAddress *wwwdoc,
319 const char *address,
320 HTParentAnchor *anAnchor,
321 HTParentAnchor **punderlying)
322 {
323 char *doc_address = NULL;
324 HTParentAnchor *underlying;
325
326 StrAllocCopy(doc_address, address);
327 if (anAnchor && anAnchor->post_data) {
328 wwwdoc->address = doc_address;
329 wwwdoc->post_data = anAnchor->post_data;
330 wwwdoc->post_content_type = anAnchor->post_content_type;
331 wwwdoc->bookmark = NULL;
332 wwwdoc->isHEAD = FALSE;
333 wwwdoc->safe = FALSE;
334 underlying = HTAnchor_findAddress(wwwdoc);
335 if (underlying->safe)
336 wwwdoc->safe = TRUE;
337 if (punderlying)
338 *punderlying = underlying;
339 } else {
340 wwwdoc->address = doc_address;
341 wwwdoc->post_data = NULL;
342 wwwdoc->post_content_type = NULL;
343 wwwdoc->bookmark = NULL;
344 wwwdoc->isHEAD = FALSE;
345 wwwdoc->safe = FALSE;
346 if (punderlying)
347 *punderlying = NULL;
348 }
349 }
350
351 /*
352 * Get the appropriate list for creating a LYNXIMGMAP: pseudo- document:
353 * either the global list (LynxMaps), or the specific list if a List Page for a
354 * POST response is requested. Also fill in the DocAddress structure etc. by
355 * calling fill_DocAddress().
356 *
357 * address is the address of the underlying resource, i.e., the one
358 * containing the MAP element, the MAP's name appended as
359 * fragment is ignored.
360 * anchor is the LYNXIMGMAP: anchor for which LYLoadIMGmap() is
361 * requested; if it is associated with POST data, we want the
362 * specific list for this combination of address+post_data.
363 *
364 * if track_internal_links is false, the Anchor passed to
365 * LYLoadIMGmap() will never have post_data, so that the global list
366 * will be used. - kw
367 */
get_the_list(DocAddress * wwwdoc,const char * address,HTParentAnchor * anchor,HTParentAnchor ** punderlying)368 static HTList *get_the_list(DocAddress *wwwdoc,
369 const char *address,
370 HTParentAnchor *anchor,
371 HTParentAnchor **punderlying)
372 {
373 HTList *result;
374
375 if (anchor->post_data) {
376 fill_DocAddress(wwwdoc, address, anchor, punderlying);
377 if (non_empty(punderlying)) {
378 result = (*punderlying)->imaps;
379 } else {
380 result = anchor->imaps;
381 }
382 } else {
383 fill_DocAddress(wwwdoc, address, NULL, punderlying);
384 result = LynxMaps;
385 }
386 return result;
387 }
388
389 /* LYLoadIMGmap - F.Macrides (macrides@sci.wfeb.edu)
390 * ------------
391 * Create a text/html stream with a list of links
392 * for HyperText References in AREAs of a MAP.
393 */
394
LYLoadIMGmap(const char * arg,HTParentAnchor * anAnchor,HTFormat format_out,HTStream * sink)395 static int LYLoadIMGmap(const char *arg,
396 HTParentAnchor *anAnchor,
397 HTFormat format_out,
398 HTStream *sink)
399 {
400 HTFormat format_in = WWW_HTML;
401 HTStream *target = NULL;
402 char *buf = NULL;
403 LYMapElement *tmp = NULL;
404 LYImageMap *theMap = NULL;
405 char *MapTitle = NULL;
406 char *MapAddress = NULL;
407 HTList *theList;
408 HTList *cur = NULL;
409 const char *address = NULL;
410 char *cp = NULL;
411 DocAddress WWWDoc;
412 HTParentAnchor *underlying;
413 BOOL old_cache_setting = LYforce_no_cache;
414 BOOL old_reloading = reloading;
415 HTFormat old_format_out = HTOutputFormat;
416
417 if (isLYNXIMGMAP(arg)) {
418 address = (arg + LEN_LYNXIMGMAP);
419 }
420 if (!(address && StrChr(address, ':'))) {
421 HTAlert(MISDIRECTED_MAP_REQUEST);
422 return (HT_NOT_LOADED);
423 }
424
425 theList = get_the_list(&WWWDoc, address, anAnchor, &underlying);
426 if (WWWDoc.safe)
427 anAnchor->safe = TRUE;
428
429 if (!theList) {
430 if (anAnchor->post_data && !WWWDoc.safe &&
431 ((underlying && underlying->document && !LYforce_no_cache) ||
432 HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) {
433 HTAlert(FAILED_MAP_POST_REQUEST);
434 return (HT_NOT_LOADED);
435 }
436 LYforce_no_cache = TRUE;
437 reloading = TRUE;
438 HTOutputFormat = WWW_PRESENT;
439 LYMapsOnly = TRUE;
440 if (!HTLoadAbsolute(&WWWDoc)) {
441 LYforce_no_cache = old_cache_setting;
442 reloading = old_reloading;
443 HTOutputFormat = old_format_out;
444 LYMapsOnly = FALSE;
445 HTAlert(MAP_NOT_ACCESSIBLE);
446 return (HT_NOT_LOADED);
447 }
448 LYforce_no_cache = old_cache_setting;
449 reloading = old_reloading;
450 HTOutputFormat = old_format_out;
451 LYMapsOnly = FALSE;
452 theList = get_the_list(&WWWDoc, address, anAnchor, &underlying);
453 }
454
455 if (!theList) {
456 HTAlert(MAPS_NOT_AVAILABLE);
457 return (HT_NOT_LOADED);
458 }
459
460 cur = theList;
461 while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
462 if (!strcmp(theMap->address, address)) {
463 break;
464 }
465 }
466 if (theMap && HTList_count(theMap->elements) == 0) {
467 /*
468 * We found a MAP without any usable AREA. Fake a redirection to the
469 * address with fragment. We do this even for post data (internal link
470 * within a document with post data) if it will not result in an
471 * unwanted network request. - kw
472 */
473 if (!anAnchor->post_data) {
474 StrAllocCopy(redirecting_url, address);
475 return (HT_REDIRECTING);
476 } else if (WWWDoc.safe ||
477 (underlying->document && !anAnchor->document &&
478 (LYinternal_flag || LYoverride_no_cache))) {
479 StrAllocCopy(redirecting_url, address);
480 redirect_post_content = TRUE;
481 return (HT_REDIRECTING);
482 }
483 }
484 if (!(theMap && theMap->elements)) {
485 if (anAnchor->post_data && !WWWDoc.safe &&
486 ((underlying && underlying->document && !LYforce_no_cache) ||
487 HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) {
488 HTAlert(FAILED_MAP_POST_REQUEST);
489 return (HT_NOT_LOADED);
490 }
491 LYforce_no_cache = TRUE;
492 reloading = TRUE;
493 HTOutputFormat = WWW_PRESENT;
494 LYMapsOnly = TRUE;
495 if (!HTLoadAbsolute(&WWWDoc)) {
496 LYforce_no_cache = old_cache_setting;
497 reloading = old_reloading;
498 HTOutputFormat = old_format_out;
499 LYMapsOnly = FALSE;
500 HTAlert(MAP_NOT_ACCESSIBLE);
501 return (HT_NOT_LOADED);
502 }
503 LYforce_no_cache = old_cache_setting;
504 reloading = old_reloading;
505 HTOutputFormat = old_format_out;
506 LYMapsOnly = FALSE;
507 cur = get_the_list(&WWWDoc, address, anAnchor, &underlying);
508 while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
509 if (!strcmp(theMap->address, address)) {
510 break;
511 }
512 }
513 if (!(theMap && theMap->elements)) {
514 HTAlert(MAP_NOT_AVAILABLE);
515 return (HT_NOT_LOADED);
516 }
517 }
518 if (track_internal_links)
519 anAnchor->no_cache = TRUE;
520
521 target = HTStreamStack(format_in,
522 format_out,
523 sink, anAnchor);
524
525 if (target == NULL) {
526 HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
527 HTAtom_name(format_in), HTAtom_name(format_out));
528 HTAlert(buf);
529 FREE(buf);
530 return (HT_NOT_LOADED);
531 }
532
533 if (non_empty(theMap->title)) {
534 StrAllocCopy(MapTitle, theMap->title);
535 } else if (non_empty(anAnchor->title)) {
536 StrAllocCopy(MapTitle, anAnchor->title);
537 } else if (non_empty(LYRequestTitle) &&
538 strcasecomp(LYRequestTitle, NO_MAP_TITLE)) {
539 StrAllocCopy(MapTitle, LYRequestTitle);
540 } else if ((cp = StrChr(address, '#')) != NULL) {
541 StrAllocCopy(MapTitle, (cp + 1));
542 }
543 if (isEmpty(MapTitle)) {
544 StrAllocCopy(MapTitle, NO_MAP_TITLE);
545 } else {
546 LYEntify(&MapTitle, TRUE);
547 }
548
549 #define PUTS(buf) (*target->isa->put_block)(target, buf, (int) strlen(buf))
550
551 HTSprintf0(&buf, "<html>\n<head>\n");
552 PUTS(buf);
553 HTSprintf0(&buf, "<META %s content=\"" STR_HTML ";charset=%s\">\n",
554 "http-equiv=\"content-type\"",
555 LYCharSet_UC[current_char_set].MIMEname);
556 PUTS(buf);
557 /*
558 * This page is a list of titles and anchors for them. Since titles
559 * already passed SGML/HTML stage they are converted to current_char_set.
560 * That is why we insist on META charset for this page.
561 */
562 HTSprintf0(&buf, "<title>%s</title>\n", MapTitle);
563 PUTS(buf);
564 HTSprintf0(&buf, "</head>\n<body>\n");
565 PUTS(buf);
566
567 HTSprintf0(&buf, "<h1><em>%s</em></h1>\n", MapTitle);
568 PUTS(buf);
569
570 StrAllocCopy(MapAddress, address);
571 LYEntify(&MapAddress, FALSE);
572 HTSprintf0(&buf, "<h2><em>MAP:</em> %s</h2>\n", MapAddress);
573 PUTS(buf);
574
575 HTSprintf0(&buf, "<%s compact>\n", ((keypad_mode == NUMBERS_AS_ARROWS) ?
576 "ol" : "ul"));
577 PUTS(buf);
578 cur = theMap->elements;
579 while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) {
580 StrAllocCopy(MapAddress, tmp->address);
581 LYEntify(&MapAddress, FALSE);
582 PUTS("<li><a href=\"");
583 PUTS(MapAddress);
584 PUTS("\"");
585 if (track_internal_links && tmp->intern_flag) {
586 PUTS(" TYPE=\"internal link\"");
587 }
588 PUTS("\n>");
589 LYformTitle(&MapTitle, tmp->title);
590 LYEntify(&MapTitle, TRUE);
591 PUTS(MapTitle);
592 PUTS("</a>\n");
593 }
594 HTSprintf0(&buf, "</%s>\n</body>\n</html>\n",
595 ((keypad_mode == NUMBERS_AS_ARROWS)
596 ? "ol"
597 : "ul"));
598 PUTS(buf);
599
600 (*target->isa->_free) (target);
601 FREE(MapAddress);
602 FREE(MapTitle);
603 FREE(buf);
604 return (HT_LOADED);
605 }
606
LYPrintImgMaps(FILE * fp)607 void LYPrintImgMaps(FILE *fp)
608 {
609 const char *only = HTLoadedDocumentURL();
610 size_t only_len = strlen(only);
611 HTList *outer = LynxMaps;
612 HTList *inner;
613 LYImageMap *map;
614 LYMapElement *elt;
615 int count;
616
617 if (HTList_count(outer) > 0) {
618 while (NULL != (map = (LYImageMap *) HTList_nextObject(outer))) {
619 if (only_len != 0) {
620 if (StrNCmp(only, map->address, only_len)
621 || (map->address[only_len] != '\0'
622 && map->address[only_len] != '#')) {
623 continue;
624 }
625 }
626 fprintf(fp, "\n%s\n", isEmpty(map->title) ? NO_MAP_TITLE : map->title);
627 fprintf(fp, "%s\n", map->address);
628 inner = map->elements;
629 count = 0;
630 while (NULL != (elt = (LYMapElement *) HTList_nextObject(inner))) {
631 fprintf(fp, "%4d. %s", ++count, elt->address);
632 if (track_internal_links && elt->intern_flag)
633 fprintf(fp, " TYPE=\"internal link\"");
634 fprintf(fp, "\n");
635 }
636 }
637 }
638 }
639
640 #ifdef GLOBALDEF_IS_MACRO
641 #define _LYIMGMAP_C_GLOBALDEF_1_INIT { "LYNXIMGMAP", LYLoadIMGmap, 0}
642 GLOBALDEF(HTProtocol, LYLynxIMGmap, _LYIMGMAP_C_GLOBALDEF_1_INIT);
643 #else
644 GLOBALDEF HTProtocol LYLynxIMGmap =
645 {"LYNXIMGMAP", LYLoadIMGmap, 0};
646 #endif /* GLOBALDEF_IS_MACRO */
647