home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / os / win32 / installer / installdll / install.c next >
Encoding:
C/C++ Source or Header  |  1998-05-07  |  15.6 KB  |  544 lines

  1. /* Apache Installer */
  2.  
  3. /*
  4.  * 26/06/97 PCS 1.000 Initial version
  5.  * 22/02/98 PCS 1.001 Used the excellent NTemacs to apply proper formating
  6.  * 04/05/98 PCS 1.002 Copy conf files to *.conf.default, then to *.conf
  7.  */
  8.  
  9. #include <windows.h>
  10. #include <winsock.h>
  11. #include <string.h>
  12. #include <stdio.h>
  13. #include <direct.h>
  14.  
  15. /* Global to store the instance handle */
  16. HINSTANCE hInstance = NULL;
  17.  
  18. /*
  19.  * MessageBox_error() is a helper function to display an error in a 
  20.  * message box, optionally including a Win32 error message. If
  21.  * the "opt" argument is value AP_WIN32ERROR then get the last Win32
  22.  * error (with GetLastError()) and add it on to the end of
  23.  * the output string. The output string is given as a printf-format
  24.  * and replacement arguments. The hWnd, title and mb_opt fields are 
  25.  * passed on to the Win32 MessageBox() call.
  26.  *
  27.  * We shouldn't use a fixed length buffer to build up the printf
  28.  * text. Umm.
  29.  */
  30.  
  31. #define AP_WIN32ERROR 1
  32.  
  33. int MessageBox_error(HWND hWnd, int opt, char *title, 
  34.              int mb_opt, char *fmt, ...)
  35. {
  36.     char buf[4000];
  37.     va_list ap;
  38.  
  39.     va_start(ap, fmt);
  40.     wvsprintf(buf, fmt, ap);
  41.     va_end(ap);
  42.  
  43.     if (opt & AP_WIN32ERROR) {
  44.     char *p;
  45.  
  46.     strcat(buf, "\r\r(");
  47.     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
  48.               NULL,
  49.               GetLastError(),
  50.               0,
  51.               buf + strlen(buf),
  52.               4000 - strlen(buf),
  53.               NULL);
  54.     p = buf+strlen(buf)-1;
  55.     while (*p == '\r' || *p == '\n')
  56.         p--;
  57.     p++;
  58.     *p = '\0';
  59.     strcat(buf, ")");
  60.     }
  61.  
  62.     return MessageBox(hWnd, buf, title, mb_opt);
  63. }
  64.  
  65. /*
  66.  * The next few functions handle expanding the @@ServerRoot@@ type
  67.  * sequences found in the distribution files. The main entry point
  68.  * is expandFile(), which is given a file to expand and the filename
  69.  * to write the expanded file it. It reads a line at a time, and
  70.  * if the line includes an "@@" sequence, calls expandLine() to
  71.  * expand the sequences.
  72.  *
  73.  * expandLine() looks for @@ sequences in the line, and when it finds
  74.  * one, looks for a corresponding entry in the replaceTable[]. If it
  75.  * finds one it writes the replacement value from the table into
  76.  * an output string.
  77.  *
  78.  * The helper function appendText() is used when expanding strings. It
  79.  * is called to copy text into an output buffer. If the output buffer
  80.  * is not big enough, it is expanded. This function also ensures that
  81.  * the output buffer is null terminated after each append operation.
  82.  *
  83.  * A table is used of values to replace, rather than just hardcoding
  84.  * the functionality, so we could replace additional values in the
  85.  * future.  We also take care to ensure that the expanded line can be
  86.  * arbitrary length (though at the moment the lines read from the
  87.  * configuration files can only be up to 2000 characters).
  88.  */
  89.  
  90. /*
  91.  * Table of items to replace. The "value" elements are filled in at runtime
  92.  * by FillInReplaceTable(). Note that the "tmpl" element is case
  93.  * sensitive.
  94.  */
  95.  
  96. typedef struct {
  97.     char *tmpl;
  98.     char *value;
  99. } REPLACEITEM;
  100. typedef REPLACEITEM *REPLACETABLE;
  101.  
  102. REPLACEITEM replaceHttpd[] = {
  103.     { "@@ServerRoot@@", NULL },    /* ServerRoot directory (i.e. install dir) */
  104.     { NULL, NULL }
  105. };
  106.  
  107. /*
  108.  * A relatively intelligent version of strcat, that expands the destination
  109.  * buffer if needed.
  110.  *
  111.  * On entry, ppBuf points to the output buffer, pnPos points to the offset
  112.  * of the current position within that buffer, and pnSize points to the
  113.  * current size of *ppBuf. pSrc points to the string to copy into the buffer,
  114.  * and nToCopy gives the number of characters to copy from pSrc.
  115.  *
  116.  * On exit, *ppBuf, *pnPos and *pnSize may have been updated if the output
  117.  * buffer needed to be expanded. The output buffer will be null terminated.
  118.  * Returns 0 on success, -1 on error. Does not report errors to the user.
  119.  */
  120.  
  121. int appendText(char **ppBuf, int *pnPos, int *pnSize, char *pSrc, int nToCopy)
  122. {
  123.     char *pBuf = *ppBuf;    /* output buffer */
  124.     int nPos = *pnPos;        /* current offset into pBuf */
  125.     int nSize = *pnSize;    /* current size of pBuf */
  126.  
  127.     while (nPos + nToCopy >= nSize) {
  128.     /* Not enough space, double size of output buffer. Note we use
  129.      * >= not > so that we have enough space for the NULL character
  130.      * in the output buffer */
  131.     char *pBufNew;
  132.  
  133.     pBufNew = realloc(pBuf, nSize * 2);
  134.     if (!pBufNew)
  135.         return -1;
  136.     nSize *= 2;
  137.  
  138.     /* Update the max size and buffer pointer */
  139.     *pnSize = nSize;
  140.     *ppBuf = pBuf = pBufNew;
  141.     }
  142.  
  143.     /* Ok, now we have enough space, copy the stuff */
  144.  
  145.     strncpy(pBuf+nPos, pSrc, nToCopy);
  146.     nPos += nToCopy;
  147.     *pnPos = nPos;        /* update current position */
  148.     pBuf[nPos] = '\0';        /* append trailing NULL */
  149.  
  150.     return 0;
  151. }
  152.  
  153. /*
  154.  * Expand all the sequences in an input line. Returns a pointer to the
  155.  * expanded line. The caller should free the returned data.
  156.  * The replaceTable argument is a table of sequences to expand.
  157.  *
  158.  * Returns NULL on error. Does not report errors to the user.
  159.  */
  160.  
  161. char *expandLine(char *in, REPLACETABLE replaceTable)
  162. {
  163.     REPLACEITEM *item;
  164.     char *pos;            /* current position in input buffer */
  165.     char *outbuf;        /* output buffer */
  166.     int outbuf_size;        /* current size of output buffer */
  167.     int outbuf_position;    /* current position in output buffer */
  168.     char *start;        /* position to copy from in input buffer */
  169.  
  170.     /* Create an initial output buffer. Guess that twice the input size
  171.      * is a good length, after expansion. Don't worry if we need more
  172.      * though, appendText() will expand as needed.
  173.      */
  174.     outbuf_size = strlen(in) * 2;
  175.     outbuf_position = 0;
  176.     outbuf = malloc(outbuf_size);
  177.     if (!outbuf)
  178.       return NULL;
  179.  
  180.     start = in;            /* mark the start of uncopied data */
  181.     pos = in;            /* current position in input buffer */
  182.  
  183.     while (1) {
  184.  
  185.     /* Look for '@@' sequence, or end of input */
  186.     if (*pos && !(*pos == '@' && *(pos+1) == '@')) {
  187.         pos++;
  188.         continue;
  189.     }
  190.  
  191.     if (!*pos) {
  192.         /* End of input string, copy the uncopied data */
  193.         if (appendText(&outbuf, &outbuf_position, &outbuf_size, 
  194.                start, pos-start) < 0) {
  195.         return NULL;
  196.         }
  197.         break;
  198.     }
  199.  
  200.     /* Found first @ of a possible token to replace. Look for end
  201.      * of the token
  202.      */
  203.     for (item = replaceTable; item->tmpl; ++item) {
  204.         if (!strncmp(pos, item->tmpl, strlen(item->tmpl)))
  205.         break;
  206.     }
  207.  
  208.     if (item->tmpl) {
  209.         /* Found match. First copy the uncopied data from the input
  210.          * buffer (start through to pos-1), then copy the expanded
  211.          * value. */
  212.         if (appendText(&outbuf, &outbuf_position, &outbuf_size,
  213.                start, pos-start) < 0) {
  214.         return NULL;
  215.         }
  216.         if (appendText(&outbuf, &outbuf_position, &outbuf_size,
  217.                item->value, strlen(item->value)) < 0) {
  218.         return NULL;
  219.         }
  220.  
  221.         /* Update current position to skip over the input buffer
  222.          * @@...@@ sequence, and update the "start" pointer to uncopied
  223.          * data
  224.          */
  225.         pos += strlen(item->tmpl);
  226.         start = pos;
  227.     } 
  228.     else {
  229.         /* The sequence did not exist in the replace table, so copy
  230.          * it as-is to the output.
  231.          */
  232.         pos++; 
  233.     }
  234.     }
  235.  
  236.     return outbuf;
  237. }
  238.  
  239. /*
  240.  * Some options to determine how we copy a file. Apart from OPT_NONE, these should
  241.  * be OR'able
  242.  */
  243.  
  244. typedef enum { 
  245.     OPT_NONE = 0, 
  246.     OPT_OVERWRITE = 1,        /* Always overwrite destination file */
  247.     OPT_EXPAND = 2,        /* Expand any @@...@@ tokens in replaceHttpd */
  248.     OPT_DELETESOURCE = 4,   /* Delete the source file after the copy */
  249.     OPT_SILENT = 8,        /* Don't tell use about failures */
  250. } options_t;
  251.  
  252. /* 
  253.  * Copy a file, expanding sequences from the replaceTable argument.
  254.  * Returns 0 on success, -1 on error. Reports errors to user.
  255.  */
  256. #define MAX_INPUT_LINE 2000
  257. int WINAPI ExpandConfFile(HWND hwnd, LPSTR szInst, LPSTR szinFile, LPSTR szoutFile, REPLACETABLE replaceTable, options_t options)
  258. {
  259.     char inFile[_MAX_PATH];
  260.     char outFile[_MAX_PATH];
  261.     char inbuf[MAX_INPUT_LINE];
  262.     FILE *infp;
  263.     FILE *outfp;
  264.  
  265.     sprintf(inFile, "%s\\%s", szInst, szinFile);
  266.     sprintf(outFile, "%s\\%s", szInst, szoutFile);
  267.  
  268.     if (!(infp = fopen(inFile, "r"))) {
  269.     MessageBox_error(hwnd, 
  270.              AP_WIN32ERROR,
  271.              "Installation Problem",
  272.              MB_OK | MB_ICONSTOP,
  273.              "Cannot read file %s", inFile);
  274.     return -1;
  275.     }
  276.     if (! (options & OPT_OVERWRITE)) {
  277.     /* Overwrite not allowed if file does not exist */
  278.     if ((outfp = fopen(outFile, "r"))) {
  279.         if (! (options & OPT_SILENT)) {
  280.         MessageBox_error(hwnd,
  281.                  0,
  282.                  "File not overwritten",
  283.                  MB_OK | MB_ICONWARNING,
  284.                  "Preserving existing file %s.\r\r"
  285.                  "The new version of this file has been left in %s", 
  286.                  outFile, inFile);
  287.         }
  288.         fclose(outfp);
  289.         fclose(infp);
  290.         return 0;
  291.     }
  292.     /* To get here, output file does not exist */
  293.     }
  294.     if (!(outfp = fopen(outFile, "w"))) {
  295.     MessageBox_error(hwnd, 
  296.              AP_WIN32ERROR,
  297.              "Installation Problem",
  298.              MB_OK | MB_ICONSTOP,
  299.              "Cannot write to file %s", outFile);
  300.     fclose(infp);
  301.     return -1;
  302.     }
  303.  
  304.     while (fgets(inbuf, MAX_INPUT_LINE, infp)) {
  305.     char *pos;
  306.     char *outbuf;
  307.  
  308.     /* Quickly look to see if this line contains any
  309.      * @@ tokens. If it doesn't, we don't need to bother
  310.      * called expandLine() or taking a copy of the input
  311.      * buffer.
  312.      */
  313.     if (options & OPT_EXPAND) {
  314.             for (pos = inbuf; *pos; ++pos)
  315.         if (*pos == '@' && *(pos+1) == '@')
  316.             break;
  317.     }
  318.  
  319.     if (options & OPT_EXPAND && *pos) {
  320.         /* The input line contains at least one '@@' sequence, so
  321.          * call expandLine() to expand any sequences we know about.
  322.          */
  323.         outbuf = expandLine(inbuf, replaceTable);
  324.         if (outbuf == NULL) {
  325.         fclose(infp);
  326.         fclose(outfp);
  327.         MessageBox_error(hwnd,
  328.                  0,
  329.                  "Installation Problem",
  330.                  MB_OK|MB_ICONSTOP,
  331.                  "An error occurred during installation");
  332.         return -1;
  333.         }
  334.     }
  335.     else {
  336.         outbuf = NULL;
  337.     }
  338.  
  339.     /* If outbuf is NULL, we did not need to expand sequences, so
  340.      * just output the contents of the input buffer.
  341.      */
  342.     fwrite(outbuf ? outbuf : inbuf, 1, 
  343.            strlen(outbuf ? outbuf : inbuf), outfp);
  344.  
  345.     if (outbuf)
  346.         free(outbuf);
  347.     }
  348.     fclose(infp);
  349.     fclose(outfp);
  350.  
  351.     if (options & OPT_DELETESOURCE) {
  352.     unlink(inFile);
  353.     }
  354.  
  355.     return 0;
  356. }
  357.  
  358. int FillInReplaceTable(HWND hwnd, REPLACETABLE table, char *szInst)
  359. {
  360.     REPLACEITEM *item;
  361.     for (item = table; item->tmpl; ++item) {
  362.     if (!strcmp(item->tmpl, "@@ServerRoot@@")) {
  363.         char *p;
  364.  
  365. #if NEED_SHORT_PATHS
  366.         int len;
  367.         len = GetShortPathName(szInst, NULL, 0);
  368.         if (len > 0) {
  369.         item->value = (char*)malloc(len+1);
  370.         GetShortPathName(szInst, item->value, len);
  371.         }
  372. #else
  373.         if ((item->value = strdup(szInst)) == NULL)
  374.             return -1;
  375. #endif
  376.         for (p = item->value; *p; p++)
  377.             if (*p == '\\') *p = '/';
  378.         continue;
  379.     }
  380. #if NEED_FQDN
  381.     if (!strcmp(item->tmpl, "FQDN")) {
  382.         item->value = GetHostName(hwnd);
  383.         continue;
  384.     }
  385. #endif
  386.     }
  387.     return 0;
  388. }
  389.  
  390. /*
  391.  * actionTable[] contains things we do when this DLL is called by InstallShield
  392.  * during the install. It is like a simple script, without us having to
  393.  * worry about parsing, error checking, etc.
  394.  *
  395.  * Each item in the table is of type ACTIONITEM. The first element is the action 
  396.  * to perform (e.g. CMD_COPY). The second and third elements are filenames
  397.  * (e.g. for CMD_COPY, the first filename is the source and the second filename
  398.  * is the destination). The final element of ACTIONITEM is a set of options
  399.  * which apply to the current "command". For example, OPT_EXPAND on a CMD_COPY
  400.  * line, tells the copy function to expand @@ServerRoot@@ tokens found in the
  401.  * source file.
  402.  *
  403.  * The contents of this table are performed in order, top to bottom. This lets
  404.  * us expand the files to the *.conf.default names, then copy to *.conf only
  405.  * if the corresponding *.conf file does not already exist. If it does exist,
  406.  * it is not overwritten.
  407.  *
  408.  * Return 1 on success, 0 on error.
  409.  */
  410.  
  411. typedef enum {
  412.     CMD_COPY = 0,
  413.     CMD_RMDIR,
  414.     CMD_RM,
  415.     CMD_END
  416. } cmd_t;
  417.  
  418. typedef struct {
  419.     cmd_t command;
  420.     char *in;
  421.     char *out;
  422.     options_t options;
  423. } ACTIONITEM;
  424. typedef ACTIONITEM *ACTIONTABLE;
  425.  
  426. ACTIONITEM actionTable[] = {
  427.     /*
  428.      * Installation of the configuraton files. These are installed into the ".tmp"
  429.      * directory by the installer. We first move them to conf\*.default (overwriting
  430.      * any *.default file from a previous install). The *.conf-dist-win files
  431.      * are also expanded for any @@...@@ tokens. Then we copy the conf\*.default
  432.      * file to corresponding conf\* file, unless that would overwrite an existing file.
  433.      */
  434.     { CMD_COPY, ".tmp\\mime.types", "conf\\mime.types.default",
  435.     OPT_OVERWRITE|OPT_DELETESOURCE },
  436.     { CMD_COPY, ".tmp\\magic", "conf\\magic.default",
  437.     OPT_OVERWRITE|OPT_DELETESOURCE },
  438.     { CMD_COPY, ".tmp\\httpd.conf-dist-win", "conf\\httpd.conf.default", 
  439.     OPT_OVERWRITE|OPT_EXPAND|OPT_DELETESOURCE },
  440.     { CMD_COPY, ".tmp\\srm.conf-dist-win", "conf\\srm.conf.default", 
  441.     OPT_OVERWRITE|OPT_EXPAND|OPT_DELETESOURCE },
  442.     { CMD_COPY, ".tmp\\access.conf-dist-win", "conf\\access.conf.default", 
  443.     OPT_OVERWRITE|OPT_EXPAND|OPT_DELETESOURCE },
  444.  
  445.     /* Now copy to the 'live' files, unless they already exist */
  446.     { CMD_COPY, "conf\\httpd.conf.default", "conf\\httpd.conf", OPT_NONE },
  447.     { CMD_COPY, "conf\\srm.conf.default", "conf\\srm.conf", OPT_NONE },
  448.     { CMD_COPY, "conf\\access.conf.default", "conf\\access.conf", OPT_NONE },
  449.     { CMD_COPY, "conf\\magic.default", "conf\\magic", OPT_NONE },
  450.     { CMD_COPY, "conf\\mime.types.default", "conf\\mime.types", OPT_NONE },
  451.  
  452.     { CMD_COPY, ".tmp\\highperformance.conf-dist", "conf\\highperformance.conf-dist", 
  453.     OPT_EXPAND|OPT_OVERWRITE|OPT_DELETESOURCE },
  454.  
  455.     /* Move the default htdocs files into place, provided they don't already
  456.      * exist.
  457.      */
  458.     { CMD_COPY, ".tmp\\index.html", "htdocs\\index.html", OPT_DELETESOURCE|OPT_SILENT },
  459.     { CMD_RM, ".tmp\\index.html", NULL, OPT_SILENT },
  460.     { CMD_COPY, ".tmp\\apache_pb.gif", "htdocs\\apache_pb.gif", OPT_DELETESOURCE|OPT_SILENT },
  461.     { CMD_RM, ".tmp\\apache_pb.gif", NULL, OPT_SILENT },
  462.  
  463.     { CMD_RMDIR, ".tmp", NULL },
  464.  
  465.     { CMD_END, NULL, NULL, OPT_NONE }
  466. };
  467.  
  468. /*
  469.  * BeforeExit() is the DLL call from InstallShield. The arguments and
  470.  * return value as defined by the installer. We are only interested
  471.  * in the install directory, szInst. Return 0 on error and 1 on
  472.  * success (!?).
  473.  */
  474.  
  475. CHAR WINAPI BeforeExit(HWND hwnd, LPSTR szSrcDir, LPSTR szSupport, LPSTR szInst, LPSTR szRes)
  476. {
  477.     ACTIONITEM *pactionItem;
  478.     int end = 0;
  479.  
  480.     FillInReplaceTable(hwnd, replaceHttpd, szInst);
  481.  
  482.     pactionItem = actionTable;
  483.     while (!end) {
  484.     switch(pactionItem->command) {
  485.     case CMD_END:
  486.         end = 1;
  487.         break;
  488.     case CMD_COPY:
  489.         if (ExpandConfFile(hwnd, szInst, 
  490.                    pactionItem->in, 
  491.                    pactionItem->out,
  492.                    replaceHttpd,
  493.                    pactionItem->options) < 0) {
  494.         /* Error has already been reported to the user */
  495.         return 0;
  496.         }
  497.         break;
  498.     case CMD_RM: {
  499.         char inFile[MAX_INPUT_LINE];
  500.  
  501.         sprintf(inFile, "%s\\%s", szInst, pactionItem->in);
  502.         if (unlink(inFile) < 0 && !(pactionItem->options & OPT_SILENT)) {
  503.         MessageBox_error(hwnd, AP_WIN32ERROR, "Error during configuration",
  504.             MB_ICONHAND,
  505.             "Could not remove file %s", 
  506.             inFile);
  507.         return 0;
  508.         }
  509.         break;
  510.     }
  511.     case CMD_RMDIR: {
  512.         char inFile[MAX_INPUT_LINE];
  513.  
  514.         sprintf(inFile, "%s\\%s", szInst, pactionItem->in);
  515.         if (rmdir(inFile) < 0) {
  516.         MessageBox_error(hwnd, AP_WIN32ERROR, "Error during configuration",
  517.             MB_ICONHAND,
  518.             "Could not delete temporary directory %s", 
  519.             inFile);
  520.         return 0;
  521.         }
  522.         break;
  523.     }
  524.     default:
  525.         MessageBox_error(hwnd, 0, "Error during configuration",
  526.             MB_ICONHAND,
  527.             "An error has occurred during configuration\r"
  528.             "(Error: unknown command %d)", (int)pactionItem->command);
  529.         end = 1;
  530.         break;
  531.     }
  532.     pactionItem++;
  533.     }
  534.     return 1;
  535. }
  536.  
  537.  
  538. BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
  539. {
  540.     if (fdwReason == DLL_PROCESS_ATTACH)
  541.     hInstance = hInstDLL;
  542.     return TRUE;
  543. }
  544.