home *** CD-ROM | disk | FTP | other *** search
/ Total C++ 2 / TOTALCTWO.iso / vfp5.0 / vfp / samples / servers / foxisapi / foxisapi.cpp < prev    next >
C/C++ Source or Header  |  1996-08-21  |  24KB  |  732 lines

  1. /*
  2.  
  3. FOXISAPI.CPP
  4.  
  5. Copyright (c)1995 Microsoft Corporation, All Right Reserved
  6.  
  7. Implements an ISAPI dll that will invoke OLE Automation objects
  8. using IDispatch.  Any object that has a PROGID and implements one
  9. or more methods with the following signature can be called by this dll:
  10.  
  11.  
  12. URLs that invoke this service from a link will look like this:
  13.  
  14. http://machine/path/progid.method?foo=bar&baz=bleh
  15.  
  16. This dll will also work with HTML forms that use the POST method,
  17. in which case the parameters to the call will come from the form
  18. elements rather than the URL.  Any parameters sent as part of the
  19. action URL will not be passed on to the OLE object.
  20.  
  21. Exports:
  22.  
  23. BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer )
  24. BOOL WINAPI HttpExtensionProc(   EXTENSION_CONTROL_BLOCK *pECB )
  25. DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID pvReserved)
  26.  
  27.   Debug instructions:
  28.   Start IIS, find the process ID of INETINFO, start MSDEV -p pid
  29.   use a browser (from another machine perhaps) and hit the page
  30.   Use IIS to stop/restart WWW services
  31.  
  32.   From MSDevStudio Technote 63:
  33.     
  34. If you have written an Internet Server Extension DLL, you might be interested in getting it
  35.  running under the debugger so you can trace its execution, set breakpoints, or monitor variable 
  36.  values after the extension is called. Getting the Microsoft Internet Information Server up and 
  37.  running with your DLL in the debugger is possible, but a little tricky. 
  38.  You will need to first find a quiet server where you can debug your DLL in a controlled 
  39.  setting. Once you've found such a resource, you can start debugging your DLL by following 
  40.  these steps:
  41.  1.    Stop the Internet Information Server publishing services by using the Internet 
  42.     Information Server Manager, or by stopping the services with the Services icon 
  43.     in the Windows NT Advanced Server Control Panel. Note that you must stop all 
  44.     three services even though you are only debugging extensions to the World-Wide
  45.     Web publishing service. To make things easier, you might wish to make the services
  46.     "Manually" started in Control Panel so you can avoid this step later in your 
  47.     development.
  48.  2.    Start Microsoft Developer Studio, and click the Close Workspace command on the File 
  49.     menu to close any opened workspace.
  50.  3.    On the File menu, click Open Workspace to open the INETINFO.EXE program. This file is 
  51.     in the directory where you installed the Microsoft Internet Information Server. 
  52.  4.    On the Build menu, click Settings, then click the Debug tab in the Project Settings 
  53.     dialog box.
  54.  5.    With General selected in the Category box, type the following in the Program Arguments 
  55.     text box:
  56.   
  57. -e W3Svc
  58.   
  59.  6.    Choose Additional DLLs from the Category drop-down list. Then, in the Local Names box, 
  60.     specify the complete path and name of your extension DLL or DLLs. Make sure the check 
  61.     box next to each is marked.
  62.  7.    Click OK to close the Project Settings dialog box.
  63.  8.    Make sure that the .PDB file for your DLL is in the same directory as the DLLs you plan 
  64.     to debug.
  65.   
  66. Debugging Tips
  67. The Internet server can be run as an interactive application. This makes debugging it much 
  68. easier. To do this you need to make a few changes to your system. For the user account that
  69. you are going to run the server under, you need to add a few privileges. 
  70. To add privileges, run User Manager (MUSRMGR.EXE), and select User Rights from the Policies
  71. menu. Check the Show Advanced User Rights check box. Then select "Act as part of the 
  72. operating system" from the Right drop-down list, and add the user account. 
  73. Repeat this process with "Generate Security Audits" (also in the Rights drop-down list). 
  74. Make sure that all Internet Services (WWW, ftp & gopher) are stopped and INetInfo.Exe is 
  75. not running (use TLIST to check). Log off and log back on. You can then load the Internet 
  76. Server with the command line:
  77.   
  78. INetInfo.Exe -e W3Svc
  79.  
  80.   
  81. If you want to load this under a debugger just make sure that the command line to 
  82. INetInfo.Exe is "-e W3Svc". (For example, to load it into WinDbg the command line is: 
  83. WinDbg INetInfo.Exe -e W3Svc
  84. You are ready to go. In Developer Studio, point to Debug on the Build menu and then choose 
  85. Go. This action starts the WWW publishing service, and the debugger will be aware of 
  86. symbols in your DLL. You'll get a warning that this file doesn't have debugging 
  87. information in it, but you can ignore that message.
  88. When you leave Microsoft Developer Studio, you will probably want to make sure that you 
  89. let Developer Studio save the workspace for INETINFO.EXE. This will let you avoid 
  90. reentering all of the settings you need to make instead, you can just open the 
  91. INETINFO.MDS workspace file when you are ready to start the debugger again.
  92.  
  93. Cached DLLs
  94. You can adjust the registry setting at 
  95. HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/CacheExtensions 
  96. to have the server reinitialize DLLs each time they are used. If this setting is 1, 
  97. the server will keep DLLs loaded in memory as long as possible. This is the default 
  98. setting for the server, since it helps the server achieve peak performance, and should 
  99. only be changed if you are using the server for debugging. If you make the setting zero, 
  100. the server will always reload extension DLLs each time they are used.
  101. Forcing the server to reload your DLL is very helpful when your DLL might be crashing or 
  102. upsetting any per-instance data that it maintains. By forcing the server to reinitialize 
  103. the DLL, you can get your DLL back into a predictable state with very little effort. 
  104. You should make sure you test your DLL using the normal CacheExtensions setting, however, 
  105. to make sure code in your DLL isn't completely dependent on that initial state.
  106.  
  107.  
  108. You can start and stop a service from the dos command prompt:
  109.     net start <service name>
  110.         or
  111.       net start "world wide web publishing service"
  112.  
  113.  
  114.  
  115.  
  116. */
  117.  
  118. #include <windows.h>
  119. #include <httpext.h>
  120. #include <memory.h>
  121. #include <stdio.h>
  122.  
  123. #define PARAMLEN 1024
  124. #define BUFLEN 4096
  125.  
  126. // TLS is used to store OLE initialization flag
  127. static DWORD g_dwTLSIndex = (DWORD)-1;
  128.  
  129. //the full path dir of this dll
  130. static char gszAppDir[MAX_PATH];
  131.  
  132. static char szErrorMessage[MAX_PATH];
  133.  
  134. // Many OA objects are not threadsafe, so we only allow one at a time
  135. static CRITICAL_SECTION g_csRuntime;
  136.  
  137. HRESULT HResultFromLastError()
  138. {
  139.     DWORD dwLastError = GetLastError();
  140.     return HRESULT_FROM_WIN32(dwLastError);
  141. }
  142.  
  143. //
  144. // Retrieve the class Id from the registry given its program id name.
  145. // The program ID name is converted from ASCII to wide char here since
  146. // URL strings are always ASCII.
  147. //
  148. HRESULT GetClsidFromProgIdA(LPCLSID pclsid, CHAR* pszName, long cbName)
  149. {
  150.     HRESULT hr;
  151.     // Allocate a wide char string for the Prog Id name.
  152.     OLECHAR *lpWideCharProgId = new WCHAR[cbName];
  153.     if (NULL == lpWideCharProgId) {
  154.         hr = ResultFromScode(E_OUTOFMEMORY);
  155.         goto LError;
  156.     }
  157.  
  158.     // Convert the ProgId name to wide chars.
  159.     if (0 == MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, pszName, -1,
  160.                                  lpWideCharProgId, cbName) ) {
  161.         hr = (HResultFromLastError());
  162.         goto LError;
  163.     }
  164.  
  165.     // Now get the class Id from the Program Id.
  166.     hr = CLSIDFromProgID(lpWideCharProgId, pclsid);
  167.  
  168. LError:
  169.     if (NULL != lpWideCharProgId) {
  170.         delete [] lpWideCharProgId;
  171.     }
  172.     return(hr);
  173. }
  174.  
  175. //
  176. // Invoke obtains the prog ID and method to be invoked and blasts away.
  177. // If successful, it returns a string to be forced back up the pipe, else NULL
  178. //
  179. BOOL CallObject(EXTENSION_CONTROL_BLOCK *pECB,
  180.         CHAR *pszProgid,CHAR *pszMethod, CHAR * lpszIniFile)
  181. {
  182.     BOOL fSuccess = FALSE;
  183.     BOOL fInCritSec = FALSE;
  184.     HRESULT hr;
  185.     CLSID clsid;
  186.     IDispatch *pdispObj = NULL;
  187.     IUnknown *punkObj = NULL;
  188.     OLECHAR wzMethod[PARAMLEN];
  189.     OLECHAR wzParams[PARAMLEN];
  190.     OLECHAR wzIniFile[MAX_PATH * 2];
  191.     BSTR bstrParams = NULL;
  192.     BSTR bstrIniFile = NULL;
  193.     CHAR *pszResults = NULL;
  194.     DWORD buflen = 0;
  195.  
  196.     OLECHAR *pwzName;
  197.     DISPID dispidMethod;
  198.     DISPPARAMS dispparms;
  199.     VARIANTARG varg[3];
  200.     VARIANTARG vResult;
  201.     EXCEPINFO excep;
  202.     
  203.     IDispatch * pdispDoRelease;
  204.  
  205.     // initialize everything up front so cleanup is safe
  206.     *szErrorMessage = '\0';
  207.     memset(&dispparms, 0, sizeof(DISPPARAMS));
  208.     memset(&varg, 0, sizeof(VARIANTARG) * 3);
  209.     memset(&excep, 0, sizeof(EXCEPINFO));
  210.     memset(wzParams, 0, PARAMLEN);
  211.  
  212.     VariantInit(&varg[0]);
  213.     VariantInit(&varg[1]);
  214.     VariantInit(&varg[2]);
  215.     VariantInit(&vResult);
  216.  
  217.     // convert progid to clsid
  218.     hr = GetClsidFromProgIdA(&clsid, pszProgid, lstrlen(pszProgid)+1);
  219.     if (FAILED(hr)) {
  220.         char szSysMsg[100];
  221.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,hr,
  222.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),szSysMsg,sizeof(szSysMsg),0);
  223.  
  224.         sprintf(szErrorMessage,"Class id for %s not found. Err code = %08x: %s",
  225.                 pszProgid,hr,szSysMsg);
  226.         goto Err;
  227.     }
  228.     // Grab critical section
  229.     EnterCriticalSection(&g_csRuntime);
  230.     fInCritSec = TRUE;
  231.  
  232.     //See if we can get an existing instance
  233.     hr = GetActiveObject(clsid,NULL,&punkObj);
  234.     if (FAILED(hr))
  235.     {
  236.         // instantiate object
  237.         hr = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IUnknown, (LPVOID *) &punkObj);
  238.     }
  239.     if (FAILED(hr) || punkObj == NULL) {
  240.         char szSysMsg[100];
  241.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,hr,
  242.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),szSysMsg,sizeof(szSysMsg),0);
  243.  
  244.         sprintf(szErrorMessage,"CoCreateInstance failed with err code %08x: %s",hr,szSysMsg);
  245.         goto Err;
  246.     }
  247.     hr = punkObj->QueryInterface(IID_IDispatch, (void**)&pdispObj);
  248.     if (FAILED(hr) || pdispObj == NULL)  {
  249.         char szSysMsg[100];
  250.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,hr,
  251.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),szSysMsg,sizeof(szSysMsg),0);
  252.  
  253.         sprintf(szErrorMessage,"QueryInterface failed with err code %08x: %s",hr,szSysMsg);
  254.         goto Err;
  255.     }
  256.     punkObj->Release();
  257.     punkObj = NULL;
  258.  
  259.     // Convert the method name and args to Wide character.
  260.     if (0 == MultiByteToWideChar(CP_ACP, 0, pszMethod, -1, wzMethod, PARAMLEN) ) {
  261.         sprintf(szErrorMessage,"MultiByteToWideChar failed");
  262.         goto Err;
  263.     }
  264.  
  265.     // If POST, grab data from control block
  266.     if (0 == strcmp(pECB->lpszMethod, "POST")) {
  267.         if (0 == MultiByteToWideChar(CP_ACP, 0, (const char*)pECB->lpbData, pECB->cbAvailable, wzParams, PARAMLEN) ) {
  268.             sprintf(szErrorMessage,"MultiByteToWideChar failed");
  269.             goto Err;
  270.         }
  271.     }
  272.     // otherwise, get it from the query string
  273.     else if (NULL != pECB->lpszQueryString) {
  274.         if (0 == MultiByteToWideChar(CP_ACP, 0, pECB->lpszQueryString, -1, wzParams, PARAMLEN) ) {
  275.             sprintf(szErrorMessage,"MultiByteToWideChar failed");
  276.             goto Err;
  277.         }
  278.     } else {
  279.           wzParams[0] = (WCHAR)0;
  280.     }
  281.     if (0 == MultiByteToWideChar(CP_ACP, 0, (const char*)lpszIniFile, -1, wzIniFile, sizeof(wzIniFile)) ) {
  282.         sprintf(szErrorMessage,"MultiByteToWideChar failed");
  283.         goto Err;
  284.     }
  285.     bstrParams = SysAllocString(wzParams);
  286.     if (NULL == bstrParams) {
  287.         sprintf(szErrorMessage,"SysAllocString failed");
  288.         goto Err;
  289.     }
  290.     bstrIniFile = SysAllocString(wzIniFile);
  291.     if (NULL == bstrIniFile) {
  292.         sprintf(szErrorMessage,"SysAllocString failed");
  293.         goto Err;
  294.     }
  295.     // Find method name
  296.     pwzName = wzMethod;
  297.     hr = pdispObj->GetIDsOfNames(IID_NULL, &pwzName, 1, LOCALE_USER_DEFAULT, &dispidMethod);
  298.     if (FAILED(hr)) {
  299.         char szSysMsg[100];
  300.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,hr,
  301.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),szSysMsg,sizeof(szSysMsg),0);
  302.  
  303.         sprintf(szErrorMessage,"GetIDsOfNames failed with err code %08x: %s",hr,szSysMsg);
  304.         goto Err;
  305.     }
  306.     // Setup parameters
  307.     dispparms.rgvarg = varg;
  308.     dispparms.rgdispidNamedArgs = NULL;
  309.     dispparms.cArgs = 3;
  310.     dispparms.cNamedArgs = 0;
  311.     pdispDoRelease = pdispObj;
  312.  
  313.     // Push in reverse order
  314.     varg[0].vt = VT_I4 | VT_BYREF ;
  315.     varg[0].plVal = (long *)&pdispDoRelease;
  316.     varg[1].vt = VT_BSTR ;
  317.     varg[1].bstrVal = bstrIniFile;
  318.     varg[2].vt = VT_BSTR ;
  319.     varg[2].bstrVal = bstrParams;
  320.  
  321.     // Now make the invocation.
  322.     hr = pdispObj->Invoke(dispidMethod, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD,
  323.                         &dispparms, &vResult, &excep, NULL);
  324.     if (FAILED(hr)) {
  325.         char szSysMsg[100];
  326.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,hr,
  327.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),szSysMsg,sizeof(szSysMsg),0);
  328.  
  329.         sprintf(szErrorMessage,"Invoke failed with err code %08x: %s",hr,szSysMsg);
  330.         goto Err;
  331.     }
  332.     // Release critical section
  333.     fInCritSec = FALSE;
  334.     LeaveCriticalSection(&g_csRuntime);
  335.  
  336.     // Assemble result
  337.  
  338.     buflen = wcslen((unsigned short *)vResult.pbstrVal);
  339.     if (buflen > 0) {
  340.         pszResults = (CHAR*)malloc(buflen + 1);
  341.         if (0 == WideCharToMultiByte(CP_ACP, 0, (unsigned short *)vResult.pbstrVal, -1, pszResults, buflen + 1, NULL, NULL)) {
  342.             sprintf(szErrorMessage,"WideCharToMultiByte failed");
  343.             goto Err;
  344.         }
  345.         // null terminate string
  346.         *(pszResults + buflen) = 0;
  347.     }
  348.     buflen++;
  349.     if (pECB->WriteClient(pECB->ConnID,(void *)pszResults,&buflen,0) == FALSE) {
  350.         char szSysMsg[100];
  351.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,hr,
  352.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),szSysMsg,sizeof(szSysMsg),0);
  353.  
  354.         sprintf(szErrorMessage,"WriteClient failed",szSysMsg);
  355.         goto Err;
  356.     }
  357.  
  358.     fSuccess = TRUE;
  359.     SysFreeString(wzParams);
  360.     SysFreeString(wzIniFile);
  361.     // Always fall through to cleanup
  362. Err:
  363.     DeleteFile(lpszIniFile);    //delete the .INI
  364.     if (fInCritSec) {
  365.         // Release critical section
  366.         LeaveCriticalSection(&g_csRuntime);
  367.     }
  368. /*
  369. For each Web site hit, the OLE server gets instantiated. If the Release() call
  370. below is executed, then the Server gets released after generating
  371. an HTML page. This means the entire VFP runtime will start up and shut down for
  372. each web hit.
  373. If the OLE Custom Server is registered as Multi-Use, and the following Release() 
  374. call is not executed, then the OLE Server will be left in memory with a reference to it,
  375. but subsequent web hits will use the same instance of the server, making performance 
  376. much better.
  377. */
  378.     
  379.     if (pdispObj != NULL) {
  380.         if (pdispDoRelease != 0) {
  381.             pdispObj->Release();  //nonzero, so release the current object
  382.             if (pdispObj != pdispDoRelease) {
  383.                 __try {    //now release the object the OLE object wants to release
  384.                     (pdispDoRelease)->Release();
  385.                 } __except  (EXCEPTION_EXECUTE_HANDLER) {
  386.  
  387.                 }
  388.             }
  389.         }
  390.     }
  391.     VariantClear(&varg[0]);
  392.     VariantClear(&varg[1]);
  393.     VariantClear(&varg[2]);
  394.     VariantClear(&vResult);
  395.     if (NULL != pszResults) {
  396.         free(pszResults);
  397.     }
  398.     return fSuccess;
  399. }
  400.  
  401. void ErrorResponse(EXTENSION_CONTROL_BLOCK *pECB,
  402.                 CHAR *pszProgid, 
  403.                 CHAR *pszMethod)
  404. {
  405.     CHAR pszBuf[BUFLEN];
  406.  
  407.     _snprintf(pszBuf, BUFLEN, "Content-Type: text/html\r\n\r\n<body><h1>"
  408.     "FOXISAPI call failed</h1><p><b>Progid is:</b> %s\n<p><b>Method is:</b> %s\n<p>"
  409.     "<b>Parameters are:</b> %s\n<p><b> parameters are:</b> %.*s\n</b>"
  410.     "<p><b>%s\n</b></body>",
  411.     ((NULL != pszProgid) ? pszProgid : ""),
  412.     ((NULL != pszMethod) ? pszMethod : ""),
  413.     ((NULL != pECB->lpszQueryString) ? pECB->lpszQueryString : ""),
  414.     (pECB->cbAvailable > 0 ? pECB->cbAvailable : 1),
  415.     (pECB->cbAvailable > 0 ? (CHAR*)pECB->lpbData : ""),szErrorMessage);
  416.  
  417.     pECB->ServerSupportFunction(pECB->ConnID,
  418.                             HSE_REQ_SEND_RESPONSE_HEADER,
  419.                 (LPDWORD) "200 Baaaad Request",
  420.                 NULL,
  421.                 (LPDWORD)pszBuf);
  422. }
  423.  
  424. // CREATE_INI_FILE will create a temp INI file and pass it to the Fox automation object as
  425. // the second parameter. This INI file contains extra info available from the
  426. // IIS
  427.  
  428.  
  429. //
  430. // GetVarAndWriteKey obtains an environment variable and saves it
  431. // to the specified key in the profile.  This function cleans
  432. // up the Fill code a lot.
  433. //
  434.  
  435. void GetVarAndWriteKey (EXTENSION_CONTROL_BLOCK *pECB,char *lpszIniFile,
  436.                         LPCTSTR lpszSection,
  437.                         LPCTSTR lpszVar, LPCTSTR lpszKey)
  438.     {
  439.     TCHAR szBuffer[MAX_PATH];
  440.     DWORD dwBufferSize;
  441.     BOOL bReturn;
  442.  
  443.     // Call server to get environment variable
  444.     dwBufferSize = MAX_PATH;
  445.     bReturn = pECB->GetServerVariable (pECB->ConnID,
  446.                                         (LPTSTR) lpszVar,
  447.                                         szBuffer,
  448.                                         &dwBufferSize);
  449.  
  450.     if (!bReturn)
  451.         return;
  452.  
  453.     // Write variable to profile if data exists
  454.     if (szBuffer[0]) {
  455.         WritePrivateProfileString (lpszSection,
  456.                                     lpszKey, 
  457.                                     szBuffer, 
  458.                                     lpszIniFile);
  459.     }
  460. }
  461.  
  462.  
  463. int LogExtraInfo(EXTENSION_CONTROL_BLOCK *pECB,char *lpszIniFile) {
  464.     DWORD dwBufferSize;
  465.     BOOL bReturn;
  466.     TCHAR *pChar, *pOpts, *pEnd;
  467.     TCHAR szBuffer[MAX_PATH];
  468.  
  469.  
  470.     //create a temp INI file name
  471.     GetTempFileName(gszAppDir,
  472.             "FOX",    //prefix
  473.             0,
  474.             lpszIniFile);
  475.     DeleteFile(lpszIniFile);    //delete the .TMP
  476.     *strrchr(lpszIniFile,'.') = '\0';
  477.     strcat(lpszIniFile,".ini");
  478.  
  479.     WritePrivateProfileString ("FOXISAPI","Request Method", pECB->lpszMethod, lpszIniFile);
  480.     WritePrivateProfileString ("FOXISAPI","Query String", pECB->lpszQueryString, lpszIniFile);
  481.     WritePrivateProfileString ("FOXISAPI","Logical Path", pECB->lpszPathInfo, lpszIniFile);
  482.     WritePrivateProfileString ("FOXISAPI","Physical Path", pECB->lpszPathTranslated, lpszIniFile);
  483.     WritePrivateProfileString ("FOXISAPI","FoxISAPI Version", "FoxISAPI v1.0", lpszIniFile);
  484.     //
  485.     // Get server variables and write the values to the profile
  486.     //
  487.  
  488.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  489.                         TEXT("SERVER_PROTOCOL"), TEXT("Request Protocol"));
  490.  
  491.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  492.                         TEXT("SCRIPT_NAME"), TEXT("Referer"));
  493.  
  494.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  495.                         TEXT("SERVER_SOFTWARE"), TEXT("Server Software"));
  496.  
  497.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  498.                         TEXT("SERVER_NAME"), TEXT("Server Name"));
  499.  
  500.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  501.                         TEXT("SERVER_PORT"), TEXT("Server Port"));
  502.  
  503.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  504.                         TEXT("REMOTE_HOST"), TEXT("Remote Host"));
  505.  
  506.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  507.                         TEXT("REMOTE_ADDR"), TEXT("Remote Address"));
  508.  
  509.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  510.                         TEXT("AUTHTEXTYPE"), TEXT("Authentication Method"));
  511.  
  512.     GetVarAndWriteKey (pECB, lpszIniFile, "FOXISAPI",
  513.                         TEXT("REMOTE_USER"), TEXT("Authenticated Username"));
  514.  
  515.  
  516.     // Keys not supported:
  517.     //
  518.     // Executable Path
  519.     // From
  520.     // Server Admin
  521.     // Authentication Realm (goes with Authenticated Username)
  522.  
  523.     // Retrieve ALL_HTTP
  524.  
  525.     dwBufferSize = sizeof (szBuffer);
  526.     bReturn = pECB->GetServerVariable (pECB->ConnID,
  527.                                         TEXT("ALL_HTTP"),
  528.                                         szBuffer,
  529.                                         &dwBufferSize);
  530.  
  531.     if (bReturn) {       // expected symbol is found
  532.         //
  533.         // Find lines, split key/data pair and write them to profile
  534.         //
  535.  
  536.         pChar = szBuffer;
  537.         while (*pChar)  {
  538.             if (*pChar == TEXT('\r') || *pChar == TEXT ('\n'))      {
  539.                 pChar++;
  540.                 continue;
  541.             }
  542.  
  543.             pOpts = strchr (pChar, TEXT(':'));  // look for separator
  544.             if (!pOpts)
  545.                 break;
  546.             if (!*pOpts)
  547.                 break;
  548.  
  549.             pEnd = pOpts;
  550.             while (*pEnd && *pEnd != TEXT('\r') && *pEnd != TEXT('\n'))
  551.                 pEnd++;
  552.  
  553.             *pOpts = 0;     // split strings
  554.             *pEnd = 0;
  555.  
  556.             WritePrivateProfileString ("All_HTTP",pChar, pOpts + 1, lpszIniFile);
  557.     
  558.             pChar = pEnd + 1;
  559.         }
  560.     }
  561.  
  562.  
  563.     
  564.     //
  565.     // Accept section provides info about the client's capabilities.  We use
  566.     // the header information stored in the HTTP_ACCEPT environment variable.
  567.     //
  568.     // The format of this variable is:
  569.     //
  570.     // type/subtype [;opt. parameters] [, type/subtype [;params]] [, ...]
  571.     //
  572.     // For example:
  573.     // */*; q=0.300, audio/x-aiff, audio/basic, image/jpeg, image/gif, text/plain, text/html
  574.     //
  575.     // The above example becomes:
  576.     //
  577.     // [Accept]
  578.     // */*=q=0.300
  579.     // audio/x-aiff=Yes
  580.     // audio/basic=Yes
  581.     // image/jpeg=Yes
  582.     // image/gif=Yes
  583.     // text/plain=Yes
  584.     // text/html=Yes
  585.     //
  586.  
  587.  
  588.     //
  589.     // Get the inbound accept line
  590.     //
  591.  
  592.     dwBufferSize = MAX_PATH;
  593.     bReturn = pECB->GetServerVariable (pECB->ConnID,
  594.                                         TEXT("HTTP_ACCEPT"),
  595.                                         szBuffer,
  596.                                         &dwBufferSize);
  597.  
  598.     
  599.     if (bReturn) {       // expected symbol is found
  600.  
  601.         //
  602.         // Skip leading spaces and grab entire type/subtype[;opts] string
  603.         //
  604.  
  605.         pChar = strtok (szBuffer, TEXT(" ,"));
  606.         while (pChar)  {
  607.             pOpts = strchr (pChar, TEXT(';'));  // look for opts, if any
  608.  
  609.             WritePrivateProfileString ("Accept",pChar, 
  610.                 pOpts == NULL ? TEXT("Yes") : pOpts, lpszIniFile);
  611.  
  612.             pChar = strtok (NULL, TEXT(" ,")); // get next type/subtype pair
  613.         }
  614.     }
  615.  
  616.     //
  617.     // The [System] section must be filled out with GMT Offset
  618.     //
  619.     // GMT offset is the number of seconds added to GMT time to reach local
  620.     // time.  For example, PST = GMT - 8 hours; GMT offset would equal 
  621.     // -28,800.  Win32 call GetTimeZoneInformation returns the number of
  622.     // minutes to subtract from GMT (UTC) to get local time.
  623.     //
  624.     // So, GMT Offset = -60*TZI.Bias.
  625.     //
  626.     TIME_ZONE_INFORMATION tzi = {0};
  627.     GetTimeZoneInformation (&tzi);
  628.     wsprintf(szBuffer,"%i", -60 * tzi.Bias);
  629.     WritePrivateProfileString ("SYSTEM","GMT Offset", szBuffer, lpszIniFile);
  630.     
  631.     return 0;
  632. }
  633.  
  634.  
  635.  
  636. BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
  637. {
  638.     pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );
  639.  
  640.     strncpy(pVer->lpszExtensionDesc,
  641.         "FOX OLE Automation Gateway",
  642.         HSE_MAX_EXT_DLL_NAME_LEN);
  643.     return TRUE;
  644. }
  645.  
  646. DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
  647. {
  648.     CHAR pszPathString[PARAMLEN];
  649.     CHAR *pszProgid = NULL;
  650.     CHAR *pszMethod = NULL;
  651.     CHAR *pszTemp = NULL;
  652.     char lpszIniFile[MAX_PATH];
  653.     int hr;
  654.  
  655. #ifdef _DEBUG
  656.     __try    {    //uncomment the next line for a breakpoint
  657. //        _asm int 3        
  658.     }    
  659.     __except (UnhandledExceptionFilter(GetExceptionInformation())) {
  660.         int i;
  661.         i=5;    //so we can set a breakpoint here
  662.     }
  663. #endif
  664.     if (FALSE == TlsGetValue(g_dwTLSIndex)) {
  665.         OleInitialize(NULL);
  666.         TlsSetValue(g_dwTLSIndex, (void*)TRUE);
  667.     }
  668.  
  669.     // only GET and POST supported
  670.     if (strcmp(pECB->lpszMethod, "GET") && strcmp(pECB->lpszMethod, "POST"))
  671.     {
  672.         return HSE_STATUS_ERROR;
  673.     }
  674.     if (hr = LogExtraInfo(pECB,lpszIniFile))    //assignment
  675.         return hr;
  676.  
  677.     // extract progid & method name from path info
  678.     strncpy(pszPathString, pECB->lpszPathInfo, PARAMLEN);
  679.     if ('/' == *pszPathString) {
  680.         pszProgid = pszPathString + 1;
  681.     }
  682.     pszMethod = strchr(pszPathString, '.');
  683.     if (NULL != pszMethod) {
  684.         // progids can have zero or one periods in them
  685.         pszTemp = strchr(pszMethod + 1, '.');
  686.         // separate progid from args
  687.         if (NULL != pszTemp) {
  688.             *pszTemp = '\0';
  689.             pszMethod = pszTemp + 1;
  690.         } else {
  691.             *pszMethod = '\0';
  692.             pszMethod++;
  693.         }
  694.     }
  695.  
  696.     // startup object, invoke method, and return results
  697.     if (FALSE == CallObject(pECB, pszProgid, pszMethod,lpszIniFile)) {
  698.         ErrorResponse(pECB, pszProgid, pszMethod);
  699.         return HSE_STATUS_ERROR;
  700.     }
  701.     return HSE_STATUS_SUCCESS;
  702. }
  703.  
  704. BOOL WINAPI DllMain(HMODULE hMod, DWORD fReason, LPVOID pvRes)
  705. {
  706.     switch (fReason) {
  707.     case DLL_PROCESS_ATTACH:
  708.         GetModuleFileName(hMod,gszAppDir,sizeof(gszAppDir));
  709.         *strrchr(gszAppDir,'\\') = '\0';
  710.         g_dwTLSIndex = TlsAlloc();
  711.         InitializeCriticalSection(&g_csRuntime);
  712.         if (-1 == g_dwTLSIndex)
  713.               return FALSE;
  714.         break;
  715.  
  716.     case DLL_THREAD_ATTACH:
  717.         // set this flag to true once ole has been initialized
  718.         TlsSetValue(g_dwTLSIndex, FALSE);
  719.         break;
  720.  
  721.     case DLL_PROCESS_DETACH:
  722.         // clean up global resources
  723.         if (-1 != g_dwTLSIndex)
  724.             TlsFree(g_dwTLSIndex);
  725.         DeleteCriticalSection(&g_csRuntime);
  726.         break;
  727.  
  728.     case DLL_THREAD_DETACH:
  729.         break;
  730.     }
  731.     return TRUE;
  732. }