summaryrefslogtreecommitdiffstats
path: root/src/LYMap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LYMap.c')
-rw-r--r--src/LYMap.c646
1 files changed, 646 insertions, 0 deletions
diff --git a/src/LYMap.c b/src/LYMap.c
new file mode 100644
index 0000000..29b60f1
--- /dev/null
+++ b/src/LYMap.c
@@ -0,0 +1,646 @@
+/*
+ * $LynxId: LYMap.c,v 1.50 2018/03/05 22:32:14 tom Exp $
+ * Lynx Client-side Image MAP Support LYMap.c
+ * ==================================
+ *
+ * Author: FM Foteos Macrides (macrides@sci.wfbr.edu)
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <HTAnchor.h>
+#include <HTAccess.h>
+#include <HTFormat.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <LYUtils.h>
+#include <LYMap.h>
+#include <GridText.h>
+#include <LYGlobalDefs.h>
+#include <LYKeymap.h>
+#include <LYCharUtils.h>
+#include <LYCharSets.h>
+#include <LYStrings.h>
+
+#ifdef DIRED_SUPPORT
+#include <LYUpload.h>
+#include <LYLocal.h>
+#endif
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#define NO_MAP_TITLE "[USEMAP]"
+
+typedef struct _LYMapElement {
+ char *address;
+ char *title;
+ BOOLEAN intern_flag;
+} LYMapElement;
+
+typedef struct _LYImageMap {
+ char *address;
+ char *title;
+ HTList *elements;
+} LYImageMap;
+
+static HTList *LynxMaps = NULL;
+
+BOOL LYMapsOnly = FALSE;
+
+/*
+ * Utility for freeing a list of MAPs.
+ */
+void ImageMapList_free(HTList *theList)
+{
+ LYImageMap *map;
+ LYMapElement *element;
+ HTList *cur = theList;
+ HTList *current;
+
+ if (!cur)
+ return;
+
+ while (NULL != (map = (LYImageMap *) HTList_nextObject(cur))) {
+ FREE(map->address);
+ FREE(map->title);
+ if (map->elements) {
+ current = map->elements;
+ while (NULL !=
+ (element = (LYMapElement *) HTList_nextObject(current))) {
+ FREE(element->address);
+ FREE(element->title);
+ FREE(element);
+ }
+ HTList_delete(map->elements);
+ map->elements = NULL;
+ }
+ FREE(map);
+ }
+ HTList_delete(theList);
+ return;
+}
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Utility for freeing the global list of MAPs. - kw
+ */
+static void LYLynxMaps_free(void)
+{
+ ImageMapList_free(LynxMaps);
+ LynxMaps = NULL;
+ return;
+}
+#endif /* LY_FIND_LEAKS */
+
+/*
+ * We keep two kinds of lists:
+ * - A global list (LynxMaps) shared by MAPs from all documents that
+ * do not have POST data.
+ * - For each response to a POST which contains MAPs, a list specific
+ * to this combination of URL and post_data. It is kept in the
+ * HTParentAnchor structure and is freed when the document is removed
+ * from memory, in the course of normal removal of anchors.
+ * MAPs from POST responses can only be accessed via internal links,
+ * i.e., from within the same document (with the same post_data).
+ * The notion of "same document" is extended, so that LYNXIMGMAP:
+ * and List Page screens are logically part of the document on which
+ * they are based. - kw
+ *
+ * If track_internal_links is false, only the global list will be used
+ * for all MAPs.
+ *
+ */
+
+/*
+ * Utility for creating an LYImageMap list, if it doesn't exist already, adding
+ * LYImageMap entry structures if needed, and removing any LYMapElements in a
+ * pre-existing LYImageMap entry so that it will have only those from AREA tags
+ * for the current analysis of MAP element content. - FM
+ */
+BOOL LYAddImageMap(char *address,
+ char *title,
+ HTParentAnchor *node_anchor)
+{
+ LYImageMap *tmp = NULL;
+ LYImageMap *old = NULL;
+ HTList *cur = NULL;
+ HTList *theList = NULL;
+ HTList *curele = NULL;
+ LYMapElement *ele = NULL;
+
+ if (isEmpty(address))
+ return FALSE;
+ if (!(node_anchor && node_anchor->address))
+ return FALSE;
+
+ /*
+ * Set theList to either the global LynxMaps list or, if we are associated
+ * with post data, the specific list. The list is created if it doesn't
+ * already exist. - kw
+ */
+ if (track_internal_links && node_anchor->post_data) {
+ /*
+ * We are handling a MAP element found while parsing node_anchor's
+ * stream of data, and node_anchor has post_data associated and should
+ * therefore represent a POST response, so use the specific list. - kw
+ */
+ theList = node_anchor->imaps;
+ if (!theList) {
+ theList = node_anchor->imaps = HTList_new();
+ }
+ } else {
+ if (!LynxMaps) {
+ LynxMaps = HTList_new();
+#ifdef LY_FIND_LEAKS
+ atexit(LYLynxMaps_free);
+#endif
+ }
+ theList = LynxMaps;
+ }
+
+ if (theList) {
+ cur = theList;
+ while (NULL != (old = (LYImageMap *) HTList_nextObject(cur))) {
+ if (old->address == 0) /* shouldn't happen */
+ continue;
+ if (!strcmp(old->address, address)) {
+ FREE(old->address);
+ FREE(old->title);
+ if (old->elements) {
+ curele = old->elements;
+ while (NULL !=
+ (ele = (LYMapElement *) HTList_nextObject(curele))) {
+ FREE(ele->address);
+ FREE(ele->title);
+ FREE(ele);
+ }
+ HTList_delete(old->elements);
+ old->elements = NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ tmp = (old != NULL) ?
+ old : typecalloc(LYImageMap);
+ if (tmp == NULL) {
+ outofmem(__FILE__, "LYAddImageMap");
+ return FALSE;
+ }
+ StrAllocCopy(tmp->address, address);
+ if (non_empty(title))
+ StrAllocCopy(tmp->title, title);
+ if (tmp != old)
+ HTList_addObject(theList, tmp);
+ return TRUE;
+}
+
+/*
+ * Utility for adding LYMapElement's to LYImageMap's
+ * in the appropriate list. - FM
+ */
+BOOL LYAddMapElement(char *map,
+ char *address,
+ char *title,
+ HTParentAnchor *node_anchor,
+ int intern_flag GCC_UNUSED)
+{
+ LYMapElement *tmp = NULL;
+ LYImageMap *theMap = NULL;
+ HTList *theList = NULL;
+ HTList *cur = NULL;
+
+ if (isEmpty(map) || isEmpty(address))
+ return FALSE;
+ if (!(node_anchor && node_anchor->address))
+ return FALSE;
+
+ /*
+ * Set theList to either the global LynxMaps list or, if we are associated
+ * with post data, the specific list. The list should already exist, since
+ * this function is only called if the AREA tag we are handling was within
+ * a MAP element in node_anchor's stream of data, so that LYAddImageMap has
+ * been called. - kw
+ */
+ if (track_internal_links && node_anchor->post_data) {
+ /*
+ * We are handling an AREA tag found while parsing node_anchor's stream
+ * of data, and node_anchor has post_data associated and should
+ * therefore represent a POST response, so use the specific list. - kw
+ */
+ theList = node_anchor->imaps;
+ if (!theList) {
+ return FALSE;
+ }
+ } else {
+ if (!LynxMaps)
+ LYAddImageMap(map, NULL, node_anchor);
+ theList = LynxMaps;
+ }
+
+ cur = theList;
+ while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
+ if (!strcmp(theMap->address, map)) {
+ break;
+ }
+ }
+ if (!theMap)
+ return FALSE;
+ if (!theMap->elements)
+ theMap->elements = HTList_new();
+ cur = theMap->elements;
+ while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) {
+ if (!strcmp(tmp->address, address)) {
+ FREE(tmp->address);
+ FREE(tmp->title);
+ HTList_removeObject(theMap->elements, tmp);
+ FREE(tmp);
+ break;
+ }
+ }
+
+ tmp = typecalloc(LYMapElement);
+ if (tmp == NULL) {
+ perror("Out of memory in LYAddMapElement");
+ return FALSE;
+ }
+ StrAllocCopy(tmp->address, address);
+ if (non_empty(title))
+ StrAllocCopy(tmp->title, title);
+ else
+ StrAllocCopy(tmp->title, address);
+ if (track_internal_links)
+ tmp->intern_flag = (BOOLEAN) intern_flag;
+ HTList_appendObject(theMap->elements, tmp);
+
+ CTRACE((tfp,
+ "LYAddMapElement\n\tmap %s\n\taddress %s\n\ttitle %s)\n",
+ NonNull(map), NonNull(address), NonNull(title)));
+
+ return TRUE;
+}
+
+/*
+ * Utility for checking whether an LYImageMap entry with a given address
+ * already exists in the LynxMaps structure. - FM
+ */
+BOOL LYHaveImageMap(char *address)
+{
+ LYImageMap *Map;
+ HTList *cur = LynxMaps;
+
+ if (!(cur && non_empty(address)))
+ return FALSE;
+
+ while (NULL != (Map = (LYImageMap *) HTList_nextObject(cur))) {
+ if (!strcmp(Map->address, address)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Fills in a DocAddress structure for getting the HTParentAnchor of the
+ * underlying resource. ALso returns a pointer to that anchor in
+ * *punderlying if we are dealing with POST data. - kw
+ *
+ * address is the address of the underlying resource, i.e., the one
+ * containing the MAP element, the MAP's name appended as
+ * fragment is ignored.
+ * anAnchor is the LYNXIMGMAP: anchor; if it is associated with POST
+ * data, we want the specific list, otherwise the global list.
+ */
+static void fill_DocAddress(DocAddress *wwwdoc,
+ const char *address,
+ HTParentAnchor *anAnchor,
+ HTParentAnchor **punderlying)
+{
+ char *doc_address = NULL;
+ HTParentAnchor *underlying;
+
+ StrAllocCopy(doc_address, address);
+ if (anAnchor && anAnchor->post_data) {
+ wwwdoc->address = doc_address;
+ wwwdoc->post_data = anAnchor->post_data;
+ wwwdoc->post_content_type = anAnchor->post_content_type;
+ wwwdoc->bookmark = NULL;
+ wwwdoc->isHEAD = FALSE;
+ wwwdoc->safe = FALSE;
+ underlying = HTAnchor_findAddress(wwwdoc);
+ if (underlying->safe)
+ wwwdoc->safe = TRUE;
+ if (punderlying)
+ *punderlying = underlying;
+ } else {
+ wwwdoc->address = doc_address;
+ wwwdoc->post_data = NULL;
+ wwwdoc->post_content_type = NULL;
+ wwwdoc->bookmark = NULL;
+ wwwdoc->isHEAD = FALSE;
+ wwwdoc->safe = FALSE;
+ if (punderlying)
+ *punderlying = NULL;
+ }
+}
+
+/*
+ * Get the appropriate list for creating a LYNXIMGMAP: pseudo- document:
+ * either the global list (LynxMaps), or the specific list if a List Page for a
+ * POST response is requested. Also fill in the DocAddress structure etc. by
+ * calling fill_DocAddress().
+ *
+ * address is the address of the underlying resource, i.e., the one
+ * containing the MAP element, the MAP's name appended as
+ * fragment is ignored.
+ * anchor is the LYNXIMGMAP: anchor for which LYLoadIMGmap() is
+ * requested; if it is associated with POST data, we want the
+ * specific list for this combination of address+post_data.
+ *
+ * if track_internal_links is false, the Anchor passed to
+ * LYLoadIMGmap() will never have post_data, so that the global list
+ * will be used. - kw
+ */
+static HTList *get_the_list(DocAddress *wwwdoc,
+ const char *address,
+ HTParentAnchor *anchor,
+ HTParentAnchor **punderlying)
+{
+ HTList *result;
+
+ if (anchor->post_data) {
+ fill_DocAddress(wwwdoc, address, anchor, punderlying);
+ if (non_empty(punderlying)) {
+ result = (*punderlying)->imaps;
+ } else {
+ result = anchor->imaps;
+ }
+ } else {
+ fill_DocAddress(wwwdoc, address, NULL, punderlying);
+ result = LynxMaps;
+ }
+ return result;
+}
+
+/* LYLoadIMGmap - F.Macrides (macrides@sci.wfeb.edu)
+ * ------------
+ * Create a text/html stream with a list of links
+ * for HyperText References in AREAs of a MAP.
+ */
+
+static int LYLoadIMGmap(const char *arg,
+ HTParentAnchor *anAnchor,
+ HTFormat format_out,
+ HTStream *sink)
+{
+ HTFormat format_in = WWW_HTML;
+ HTStream *target = NULL;
+ char *buf = NULL;
+ LYMapElement *tmp = NULL;
+ LYImageMap *theMap = NULL;
+ char *MapTitle = NULL;
+ char *MapAddress = NULL;
+ HTList *theList;
+ HTList *cur = NULL;
+ const char *address = NULL;
+ char *cp = NULL;
+ DocAddress WWWDoc;
+ HTParentAnchor *underlying;
+ BOOL old_cache_setting = LYforce_no_cache;
+ BOOL old_reloading = reloading;
+ HTFormat old_format_out = HTOutputFormat;
+
+ if (isLYNXIMGMAP(arg)) {
+ address = (arg + LEN_LYNXIMGMAP);
+ }
+ if (!(address && StrChr(address, ':'))) {
+ HTAlert(MISDIRECTED_MAP_REQUEST);
+ return (HT_NOT_LOADED);
+ }
+
+ theList = get_the_list(&WWWDoc, address, anAnchor, &underlying);
+ if (WWWDoc.safe)
+ anAnchor->safe = TRUE;
+
+ if (!theList) {
+ if (anAnchor->post_data && !WWWDoc.safe &&
+ ((underlying && underlying->document && !LYforce_no_cache) ||
+ HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) {
+ HTAlert(FAILED_MAP_POST_REQUEST);
+ return (HT_NOT_LOADED);
+ }
+ LYforce_no_cache = TRUE;
+ reloading = TRUE;
+ HTOutputFormat = WWW_PRESENT;
+ LYMapsOnly = TRUE;
+ if (!HTLoadAbsolute(&WWWDoc)) {
+ LYforce_no_cache = old_cache_setting;
+ reloading = old_reloading;
+ HTOutputFormat = old_format_out;
+ LYMapsOnly = FALSE;
+ HTAlert(MAP_NOT_ACCESSIBLE);
+ return (HT_NOT_LOADED);
+ }
+ LYforce_no_cache = old_cache_setting;
+ reloading = old_reloading;
+ HTOutputFormat = old_format_out;
+ LYMapsOnly = FALSE;
+ theList = get_the_list(&WWWDoc, address, anAnchor, &underlying);
+ }
+
+ if (!theList) {
+ HTAlert(MAPS_NOT_AVAILABLE);
+ return (HT_NOT_LOADED);
+ }
+
+ cur = theList;
+ while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
+ if (!strcmp(theMap->address, address)) {
+ break;
+ }
+ }
+ if (theMap && HTList_count(theMap->elements) == 0) {
+ /*
+ * We found a MAP without any usable AREA. Fake a redirection to the
+ * address with fragment. We do this even for post data (internal link
+ * within a document with post data) if it will not result in an
+ * unwanted network request. - kw
+ */
+ if (!anAnchor->post_data) {
+ StrAllocCopy(redirecting_url, address);
+ return (HT_REDIRECTING);
+ } else if (WWWDoc.safe ||
+ (underlying->document && !anAnchor->document &&
+ (LYinternal_flag || LYoverride_no_cache))) {
+ StrAllocCopy(redirecting_url, address);
+ redirect_post_content = TRUE;
+ return (HT_REDIRECTING);
+ }
+ }
+ if (!(theMap && theMap->elements)) {
+ if (anAnchor->post_data && !WWWDoc.safe &&
+ ((underlying && underlying->document && !LYforce_no_cache) ||
+ HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) {
+ HTAlert(FAILED_MAP_POST_REQUEST);
+ return (HT_NOT_LOADED);
+ }
+ LYforce_no_cache = TRUE;
+ reloading = TRUE;
+ HTOutputFormat = WWW_PRESENT;
+ LYMapsOnly = TRUE;
+ if (!HTLoadAbsolute(&WWWDoc)) {
+ LYforce_no_cache = old_cache_setting;
+ reloading = old_reloading;
+ HTOutputFormat = old_format_out;
+ LYMapsOnly = FALSE;
+ HTAlert(MAP_NOT_ACCESSIBLE);
+ return (HT_NOT_LOADED);
+ }
+ LYforce_no_cache = old_cache_setting;
+ reloading = old_reloading;
+ HTOutputFormat = old_format_out;
+ LYMapsOnly = FALSE;
+ cur = get_the_list(&WWWDoc, address, anAnchor, &underlying);
+ while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
+ if (!strcmp(theMap->address, address)) {
+ break;
+ }
+ }
+ if (!(theMap && theMap->elements)) {
+ HTAlert(MAP_NOT_AVAILABLE);
+ return (HT_NOT_LOADED);
+ }
+ }
+ if (track_internal_links)
+ anAnchor->no_cache = TRUE;
+
+ target = HTStreamStack(format_in,
+ format_out,
+ sink, anAnchor);
+
+ if (target == NULL) {
+ HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
+ HTAtom_name(format_in), HTAtom_name(format_out));
+ HTAlert(buf);
+ FREE(buf);
+ return (HT_NOT_LOADED);
+ }
+
+ if (non_empty(theMap->title)) {
+ StrAllocCopy(MapTitle, theMap->title);
+ } else if (non_empty(anAnchor->title)) {
+ StrAllocCopy(MapTitle, anAnchor->title);
+ } else if (non_empty(LYRequestTitle) &&
+ strcasecomp(LYRequestTitle, NO_MAP_TITLE)) {
+ StrAllocCopy(MapTitle, LYRequestTitle);
+ } else if ((cp = StrChr(address, '#')) != NULL) {
+ StrAllocCopy(MapTitle, (cp + 1));
+ }
+ if (isEmpty(MapTitle)) {
+ StrAllocCopy(MapTitle, NO_MAP_TITLE);
+ } else {
+ LYEntify(&MapTitle, TRUE);
+ }
+
+#define PUTS(buf) (*target->isa->put_block)(target, buf, (int) strlen(buf))
+
+ HTSprintf0(&buf, "<html>\n<head>\n");
+ PUTS(buf);
+ HTSprintf0(&buf, "<META %s content=\"" STR_HTML ";charset=%s\">\n",
+ "http-equiv=\"content-type\"",
+ LYCharSet_UC[current_char_set].MIMEname);
+ PUTS(buf);
+ /*
+ * This page is a list of titles and anchors for them. Since titles
+ * already passed SGML/HTML stage they are converted to current_char_set.
+ * That is why we insist on META charset for this page.
+ */
+ HTSprintf0(&buf, "<title>%s</title>\n", MapTitle);
+ PUTS(buf);
+ HTSprintf0(&buf, "</head>\n<body>\n");
+ PUTS(buf);
+
+ HTSprintf0(&buf, "<h1><em>%s</em></h1>\n", MapTitle);
+ PUTS(buf);
+
+ StrAllocCopy(MapAddress, address);
+ LYEntify(&MapAddress, FALSE);
+ HTSprintf0(&buf, "<h2><em>MAP:</em>&nbsp;%s</h2>\n", MapAddress);
+ PUTS(buf);
+
+ HTSprintf0(&buf, "<%s compact>\n", ((keypad_mode == NUMBERS_AS_ARROWS) ?
+ "ol" : "ul"));
+ PUTS(buf);
+ cur = theMap->elements;
+ while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) {
+ StrAllocCopy(MapAddress, tmp->address);
+ LYEntify(&MapAddress, FALSE);
+ PUTS("<li><a href=\"");
+ PUTS(MapAddress);
+ PUTS("\"");
+ if (track_internal_links && tmp->intern_flag) {
+ PUTS(" TYPE=\"internal link\"");
+ }
+ PUTS("\n>");
+ LYformTitle(&MapTitle, tmp->title);
+ LYEntify(&MapTitle, TRUE);
+ PUTS(MapTitle);
+ PUTS("</a>\n");
+ }
+ HTSprintf0(&buf, "</%s>\n</body>\n</html>\n",
+ ((keypad_mode == NUMBERS_AS_ARROWS)
+ ? "ol"
+ : "ul"));
+ PUTS(buf);
+
+ (*target->isa->_free) (target);
+ FREE(MapAddress);
+ FREE(MapTitle);
+ FREE(buf);
+ return (HT_LOADED);
+}
+
+void LYPrintImgMaps(FILE *fp)
+{
+ const char *only = HTLoadedDocumentURL();
+ size_t only_len = strlen(only);
+ HTList *outer = LynxMaps;
+ HTList *inner;
+ LYImageMap *map;
+ LYMapElement *elt;
+ int count;
+
+ if (HTList_count(outer) > 0) {
+ while (NULL != (map = (LYImageMap *) HTList_nextObject(outer))) {
+ if (only_len != 0) {
+ if (StrNCmp(only, map->address, only_len)
+ || (map->address[only_len] != '\0'
+ && map->address[only_len] != '#')) {
+ continue;
+ }
+ }
+ fprintf(fp, "\n%s\n", isEmpty(map->title) ? NO_MAP_TITLE : map->title);
+ fprintf(fp, "%s\n", map->address);
+ inner = map->elements;
+ count = 0;
+ while (NULL != (elt = (LYMapElement *) HTList_nextObject(inner))) {
+ fprintf(fp, "%4d. %s", ++count, elt->address);
+ if (track_internal_links && elt->intern_flag)
+ fprintf(fp, " TYPE=\"internal link\"");
+ fprintf(fp, "\n");
+ }
+ }
+ }
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _LYIMGMAP_C_GLOBALDEF_1_INIT { "LYNXIMGMAP", LYLoadIMGmap, 0}
+GLOBALDEF(HTProtocol, LYLynxIMGmap, _LYIMGMAP_C_GLOBALDEF_1_INIT);
+#else
+GLOBALDEF HTProtocol LYLynxIMGmap =
+{"LYNXIMGMAP", LYLoadIMGmap, 0};
+#endif /* GLOBALDEF_IS_MACRO */