home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Internet Business Development Kit / PRODUCT_CD.iso / ftpage / disk6 / imagemap.c_ / imagemap.c
Encoding:
C/C++ Source or Header  |  1995-09-22  |  14.9 KB  |  505 lines

  1. // tabs = 4
  2. //------------------------------------------------------------------------
  3. // TITLE:        IMAGEMAP.C
  4. //
  5. // FACILITY:    Image Mapper
  6. //
  7. // ABSTRACT:    This program is intended to serve as a "back end" to a
  8. //                world-wide web server. It takes the name of a "map" and
  9. //                a set of coordinates, and returns a URL (document address)
  10. //                specific to the coordinates and the information in the map.
  11. //
  12. //                The maps themselves are in separate files. The maps are
  13. //                listed in a configuration file called "imagemap.cnf" 
  14. //                that is located on the path given by the environment 
  15. //                variable HTTPD_CONFDIR, defaulting to "c:/httpd/conf".
  16. //                Each entry in this file must be for the form:
  17. //
  18. //                <mapname> : <mapfile pathname>
  19. //
  20. //                Note that this provides a logical to physical mapping facility
  21. //                for the mapfiles. The mapfiles themselves list regions in the 
  22. //                target bitmap as follows:
  23. //
  24. //                <rtype> <URL> <coords>
  25. //
  26. //                where <rtype> is "rect", "poly", "ellipse" or "circle",
  27. //                <URL> is the URL of the document to return if the testpoint
  28. //                is within that region, and <coords> are the defining x-y
  29. //                coordinates    for that type of region.
  30. //
  31. //                NOTE: The <rtype> can also be "default", in which case the URL 
  32. //                is the one to send if the test point is not in any of the 
  33. //                regions. In this case, no coordinates are needed.
  34. //
  35. //
  36. // ENVIRONMENT:    Microsoft Windows 3.1/3.11 (16-bit)
  37. //                Developed under Borland C++ 4.0
  38. //
  39. // AUTHOR:        Bob Denny <rdenny@netcom.com>
  40. //
  41. // Edit Log:
  42. //
  43. // When            Who        What
  44. //----------    ---        --------------------------------------------------
  45. // 21-Nov-94    rbd        Adapted from Kevin Hughes & Casey Barton version,
  46. //                        and the version I made that used doubles.
  47. //                        Convert to use ints and the Windows built-in 
  48. //                        region handling functions.
  49. // 17-Dec-94    rbd        Normalize rects before doing the test so that
  50. //                        corners may be given in any order.
  51. // 07-Feb-95    rbd        Get ofile before first opportunity to call error
  52. //                        routine, so can report error properly. Get as much
  53. //                        as possible from INI file not the command line,
  54. //                        as the command line can get smashed by the OS
  55. //                        or the CRT.  Header lines have CRLF termination.
  56. //                        Add <HTML>, <HEAD>, and <BODY> to error message.
  57. //------------------------------------------------------------------------
  58.  
  59. #include <stdio.h>
  60. #include <string.h>
  61. #include <stdlib.h>
  62. #include <ctype.h>
  63. #include <windows.h>
  64. #include "util.h"
  65.  
  66. #define DEFAULT_CONF_DIR "c:\\vermeer\\httpd\\conf"
  67. #define CONF_FILE_NAME "imagemap.cnf"
  68. #define PROGRAM_VERSION "V2.1 (09-Feb-95)"
  69.  
  70. #define MAXLINE 500
  71. #define MAXVERTS 100
  72. #define X 0
  73. #define Y 1
  74. #define END_SIGNAL 0xFFFFFFFF
  75.  
  76. static char *bad_tgt_msg = "Your client probably doesn't support image maps.";
  77. static char ofile[256];
  78. static BOOL fDebug = FALSE;
  79.  
  80. void sendmesg(char *url);
  81. void servererr(char *msg);
  82. void debug_wait(void);
  83. BOOL pointinrect(int point[2], int coords[MAXVERTS][2]);
  84. BOOL pointincircle(int point[2], int coords[MAXVERTS][2]);
  85. BOOL pointinpoly(int point[2], int pgon[MAXVERTS][2], int nvert);
  86. BOOL pointinellipse(int point[2], int coords[MAXVERTS][2]);
  87. static void NormalizeRect(RECT *rp);
  88.  
  89. //========================================================================
  90. // From httpd (windows CGI 1.1):
  91. //
  92. //        argv[1]        CGI .INI file pathname
  93. //        argv[2]        Input file (does not exist for Imagemap)
  94. //        argv[3]        Output file
  95. //        argv[4]        Coordinates
  96. //
  97. //========================================================================
  98. #pragma argsused
  99. int main(int argc, char *argv[])
  100. {
  101.     char input[MAXLINE], mapname[MAXLINE], def[MAXLINE], conf[256];
  102.     int testpoint[2], pointarray[MAXVERTS][2];
  103.     int i, j, k;
  104.     FILE *fp;
  105.     char *t, *cp;
  106.     MSG msg;
  107.  
  108.     //
  109.     // Yield to the system for a bit so the server has a chance
  110.     // to synchronize with our exit...
  111.     //
  112.     for(i=0; i<10; i++)
  113.         PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  114.     
  115.     //
  116.     // Locate the configuration file via this environment variable.
  117.     //
  118.     if((t = getenv("HTTPD_CONFDIR")) != NULL)
  119.         strcpy(conf, t);
  120.     else
  121.         strcpy(conf, DEFAULT_CONF_DIR);
  122.  
  123.     i = strlen(conf) - 1;
  124.     if((conf[i] != '/') && (conf[i] != '\\'))
  125.         strcat(conf, "\\");                        // Assure trailing slash
  126.     strcat(conf, CONF_FILE_NAME);                // Full path to our config file
  127.  
  128.     //
  129.     // Run according to fDebugging mode
  130.     //
  131.     GetPrivateProfileString("System", "Debug Mode", "No", 
  132.                 input, MAXLINE, argv[1]);
  133.     if(tolower(input[0]) == 'y')
  134.     {
  135.         printf("Windows httpd image mapper %s\n", PROGRAM_VERSION);
  136.         printf("Debugging mode set by server.\n");
  137.         printf("Command Line: argc = %d\n", argc);
  138.         for(i = 0; i < argc; i++)
  139.             printf("  argv[%d] = \"%s\"\n", i, argv[i]);
  140.         fDebug = TRUE;
  141.     }
  142.  
  143.     //
  144.     // If web server wants back-end debugging, install an atexit() handler
  145.     // that holds the console open until a key is pressed
  146.     //
  147.     if(fDebug)
  148.         atexit(debug_wait);
  149.     
  150.      //
  151.     // argv[3] (and the "Output File") contains the name of the file
  152.     // into which we put the result (a "Location:" document or an
  153.     // error message). Use the "Output File" variable because the
  154.     // command line can be smashed by the OS or CRT.
  155.     //
  156.     GetPrivateProfileString("System", "Output File", "",
  157.                 input, MAXLINE, argv[1]);
  158.     strcpy(ofile, input);
  159.  
  160.     //
  161.     // The "Logical Path"  (URL extension) contains the map name.
  162.     //    
  163.     GetPrivateProfileString("CGI", "Logical Path", "", 
  164.                 input, MAXLINE, argv[1]);
  165.     strcpy(mapname, &input[1]);            // Skip leading slash
  166.     if(mapname[0] == '\0')                // No map name?
  167.         servererr(bad_tgt_msg);            // Client doesn't support imagemapping
  168.  
  169.  
  170.     //
  171.     // Get target coordinates. The only requirement for syntax is
  172.     // that there be two numeric strings acceptable to strtol()
  173.     // and that they be separated by ONE CHARACTER that is not.
  174.     // Browsers SHOULD send "x,y". Use base=0 so strtol() can 
  175.     // deal with decimal, octal and hex values.
  176.     //
  177.     GetPrivateProfileString("CGI", "Query String", "",
  178.                 input, MAXLINE, argv[1]);
  179.     cp = input;
  180.     if(cp == NULL)                        // Missing arg = access violation
  181.         servererr(bad_tgt_msg);            // Bogus URL, no doubt
  182.     testpoint[X] = strtol(cp, &t, 0);    // Attempt to convert X
  183.     if(t == cp)
  184.         servererr(bad_tgt_msg);
  185.     
  186.     cp = t + 1;
  187.     testpoint[Y] = strtol(cp, &t, 0);    // Attempt to convert y
  188.     if(t == cp)
  189.         servererr(bad_tgt_msg);
  190.  
  191.     if(fDebug)
  192.         printf("Map = %s\nOutput = %s\nCoord = [%d,%d]\n",
  193.             mapname, ofile, testpoint[X], testpoint[Y]);
  194.  
  195.     //
  196.     // Open the config file and find the line that matches the map
  197.     // name given in the URL extension.
  198.     // 
  199.     if ((fp = fopen(conf,"r")) == NULL)
  200.         servererr("Couldn't open imagemap config. file.");
  201.  
  202.     while(!(getline(input, MAXLINE, fp))) {
  203.         char buf[MAXLINE];
  204.         
  205.         if((input[0] == '#') || (!input[0]))        // # lines are comments
  206.             continue;
  207.         
  208.         for(i=0; !isspace(input[i]) && (input[i] != ':'); i++)
  209.             buf[i] = input[i];
  210.         
  211.         buf[i] = '\0';
  212.         
  213.         if(!strcmp(buf, mapname))                    // Preserve mapname case
  214.             break;
  215.     }
  216.     
  217.     if(feof(fp)) {
  218.         char buf[256];
  219.  
  220.         sprintf(buf, "Map \"%s\" not found in configuration file.", mapname);
  221.         fclose(fp);
  222.         servererr(buf);
  223.     }
  224.     fclose(fp);
  225.  
  226.     while(isspace(input[i]) || input[i] == ':') ++i;    // Skip past ":" on index line
  227.  
  228.     //
  229.     // Now input[i] -> physical pathname for the mapfile. Collect it and 
  230.     // open the mapfile.
  231.     // 
  232.     for(j=0;input[i] && !isspace(input[i]);++i,++j)
  233.         conf[j] = input[i];
  234.     conf[j] = '\0';
  235.  
  236.     if((fp=fopen(conf,"r")) == NULL)
  237.         servererr("Couldn't open map file.");
  238.  
  239.     //
  240.     // Here's where we read in the regions, URLs and defining coordinates
  241.     // and for each region, perform the hit test for that region type. 
  242.     //
  243.     while(!(getline(input,MAXLINE,fp))) {
  244.         char type[MAXLINE];
  245.         char url[MAXLINE];
  246.  
  247.         if((input[0] == '#') || (!input[0]))        // Skip comment lines
  248.             continue;
  249.  
  250.         type[0] = '\0';url[0] = '\0';
  251.  
  252.         for(i=0; !isspace(input[i]) && (input[i]); i++)    // Get the type
  253.             type[i] = input[i];
  254.         type[i] = '\0';
  255.  
  256.         while(isspace(input[i])) ++i;
  257.  
  258.         for(j=0; input[i] && !isspace(input[i]); ++i, ++j)// Get the URL
  259.             url[j] = input[i];
  260.         url[j] = '\0';
  261.  
  262.         if(!stricmp(type, "default")) {
  263.             strcpy(def, url);
  264.             continue;
  265.         }
  266.  
  267.         //
  268.         // (rbd) Use the features of strtol() to scan off coordinate pairs
  269.         //
  270.         k = 0;                        // Indexes coordinate pairs
  271.         cp = &input[i];                // Switch to pointer
  272.         while (*cp != '\0')
  273.         {
  274.             pointarray[k][X] = (int)strtol(cp, &t, 0);
  275.             if(t == cp)                // No number converted
  276.             {
  277.                 if(*cp != '\0')        // If not at end yet
  278.                     cp += 1;        // Skip past this stopper
  279.                 continue;            // Try again, stop if end of string
  280.             }
  281.             cp = t + 1;                // Skip past stopper (should be ",")
  282.             pointarray[k][Y] = (int)strtol(cp, &t, 0);
  283.             if(t == cp)                // If no Y, this is bad.
  284.             {
  285.                 char buf[256];
  286.  
  287.                 fclose(fp);
  288.                 sprintf(buf,
  289.                     "Missing Y value in map file %s<P>offending line: %s<P>",
  290.                     conf, input);
  291.                 servererr(buf);
  292.             }
  293.             if(*t != '\0')
  294.                 cp = t + 1;
  295.             else
  296.                 cp = t;
  297.             k += 1;
  298.         }
  299.  
  300.         //
  301.         // If sendmesg() is called, it never returns. It exit()s.
  302.         //
  303.         pointarray[k][X] = END_SIGNAL;        // Add signal value
  304.  
  305.         if(!strcmpi(type,"poly"))
  306.             if(pointinpoly(testpoint, pointarray, k))
  307.                 sendmesg(url);
  308.  
  309.         if(!strcmpi(type,"circle"))
  310.             if(pointincircle(testpoint, pointarray))
  311.                 sendmesg(url);
  312.  
  313.         if(!strcmpi(type,"ellipse"))
  314.             if(pointinellipse(testpoint, pointarray))
  315.                 sendmesg(url);
  316.  
  317.         if(!strcmpi(type,"rect"))
  318.             if(pointinrect(testpoint, pointarray))
  319.                 sendmesg(url);
  320.     }
  321.  
  322.     //
  323.     // If we get here, the testpoint was not in any of the regions.
  324.     // Send the default, unless we didn't get one, in which case, 
  325.     // send an error message.
  326.     //
  327.     if(def[0])
  328.         sendmesg(def);
  329.     servererr("No default specified.");
  330.  
  331.     //NOTREACHED
  332.     return(0);            // Shut compiler up
  333. }
  334.  
  335. //=============================================================================
  336. //
  337. //    sendmesg() - Return the URL for the selected region.
  338. //
  339. //=============================================================================
  340. void sendmesg(char *url)  // Output destination URLs directly to OUTPUT_FILE
  341. {
  342.     FILE *outfile;
  343.  
  344.     if(fDebug)
  345.         printf("Resolved to:\n %s\n", url);
  346.  
  347.     if ((outfile = fopen(ofile,"w")) == NULL)
  348.     {
  349.         printf("Couldn't open output file. Check your TEMP env. var.\n");
  350.         exit(-1);
  351.     }
  352.     fprintf(outfile,"Location: %s\015\012",url);
  353.     fprintf(outfile,"URI: <%s>\015\012\015\012",url);
  354.     fprintf(outfile,
  355.         "This document has moved <A HREF=\"%s\">here</A>\012", url);
  356.     fclose(outfile);
  357.     exit(0);
  358. }
  359.  
  360.  
  361. //=============================================================================
  362. //
  363. //    servererr() - Return an HTTP error message.
  364. //
  365. //=============================================================================
  366. void servererr(char *msg) // Output server errors directly to OUTPUT_FILE
  367. {
  368.     FILE *outfile;
  369.  
  370.     if(fDebug)
  371.         printf("An error occurred:\n %s\n", msg);
  372.  
  373.     if ((outfile = fopen(ofile,"w")) == NULL)
  374.     {
  375.         printf("Couldn't open output file. Check your TEMP env. var.\n");
  376.         exit(-1);
  377.     }
  378.     fprintf(outfile,"Content-type: text/html\012\015\012\015");
  379.     fprintf(outfile,"<html><head><title>Mapping Server Error</title></head>");
  380.     fprintf(outfile,"<body><h1>Mapping Server Error</h1><HR>");
  381.     fprintf(outfile,"This server encountered an error:<p>");
  382.     fprintf(outfile,"<code>%s</code></body></html>", msg);
  383.     fclose(outfile);
  384.     exit(-1);
  385. }
  386.  
  387.  
  388. //=============================================================================
  389. //
  390. //    sendmesg() - Return the URL for the selected region.
  391. //
  392. //=============================================================================
  393. void debug_wait(void)
  394. {
  395.     char buf[32];
  396.  
  397.     printf("\nPress [enter] to exit...");
  398.     fflush(stdout);
  399.     gets(buf);
  400. }
  401.  
  402. //=============================================================================
  403. //
  404. //    pointinrect() - Return TRUE if point is in rectangle
  405. //
  406. //    Rectangle is defined as top,left bottom,right
  407. //
  408. //=============================================================================
  409. BOOL pointinrect(int point[2], int coords[MAXVERTS][2])
  410. {
  411.     RECT r;
  412.     POINT p;
  413.  
  414.     p.x = point[0];
  415.     p.y = point[1];
  416.  
  417.     SetRect(&r, coords[0][X], coords[0][Y], coords[1][X], coords[1][Y]);
  418.     NormalizeRect(&r);
  419.     return(PtInRect(&r, p));
  420. }
  421.  
  422. //=============================================================================
  423. //
  424. //    pointincircle() - Return TRUE if point is in circle
  425. //
  426. //    For compatibility with old-style maps. Circle is defined as centerpoint,
  427. //    and any point on circumference. For new maps, use ellipse (below).
  428. //
  429. //=============================================================================
  430.  
  431. BOOL pointincircle(int point[2], int coords[MAXVERTS][2])
  432. {
  433.     unsigned int radius1, radius2; 
  434.  
  435.     radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
  436.       + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
  437.  
  438.     radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
  439.       + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
  440.  
  441.     return (radius2 <= radius1);
  442. }
  443.  
  444.  
  445. //=============================================================================
  446. //
  447. //    pointinellipse() - Return TRUE if point is in ellipse
  448. //
  449. //  Ellipse is given by the bounding rectangle top,left bottom,right.
  450. //
  451. //=============================================================================
  452. BOOL pointinellipse(int point[2], int coords[MAXVERTS][2])
  453. {
  454.     RECT r;
  455.     HRGN e;
  456.     BOOL f;
  457.  
  458.     SetRect(&r, coords[0][X], coords[0][Y], coords[1][X], coords[1][Y]);
  459.     NormalizeRect(&r);
  460.     e = CreateEllipticRgn(r.left, r.top, r.right, r.bottom);
  461.     f = PtInRegion(e, point[0], point[1]);
  462.     DeleteObject(e);
  463.     return(f);
  464. }
  465.  
  466.  
  467. //=============================================================================
  468. //
  469. //    pointinpoly() - Return TRUE if point is in polygon
  470. //
  471. //  Polygon is given by a series of vertices (x,y). WARNING: Complex
  472. //    overlapping polygons may not act like you think. See the docs on 
  473. //    SetPolyFillMode() for more info. This function is intended to be
  474. //    used on non-overlapping polygons, and will work fine for them.
  475. //
  476. //=============================================================================
  477. int pointinpoly(int point[2], int pgon[MAXVERTS][2], int nvert)
  478. {
  479.     HRGN p = CreatePolygonRgn((POINT FAR *)pgon, nvert, ALTERNATE);
  480.     BOOL f = PtInRegion(p, point[0], point[1]);
  481.     DeleteObject(p);
  482.     return(f);
  483. }
  484.  
  485.  
  486. //=============================================================================
  487. //
  488. //    NormalizeRect() - Assure topleft is really left and above
  489. //
  490. //=============================================================================
  491. static void NormalizeRect(RECT *rp)
  492. {
  493.     int i, j;
  494.  
  495.     if(rp->left > rp->right)
  496.     {
  497.         i = rp->left;
  498.         j = rp->top;
  499.         rp->left = rp->right;
  500.         rp->top = rp->bottom;
  501.         rp->right = i;
  502.         rp->bottom = j;
  503.     }
  504. }
  505.