home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 July / PCWorld_2000-07_cd.bin / Komunik / sambar / _SETUP.1 / search.c < prev    next >
C/C++ Source or Header  |  1998-03-09  |  12KB  |  521 lines

  1. /*
  2. ** SEARCH
  3. **
  4. **      This is a simple ISAPI search DLL that takes a set of words
  5. **           to search for in the "query" parameter, and displays all files
  6. **        that match the keywords.
  7. **
  8. **        Important!  The search occurs from the location of the search.dll
  9. **        and below.  The search.dll will only work properly if placed in
  10. **        the root of your documents directory.
  11. **
  12. **        Confidential Property of Tod Sambar
  13. **        (c) Copyright Tod Sambar 1998
  14. **        All rights reserved.
  15. **
  16. **
  17. ** History:
  18. ** Chg#    Date    Description                                                Resp
  19. ** ----    -------    -------------------------------------------------------    ----
  20. **        20MAR98    Created                                                    sambar
  21. */
  22.  
  23. #include <windows.h>
  24. #include <httpext.h>
  25. #include <string.h>
  26. #include <stdio.h>
  27. #include <stdarg.h>
  28.  
  29. /*
  30. ** Globals
  31. */
  32. #define PAGE_END        "<P><B>Done.</B>\n</BODY></HTML>\n"
  33. #define INVALID_FORMAT     "<B>Invalid search syntax.</B><P><I>search?query=foo&logic=AND</I>"
  34. #define NO_RESULTS         "<P><B>No results matching search request.</B>"
  35.  
  36. /*
  37. ** Local Prototypes
  38. */
  39. static DWORD          search_error(EXTENSION_CONTROL_BLOCK *pECB, CHAR *errorstr);
  40. static void          search_find(EXTENSION_CONTROL_BLOCK *pECB, 
  41.                         CHAR *docdir, CHAR *urldir, 
  42.                         CHAR kwords[10][256], int numkwords, int any, 
  43.                         int *nump);
  44. static short          search_match(CHAR *filename, CHAR kwords[10][256], 
  45.                         int numkwords, int any);
  46. static void         escape_to_ascii(CHAR *buf, int buflen);
  47. static short         get_param(CHAR *params, CHAR *arg, CHAR *buf, int buflen);
  48. static short         get_next(CHAR *head, CHAR *buffer, CHAR **tailp);
  49.  
  50.  
  51. /*
  52. **    GetExtensionVersion
  53. **
  54. **    ISAPI/Win32 API method to ensure compatibility with the Server.
  55. */
  56. BOOL WINAPI
  57. GetExtensionVersion(HSE_VERSION_INFO *pVer)
  58. {
  59.     pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
  60.  
  61.     lstrcpyn(pVer->lpszExtensionDesc, "Sambar Server ISAPI Search extension.",
  62.             HSE_MAX_EXT_DLL_NAME_LEN);
  63.  
  64.     return TRUE;
  65.  
  66. /*
  67. **    HttpExtensionProc
  68. **
  69. **     Called in response to the client request.
  70. **
  71. **  Format:
  72. **        search?query="<word1> <word2>... <wordN>"&logic="AND">
  73. **  or
  74. **        search?query="<word1> <word2>... <wordN>"&logic="OR">
  75. */
  76. DWORD WINAPI
  77. HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
  78. {
  79.     int        any;
  80.     int        num;
  81.     int        numkwords;
  82.     int        found;
  83.     DWORD    buflen;
  84.     CHAR    *head;
  85.     CHAR    *params;
  86.     CHAR    tmp[256];
  87.     CHAR    kwords[10][256];
  88.     CHAR    buffer[2048];
  89.     CHAR    urldir[512];
  90.  
  91.     /* Get the GET/POST data                                            */
  92.     params = NULL;
  93.     if (!stricmp(pECB->lpszMethod, "GET")) 
  94.     {
  95.         /* Set the parameters list to the QUERY_STRING data                */
  96.         params = pECB->lpszQueryString;
  97.     }
  98.     else     /* POST */
  99.     {
  100.         /* Note: cbTotalBytes = cbAvailable    in the Sambar Server        */ 
  101.         if(pECB->cbTotalBytes > 0) 
  102.             params = pECB->lpbData;
  103.     }
  104.  
  105.     if (params == NULL)
  106.         return (search_error(pECB, INVALID_FORMAT));
  107.  
  108.     /* Get the "query" and "logic" parameters                            */
  109.     if (!get_param(params, "logic", tmp, 255))
  110.         return (search_error(pECB, INVALID_FORMAT));
  111.  
  112.     if (stricmp(tmp, "and"))
  113.         any = 1;
  114.     else
  115.         any = 0;
  116.  
  117.     if (!get_param(params, "query", tmp, 255))
  118.         return (search_error(pECB, INVALID_FORMAT));
  119.  
  120.     /* Parse the query into a list.                                        */
  121.     head = tmp;
  122.     numkwords = 0;
  123.     while ((numkwords < 10) && get_next(head, kwords[numkwords], &head))
  124.     {
  125.         _strupr(kwords[numkwords]);
  126.         numkwords++;
  127.     }
  128.  
  129.     wsprintf(buffer,
  130.              "Content-Type: text/html\r\n"
  131.              "\r\n"
  132.              "<head><title>Sambar ISAPI Search</title></head>\n"
  133.              "<body><h1>Sambar ISAPI Search</h1>\n"
  134.              "<hr><P>Keywords: %s</P>\n", tmp);
  135.  
  136.     buflen = lstrlen(buffer);
  137.  
  138.     if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
  139.         "200 OK", &buflen, (LPDWORD)buffer)) 
  140.     {
  141.         pECB->dwHttpStatusCode = 500;
  142.         return HSE_STATUS_ERROR;
  143.     }
  144.  
  145.     /*
  146.     ** Search for the keywords.
  147.     */
  148.     num = 0;
  149.  
  150.     /*
  151.     ** Get the document root to search from.
  152.     */
  153.     GetModuleFileName(GetModuleHandle("search.dll"), buffer, 1024);
  154.     buflen = strlen(buffer) - strlen("\\search.dll");
  155.     buffer[buflen] = '\0';
  156.  
  157.     /*
  158.     ** Get the URL path
  159.     */
  160.     buflen = 512;
  161.     if (!pECB->GetServerVariable(pECB->ConnID, "URL", urldir, &buflen))
  162.     {
  163.         buflen = lstrlen(NO_RESULTS);
  164.         pECB->WriteClient(pECB->ConnID, NO_RESULTS, &buflen, 0);
  165.         pECB->dwHttpStatusCode = 200;
  166.         return HSE_STATUS_SUCCESS;
  167.     }
  168.  
  169.     /* Strip off to the last "directory" symbol.                        */
  170.     found = 0;
  171.     buflen = strlen(urldir);
  172.     while ((!found) && (buflen > 0))
  173.     {
  174.         if ((urldir[buflen - 1] == '/') || (urldir[buflen - 1] == '/'))
  175.             found = 1;
  176.  
  177.         buflen--;
  178.     }
  179.  
  180.     urldir[buflen] = '\0';
  181.  
  182.     search_find(pECB, buffer, urldir, kwords, numkwords, any, &num);
  183.  
  184.     if (num == 0)
  185.     {
  186.         buflen = lstrlen(NO_RESULTS);
  187.         pECB->WriteClient(pECB->ConnID, NO_RESULTS, &buflen, 0);
  188.     }
  189.  
  190.     buflen = lstrlen(PAGE_END);
  191.     pECB->WriteClient(pECB->ConnID, PAGE_END, &buflen, 0);
  192.  
  193.     pECB->dwHttpStatusCode = 200;
  194.  
  195.     return HSE_STATUS_SUCCESS;
  196. }
  197.  
  198. /*
  199. ** search_find
  200. **
  201. **        Recursively search from the directory in which the search.dll
  202. **        is located and below returning files that match the query
  203. **        criteria.
  204. */
  205. static void
  206. search_find(EXTENSION_CONTROL_BLOCK *pECB, CHAR *docdir, CHAR *urldir, 
  207.              CHAR kwords[10][256], int numkwords, int any, int *nump)
  208. {
  209.     DWORD            buflen;
  210.     HANDLE            hFile;
  211.     WIN32_FIND_DATA    findData;
  212.     CHAR            buffer[2048];
  213.     CHAR            newurl[2048];
  214.  
  215.     /*
  216.     ** Loop through all the files from the root directory.                
  217.     */
  218.     sprintf(buffer, "%s\\*.htm", docdir);
  219.     hFile = FindFirstFile(buffer, &findData);
  220.     while (hFile != INVALID_HANDLE_VALUE)
  221.     {
  222.         /* Search file handle...                                        */
  223.         sprintf(buffer, "%s\\%s", docdir, findData.cFileName);
  224.         if (search_match(buffer, kwords, numkwords, any))
  225.         {
  226.             *nump = *nump + 1;
  227.             sprintf(buffer, "<A HREF=\"%s/%s\">%s/%s</A><BR>\n", 
  228.                 urldir, findData.cFileName, urldir, findData.cFileName);
  229.             buflen = lstrlen(buffer);
  230.             pECB->WriteClient(pECB->ConnID, buffer, &buflen, 0);
  231.         }
  232.  
  233.         if (!FindNextFile(hFile, &findData))
  234.             break;
  235.     }
  236.  
  237.     FindClose(hFile);
  238.     
  239.     sprintf(buffer, "%s\\*.html", docdir);
  240.     hFile = FindFirstFile(buffer, &findData);
  241.     while (hFile != INVALID_HANDLE_VALUE)
  242.     {
  243.         /* Search file handle...                                        */
  244.         sprintf(buffer, "%s\\%s", docdir, findData.cFileName);
  245.         if (search_match(buffer, kwords, numkwords, any))
  246.         {
  247.             *nump = *nump + 1;
  248.             sprintf(buffer, "<A HREF=\"%s/%s\">%s/%s</A><BR>\n", 
  249.                 urldir, findData.cFileName, urldir, findData.cFileName);
  250.             buflen = lstrlen(buffer);
  251.             pECB->WriteClient(pECB->ConnID, buffer, &buflen, 0);
  252.         }
  253.  
  254.         if (!FindNextFile(hFile, &findData))
  255.             break;
  256.     }
  257.  
  258.     FindClose(hFile);
  259.     
  260.     sprintf(buffer, "%s\\*.txt", docdir);
  261.     hFile = FindFirstFile(buffer, &findData);
  262.     while (hFile != INVALID_HANDLE_VALUE)
  263.     {
  264.         /* Search file handle...                                        */
  265.         sprintf(buffer, "%s\\%s", docdir, findData.cFileName);
  266.         if (search_match(buffer, kwords, numkwords, any))
  267.         {
  268.             *nump = *nump + 1;
  269.             sprintf(buffer, "<A HREF=\"%s/%s\">%s/%s</A><BR>\n", 
  270.                 urldir, findData.cFileName, urldir, findData.cFileName);
  271.             buflen = lstrlen(buffer);
  272.             pECB->WriteClient(pECB->ConnID, buffer, &buflen, 0);
  273.         }
  274.  
  275.         if (!FindNextFile(hFile, &findData))
  276.             break;
  277.     }
  278.  
  279.     FindClose(hFile);
  280.     
  281.     /*
  282.     ** Recurse the directory(s)
  283.     */
  284.     sprintf(buffer, "%s\\*.*", docdir);
  285.     hFile = FindFirstFile(buffer, &findData);
  286.     while (hFile != INVALID_HANDLE_VALUE)
  287.     {
  288.         if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  289.             (strcmp(findData.cFileName, ".") != 0) && 
  290.             (strcmp(findData.cFileName, "..") != 0))
  291.         {
  292.             sprintf(buffer, "%s\\%s", docdir, findData.cFileName);
  293.             sprintf(newurl, "%s/%s", urldir, findData.cFileName);
  294.             search_find(pECB, buffer, newurl, kwords, numkwords, any, nump);
  295.         }
  296.  
  297.         if (!FindNextFile(hFile, &findData))
  298.             break;
  299.     }
  300.  
  301.     FindClose(hFile);
  302.     
  303.     return;
  304. }
  305.  
  306. /*
  307. ** search_match
  308. **
  309. **        Determine if the file matches the search criteria provided.
  310. */
  311. static short
  312. search_match(CHAR *filename, CHAR kwords[10][256], int numkwords, int any)
  313. {
  314.     int        i;
  315.     int        matched;
  316.     FILE    *hFile;
  317.     short    match[10];
  318.     CHAR    buffer[2048];
  319.  
  320.     hFile = fopen(filename, "r");
  321.     if (hFile == NULL)
  322.         return (0);
  323.  
  324.     matched = 0;
  325.     for (i = 0; i < numkwords; i++)
  326.         match[i] = 0;
  327.         
  328.     while (fgets(buffer, 2048, hFile) != NULL)
  329.     {
  330.         _strupr(buffer);
  331.  
  332.         /* Match found?                                                    */
  333.         for (i = 0; i < numkwords; i++)
  334.         {
  335.             if (strstr(buffer, kwords[i]) != NULL)
  336.             {
  337.                 if (any)
  338.                 {
  339.                     fclose(hFile);
  340.                     return (1);
  341.                 }
  342.             
  343.                 if (!match[i])
  344.                 {
  345.                     match[i] = 1;
  346.                     matched++;
  347.                 }
  348.             }
  349.         }
  350.  
  351.         /* All words matched...                                            */
  352.         if (matched == numkwords)
  353.         {
  354.             fclose(hFile);
  355.             return (1);
  356.         }
  357.     }
  358.  
  359.     fclose(hFile);
  360.  
  361.     return 0;
  362. }
  363.  
  364. /*
  365. ** search_error
  366. **
  367. **        Report a failure of the search interface.
  368. */
  369. static DWORD
  370. search_error(EXTENSION_CONTROL_BLOCK *pECB, CHAR *errorstr)
  371. {
  372.     DWORD    buflen;
  373.     CHAR    buffer[2048];
  374.  
  375.     /* Prepare the response header                                        */
  376.     wsprintf(buffer,
  377.              "Content-Type: text/html\r\n"
  378.              "\r\n"
  379.              "<head><title>Sambar ISAPI Search</title></head>\n"
  380.              "<body><h1>Sambar ISAPI Search</h1>\n"
  381.              "<hr><P>%s</P>\n"
  382.              "</body></html>\n", errorstr);
  383.  
  384.     buflen = lstrlen(buffer);
  385.  
  386.     if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
  387.         "200 OK", &buflen, (LPDWORD)buffer)) 
  388.     {
  389.         pECB->dwHttpStatusCode = 500;
  390.         return HSE_STATUS_ERROR;
  391.     }
  392.  
  393.     pECB->dwHttpStatusCode = 200;
  394.  
  395.     return HSE_STATUS_SUCCESS;
  396. }
  397.  
  398. /*
  399. **
  400. ** Parameter Processing...
  401. **
  402. */
  403.  
  404. static CHAR
  405. hex_to_ascii(CHAR *cval) 
  406. {
  407.     CHAR     c;
  408.  
  409.     c =  (cval[0] >= 'A' ? ((cval[0] & 0xDF) - 'A') + 10 : (cval[0] - '0'));
  410.     c *= 16;
  411.     c += (cval[1] >= 'A' ? ((cval[1] & 0xDF) - 'A') + 10 : (cval[1] - '0'));
  412.  
  413.     return c;
  414. }
  415.  
  416. static void 
  417. escape_to_ascii(CHAR *buf, int buflen) 
  418. {
  419.     int i, j;
  420.  
  421.     for (i = 0, j = 0; j < buflen; ++i, ++j)
  422.     {
  423.         if ((buf[i] = buf[j]) == '%' ) 
  424.         {
  425.             buf[i] = hex_to_ascii(&buf[j+1]);
  426.             j+=2;
  427.         }
  428.     }
  429.  
  430.     buf[i] = '\0';
  431. }
  432.  
  433. /*
  434. **    GET_PARAM
  435. **
  436. */
  437. static short
  438. get_param(CHAR *params, CHAR *arg, CHAR *buf, int buflen)
  439. {
  440.     int        i;
  441.     int        len;
  442.     CHAR     *head;
  443.     CHAR     *tail;
  444.  
  445.     /*
  446.     ** Find the value passed in by the client for some particular 
  447.     ** parameter within the query string. 
  448.     */
  449.     head = strstr(params, arg);
  450.     if (!head)
  451.         return (0);
  452.  
  453.     /* Increment past the equals sign...                                */
  454.     head += strlen(arg) + 1;
  455.   
  456.     /* Now determine the length of the value string.                    */
  457.     tail = strchr(head, '&');
  458.     if (tail)        
  459.         len = tail - head;
  460.     else    
  461.         len = strlen(head);
  462.  
  463.     /* Fail if we have zero lenght string.                                */
  464.     if ((len <= 0) || (len > buflen))
  465.         return (0);
  466.  
  467.     strncpy(buf, head, len);
  468.     buf[len] = '\0'; 
  469.  
  470.     /* 
  471.     ** Now replace "+" characters with " " characters and
  472.     ** "%xx" (hexadecemal) to the ASCII representation.
  473.     */
  474.     for (i = 0; i < len; i++)
  475.     {
  476.         if (buf[i] ==  '+')
  477.             buf[i] = ' ';
  478.     }
  479.  
  480.     escape_to_ascii(buf, len);
  481.  
  482.     return (1);
  483. }
  484.  
  485. static short
  486. get_next(CHAR *head, CHAR *buffer, CHAR **tailp)
  487. {
  488.     int        hlen;
  489.     int        blen;
  490.     CHAR    end;
  491.  
  492.     hlen = 0;
  493.     end = ' ';
  494.  
  495.     while (isspace(head[hlen]))
  496.         hlen++;
  497.  
  498.     if (head[hlen] == '\0')
  499.         return (0);
  500.  
  501.     if ((head[hlen] == '"') || (head[hlen] == '\''))
  502.     {
  503.         end = head[hlen];
  504.         hlen++;
  505.     }
  506.  
  507.     blen = 0;
  508.     while ((head[hlen] != '\0') && (head[hlen] != end))
  509.     {
  510.         buffer[blen] = head[hlen];
  511.         hlen++;
  512.         blen++;
  513.     }
  514.  
  515.     buffer[blen] = '\0';
  516.     *tailp = &head[hlen];
  517.  
  518.     return (1);
  519. }
  520.