home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 1999 January / dppcpro0199a.iso / January / Fp98 / SDK / WebBot / votebot / server / votesrv.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-18  |  13.9 KB  |  445 lines

  1. //
  2. // Copyright (c) 1997 Microsoft Corp.  All Rights Reserved
  3.  
  4. // votecount.cpp : Defines the initialization routines for the DLL.
  5. //
  6.  
  7. #include "votecount.h"
  8. #include "votefile.h"
  9. #include "../../webbot.h"
  10. #include <string.h>
  11. #include <stdlib.h>
  12.  
  13. #ifdef UNIX
  14. #include <unistd.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #else
  18. #include <direct.h>
  19. #include <io.h>
  20. #endif
  21.  
  22.  
  23.  
  24. ////////////////////////////////////////////////////////////////////////
  25. // Utility URL and filename manipulation functions.
  26.  
  27.  
  28. // Extracts just the filename portion of the URL.
  29. // that is, a URL of "/simple/foo/default.htm" becomes "default.htm",
  30. // and "blah.html" would become "blah.html"
  31. const char* URLtoFileName(const char *URL)
  32. {
  33.     if (!URL)
  34.         return "";
  35.  
  36.     // Look for the last '/' in the URL:
  37.     const char *last = URL;
  38.     for (const char *p=URL; *p; p++)
  39.         if (*p == '/')
  40.             last = p+1;
  41.     return last;
  42. }
  43.  
  44.  
  45.  
  46. // Copies the PageURL directory name (the PageURL minus the page itself) into 
  47. // the destination.  It also translates the '/' character into the character
  48. // specified by translateChar.  This is useful if the desired final form
  49. // is to contain '\' or ':' as the directory separator.  If the PageURL
  50. // is NULL, it does nothing.
  51. // Returns the number of characters copied, not counting the '\0' at the end
  52. // NOTE: this routine assumes enough space has been allocated in the 
  53. // destination buffer to hold the *entire* PageURL, not just enough up
  54. // to the '/' of the PageURL.
  55. //
  56. // Example: PageURL="Matt/simple/index.htm" and translateChar is '\' then
  57. // the result in dest is "Matt\simple"
  58.  
  59. void CopyPageDirName(const char *PageURL, char *dest, char translateChar)
  60. {
  61.     if (!PageURL)
  62.         return;
  63.     const char *pSource = PageURL;    
  64.     char *pDest = dest;
  65.     char *last = pDest;
  66.     for (; *pSource; pSource++, pDest++)
  67.     {
  68.         if (*pSource == '/')
  69.         {
  70.             last = pDest;
  71.             *pDest = translateChar;
  72.         }
  73.         else
  74.             *pDest = *pSource;
  75.     }
  76.  
  77.     // last points to the last '/' in the PageURL, or the beginning of it if
  78.     // there were no '/' in the string.  Thus, close off the string at
  79.     // this location.
  80.  
  81.     // if characters were copied then append a dir seperator to the end
  82.     // of the whole thing.  Otherwise, just close off the string    
  83.     if (last != dest)
  84.     {
  85.         *last = translateChar;
  86.         *(last+1) = '\0';
  87.     }
  88.     else    
  89.         *last = '\0';            
  90. }
  91.  
  92.  
  93. const char *SubWebName(CWebBotDict &bot)
  94. {
  95.     // Returns the subweb name, taken from the WebURL.
  96.     // Assumes WebURL is in the form
  97.     // protocol://host/subweb
  98.     //
  99.     // Method to find the subweb: Scan for the first / which
  100.     // does not have a / following it.  
  101.     const char *WebURL = bot.GetValue("WebURL");
  102.     if (!WebURL)
  103.         return "";
  104.     
  105.     const char *p;
  106.     for (p = WebURL; *p; p++)
  107.     {
  108.         if (*p=='/')
  109.         {
  110.             if (*(p+1) != '/')
  111.                 return p;
  112.             else
  113.                 p++;            // Skip over next spot
  114.         }
  115.     }
  116.     return p;
  117.  
  118. }
  119.  
  120.  
  121.  
  122.  
  123. ///////////////////////////////////////////////////////////////////
  124. // Vote count file functions
  125.  
  126. void CreateAndResetVoteCntFile(const char *VoteCntFile, long resetValue=0)
  127. {
  128.     // Create a CVote object for dealing with the vote count file.
  129.     // Waits until the file is available, then locks it.  Times out
  130.     // if the file does not become available in a certain amount of time.
  131.     // If the file does not exist, it will create it.
  132.    
  133.     // If new vote types are added by end-users,
  134.     // and they have different reset values, this parameter will come in
  135.     // handy.  For now, just stifle compiler warnings.
  136.  
  137.     (void)resetValue;
  138.  
  139.     CVote votefile(VoteCntFile, TRUE);
  140.     votefile.SetVoteCount("vote1type", 0);
  141.     votefile.SetVoteCount("vote2type", 0);
  142.  
  143.     // CVote destructor writes and unlocks the file
  144. }
  145.  
  146.  
  147. void HandleVoteCntFile(CWebBotDict &bot)
  148. {    
  149.     // Figures out the filename for the vote counter.  
  150.     // and makes sure it exists with a correct count according to the
  151.     // reset key.  If the reset key does not exist, and the file doesn't
  152.     // exist, make a count beginning at 0.
  153.  
  154.     // The vote counter file name will be the name of the page with .vot
  155.     // appended to the end, stored in the _private directory
  156.  
  157.     const char *PageURL = bot.GetValue("PageURL");
  158.     const char *PageFileName = URLtoFileName(PageURL);
  159.     const char *DocumentRoot = bot.GetValue("DocumentRoot");    
  160.  
  161.     if (!PageURL || !DocumentRoot)
  162.         return;
  163.  
  164.     // The idea is that the VoteCountFile is a filename which is relative to
  165.     // the location of the .htm file which contains the counter tag.
  166.     // In order to "mess around with" the vote counter file, we need to know
  167.     // the absolute pathname to the file.  The DocumentRoot tells us
  168.     // where the root directory of the web is (e.g. c:\inetsrv\wwwroot\Kuan), 
  169.     // and the PageURL tells us what is the web-relative URL of the 
  170.     // .htm file which called us (e.g. simple/index.htm).  
  171.     // So, strip off the filename of the PageURL, and append that to
  172.     // the end of the DocumentRoot, changing all '/' to the DIR_SEPARATOR
  173.     // for this platform.
  174.  
  175.     // Create a buffer large enough:
  176.     int DocumentRootLen = strlen(DocumentRoot);
  177.     char *PageDirName = new char[DocumentRootLen + 1 +        // +1 for DIR_SEPARATOR
  178.                             strlen(PageURL) + 1 +        // +1 for DIR_SEPARATOR
  179.                             + 9    +                        // +9 for "_private/"
  180.                             strlen(PageFileName) + 5];    // +5 for ".vot"+'\0'
  181.         
  182.  
  183.     // Put in the document root:
  184.     strcpy(PageDirName, DocumentRoot);
  185.  
  186.     // Append the DIR_SEPARATOR
  187.     PageDirName[DocumentRootLen] = DIR_SEPARATOR;
  188.  
  189.     // Append the PageURL, changing all '/' to the local machine's DIR_SEPARATOR
  190.     // Also appends the DIR_SEPERATOR to the end of the string
  191.     CopyPageDirName(PageURL, &PageDirName[DocumentRootLen+1], DIR_SEPARATOR);
  192.  
  193.     strcat(PageDirName, "_private/");
  194.  
  195.     // Check for existence of, and create directory
  196.     if (access(PageDirName, 0) != 0)
  197.     {
  198. #ifdef UNIX
  199.         mkdir(PageDirName, 0777);
  200. #else
  201.         mkdir(PageDirName);
  202. #endif
  203.     }
  204.     
  205.     // Now append the Page filename
  206.     strcat(PageDirName, PageFileName);
  207.  
  208.     // Now append the ".vot" ending
  209.     strcat(PageDirName, ".vot");
  210.  
  211.     ////////////////////////////////////////////////////
  212.     // Now buffer holds the filename of the vote count file.
  213.  
  214.     // If we are to reset it, do so:
  215.     const char *reset;
  216.     const char *resetValue;
  217.     reset = bot.GetValue("B-Reset");
  218.  
  219.     if (    (reset) && 
  220.         (*reset=='T') && 
  221.         (resetValue = bot.GetValue("I-ResetValue"))
  222.        )
  223.  
  224.     {        
  225.         // Only if reset key is TRUE, and there's a reset value
  226.         CreateAndResetVoteCntFile(PageDirName, atol(resetValue));
  227.         bot.SetValue("B-Reset", "FALSE");
  228.     }
  229.     else
  230.     {
  231.         // If the file doesn't exist then create it with a count of 0
  232.         if (access(PageDirName, 0) != 0)
  233.             CreateAndResetVoteCntFile(PageDirName, 0);
  234.     }
  235.  
  236.     delete[] PageDirName;
  237. }
  238.  
  239.  
  240. const int QueryNumTranslations = 2;
  241. const char QueryTranslateFrom[QueryNumTranslations] = {' ', '\\'};
  242. const char *QueryTranslateTo[QueryNumTranslations] = {"+", "/"};
  243.  
  244. const int URLNumTranslations = 2;
  245. const char URLTranslateFrom[URLNumTranslations] = {' ', '\\'};
  246. const char *URLTranslateTo[URLNumTranslations] = {"%20", "/"};
  247.  
  248.  
  249. char *URLEncode(const char *url, 
  250.                 const char *translateFrom, 
  251.                 const char **translateTo, 
  252.                 int numTranslations)
  253. {
  254.     // Note: Users of this function should delete the pointer themselves
  255.     if (url == NULL)
  256.         return NULL;
  257.  
  258.     // maximum size of the encoded string is the url's len*3.  This assumes that
  259.     // no TranslateTo string is more than 3 chars, which will always be
  260.     // true for URL encoding
  261.     char *encoded = new char[strlen(url)*3 + 1];            // +1 for '\0'
  262.  
  263.     char *currentTo = encoded;
  264.     BOOL found;
  265.     for (const char *currentFrom = url; *currentFrom; currentFrom++)
  266.     {
  267.         found = FALSE;
  268.         for (int i = 0; i < numTranslations && !found; i++)
  269.         {
  270.             if (*currentFrom == translateFrom[i])
  271.             {
  272.                 // Matches a char, copy over the translateTo for this char
  273.                 strcpy(currentTo, translateTo[i]);
  274.                 currentTo+=strlen(translateTo[i]);
  275.                 found = TRUE;
  276.             }
  277.         }
  278.  
  279.         if (!found)
  280.         {
  281.             // Wasn't a char to translate, just do plain vanilla copy
  282.             *currentTo = *currentFrom;
  283.             currentTo++;
  284.         }
  285.     }
  286.     // Close it off
  287.     *currentTo = '\0';    
  288.     return encoded;
  289. }
  290.  
  291.  
  292. /////////////////////////////////////////////////////////////////////
  293. // DLL Entry point for expanding the WebBot
  294.  
  295.  
  296. // cgi and form are unused, so they are commented out to prevent compiler
  297. // warnings.
  298. BeginWebBotExpand(votebot,ret,bot,cgi,form)
  299. {
  300.     // Squelch compiler warnings
  301.     (void)cgi;
  302.     (void)form;
  303.  
  304.     // Take care of the vote count file
  305.     HandleVoteCntFile(bot);
  306.  
  307.     // Create the HTML tag which will call the cgi program "votebot"
  308.  
  309.     // First, generate the PageDirName
  310.     const char *PageURL = bot.GetValue("PageURL");
  311.     if (!PageURL)
  312.         PageURL = "";
  313.  
  314.     // The +2 is for the potentially extra '/' at the end, and for the '\0'
  315.     char *PageDirName = new char[strlen(PageURL) + 2];
  316.     CopyPageDirName(PageURL, PageDirName, '/');
  317.  
  318.     // Now generate the Page Filename
  319.     const char *PageFileName = URLtoFileName(PageURL);
  320.  
  321.     // create the URL.  
  322.  
  323.     // Method:
  324.     // Create the URL without the query string in ret, URL encode it into "url"
  325.     // Create the query string in ret, do a query string encoding into "query"
  326.     // Clear ret, put in the MIME headers, append url and query
  327.  
  328.     // First generate URL:    
  329.     ret.Clear();
  330.  
  331.     ret.Append("/cgi-bin/votebot.exe");
  332.     ret.Append(SubWebName(bot));
  333.     ret.Append("/");    
  334.     ret.Append(PageDirName);
  335.     ret.Append("?");
  336.     char *url = URLEncode(ret.GetContents(), 
  337.                             URLTranslateFrom, 
  338.                             URLTranslateTo, 
  339.                             URLNumTranslations);        
  340.  
  341.     // Put in the MIME Links: header so that the explorer knows
  342.     // that our WebBot is linking to the vote count file.  That is, append
  343.     // Links: _private/PageFilename.vot.  But URL encode the filename
  344.     ret.Clear();
  345.     ret.Append("Links: ");
  346.     ret.Append("_private/");
  347.     char *EncodedPageFileName = URLEncode(PageFileName,
  348.                                           URLTranslateFrom,
  349.                                           URLTranslateTo,
  350.                                           URLNumTranslations);
  351.     ret.Append(EncodedPageFileName);
  352.     ret.Append(".vot");
  353.  
  354.     // Put in the MIME WriteLinks: header so that the explorer knows
  355.     // that our WebBot is writing to the vote count file.  This will cause  
  356.     // the server extensions to set the permissions on this
  357.     // file so that browsers can write to it.  
  358.     // WriteLinks: _private/PageFilename.vot.  
  359.  
  360.     ret.Append("\nWriteLinks: ");
  361.     ret.Append("_private/");
  362.     ret.Append(EncodedPageFileName);
  363.     ret.Append(".vot");    
  364.  
  365.     // Put in the MIME Error: header to signify any setup errors
  366.  
  367.     const char *sztype = bot.GetValue("S-VOTETYPE");
  368.     const char *szopt  = bot.GetValue("S-VOTEOPT");
  369.     if (!sztype || !szopt)
  370.         ret.Append("\nError: A vote component is incorrectly configured.  "
  371.                    "You must provide values for the \"S-VOTETYPE\" and "
  372.                    "\"S-VOTEOPT\" parameter(s).");
  373.  
  374.  
  375.     // a blank like signifies the end of all MIME headers:
  376. #ifdef UNIX
  377.     ret.Append("\r\n\r\n");
  378. #else
  379.     ret.Append("\n\n");
  380. #endif
  381.  
  382.     // stick the vote submit form out there.
  383.     
  384.     // for ASP implementation:
  385.     // ret.Append("\n<form method=\"POST\" action=\"asp/votebot.asp\">");
  386.  
  387.     // for CGI implementation:
  388.     ret.Append("<form method=\"GET\" action=\"");
  389.     
  390.     // Append the name of the CGI executable (vote submission handler)
  391.     ret.Append("/cgi-bin/votebot.exe");
  392.     ret.Append(SubWebName(bot));
  393.     ret.Append("/");    
  394.     ret.Append(PageDirName);
  395.     
  396.     ret.Append("\" target=\"_top\">\n<hr>");
  397.     ret.Append("\n<input type=\"hidden\" name=\"Page\" value=\"");
  398.     ret.Append(PageFileName);
  399.     
  400.     ret.Append("\">\n<div align=\"center\"><center><p>");
  401.     ret.Append("How do you feel about the ");    
  402.         
  403.     if (sztype && !strcmp(sztype,"OPINION"))
  404.         ret.Append("opinion");
  405.     
  406.     else if (sztype && !strcmp(sztype,"PROPOSAL"))
  407.         ret.Append("proposal");
  408.         
  409.     ret.Append(" expressed on this page?</p>");
  410.     ret.Append("\n</center></div><div align=\"center\"><center><p><input");
  411.     ret.Append("\ntype=\"radio\" value=\"V1\" checked name=\"Vote\">");
  412.     
  413.     if (szopt && !strcmp(szopt,"AGREE"))
  414.     {
  415.         ret.Append(" I agree!       <input");
  416.         ret.Append("\ntype=\"radio\" name=\"Vote\" value=\"V2\"> I disagree!");
  417.     }
  418.     
  419.     else if (szopt && !strcmp(szopt,"YES/NO"))
  420.     {
  421.         ret.Append(" Yes!       <input");
  422.         ret.Append("\ntype=\"radio\" name=\"Vote\" value=\"V2\"> No!");
  423.     }
  424.     
  425.     else if (szopt && !strcmp(szopt,"FOR/AGAINST"))
  426.     {
  427.         ret.Append(" For!       <input");
  428.         ret.Append("\ntype=\"radio\" name=\"Vote\" value=\"V2\"> Against!");
  429.     };
  430.         
  431.     ret.Append("\n</p></center></div>");    
  432.     ret.Append("\n<div align=\"center\"><center><p><input type=\"submit\"");
  433.     ret.Append("value=\"Cast Your Ballot!\" name=\"B1\"></p>");
  434.     ret.Append("\n</center></div><hr>");
  435.     ret.Append("\n</form>");
  436.  
  437.     delete[] PageDirName;
  438.     delete[] EncodedPageFileName;
  439.     delete[] url;
  440. }
  441. EndWebBotExpand
  442.  
  443.  
  444.  
  445.