//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 2000. // // File: V R O O T S . C P P // // Contents: Implements the virtual root system for the HTTP server // // Notes: // // Author: danielwe 2000/11/6 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "httpd.h" #include "ncreg.h" // Used to split a URL and Path Translated apart for ISAPI/ASP scripts inline void SetPathInfo(PSTR *ppszPathInfo,PSTR pszInputURL,int iURLLen) { int iLen = strlen(pszInputURL+iURLLen) + 2; // If we've mapped the virtual root "/" to a script, need an extra "/" for the path // (normally we use the origial trailing "/", but in this case the "/" is the URL // BUGBUG: Probably should rewrite the Virtual roots parsing // mechanism so that it's cleaner. *ppszPathInfo = MySzAllocA((iURLLen == 1) ? iLen + 1 : iLen); if (! (*ppszPathInfo)) goto done; if (iURLLen == 1) { (*ppszPathInfo)[0] = '/'; memcpy( (*ppszPathInfo) +1, pszInputURL + iURLLen, iLen); } else memcpy(*ppszPathInfo, pszInputURL + iURLLen, iLen); done: // URL shouldn't contain path info, break it apart pszInputURL[iURLLen] = 0; } PVROOTINFO CVRoots::MatchVRoot(PCSTR pszInputURL, int iInputLen) { int i, iMatch; // If there was an error on setting up the vroots, m_pVRoots = NULL. if (!m_pVRoots) return NULL; for(i=0, iMatch=-1; i= iLen) { if(0 == _memicmp(pszInputURL, m_pVRoots[i].pszURL, iLen)) { // If it's not root dir, always matched. Otherwise it's possible // there wasn't a match. For root dirs, pszURL[iLen] is always "/" if (!m_pVRoots[i].bRootDir || m_pVRoots[i].iURLLen == 1 || pszInputURL[iLen] == '/' || pszInputURL[iLen] == '\0') { TraceTag(ttidWebServer, "URL %s matched VRoot %s (path %S, perm=%d, auth=%d)", pszInputURL, m_pVRoots[i].pszURL, m_pVRoots[i].wszPath, m_pVRoots[i].dwPermissions, m_pVRoots[i].AuthLevel); return &(m_pVRoots[i]); } } } } TraceTag(ttidWebServer, "URL %s did not matched any VRoot", pszInputURL); return NULL; } BOOL CVRoots::FillVRoot(PVROOTINFO pvr, LPWSTR wszURL, LPWSTR wszPath) { int err = 0; // err variable is used in non-Debug mode const char cszDLL[] = ".dll"; const char cszASP[] = ".asp"; CHAR pszURL[MAX_PATH+1]; CHAR pszPath[MAX_PATH+1]; // convert URL to MBCS int iLen = pvr->iURLLen = MyW2A(wszURL, pszURL, sizeof(pszURL)); if(!iLen) { myleave(83); } pvr->iURLLen--; // -1 for null-term pvr->iPathLen = wcslen(wszPath); MyW2A(wszPath, pszPath, sizeof(pszPath)); // check to see if Vroot ends in .dll or .asp, in this case we send // client not to the directory but to the script page. if (pvr->iPathLen >= sizeof(cszDLL) && 0 == strcmpi(pszPath + pvr->iPathLen - sizeof(cszDLL) +1,cszDLL)) { pvr->ScriptType = SCRIPT_TYPE_EXTENSION; } else if (pvr->iPathLen >= sizeof(cszASP) && 0 == strcmpi(pszPath + pvr->iPathLen - sizeof(cszASP) +1,cszASP)) { pvr->ScriptType = SCRIPT_TYPE_ASP; } else { pvr->ScriptType = SCRIPT_TYPE_NONE; } // If one of URL or path ends in a slash, the other must too. // If either the URL ends in a "/" or when the path ends in "\", we remove // the extra symbol. However, in the case where either URL or path is // root we don't do this. if (pvr->iURLLen != 1 && pszURL[pvr->iURLLen-1]=='/') { pszURL[pvr->iURLLen-1] = L'\0'; pvr->iURLLen--; } else if (pvr->iURLLen == 1 && pszURL[0]=='/' && pvr->ScriptType == SCRIPT_TYPE_NONE) { // if it's the root URL, make sure correspinding path ends with "\" // (if it's a directory only, leave ASP + ISAPI's alone) if (wszPath[pvr->iPathLen-1] != L'\\') { wszPath[pvr->iPathLen] = L'\\'; pvr->iPathLen++; wszPath[pvr->iPathLen] = L'\0'; } } // If Path ends in "\" (and it's not the root path or root virtual root) // remove the "\" if (pvr->iURLLen != 1 && pvr->iPathLen != 1 && wszPath[pvr->iPathLen-1]==L'\\') { wszPath[pvr->iPathLen-1] = L'\0'; pvr->iPathLen--; } else if (pvr->iPathLen == 1 && wszPath[0]==L'\\') { // Trailing "/" must match "\". However, we need a slight HACK to make this work if (pszURL[pvr->iURLLen-1] != '/') { pszURL[pvr->iURLLen] = '/'; pvr->iURLLen++; pszURL[pvr->iURLLen] = '\0'; } pvr->bRootDir = TRUE; } pvr->pszURL = MySzDupA(pszURL); pvr->wszPath = MySzDupW(wszPath); // Fill in defaults for these pvr->wszUserList = NULL; pvr->dwPermissions = HTTP_DEFAULTP_PERMISSIONS; pvr->AuthLevel = AUTH_PUBLIC; TraceTag(ttidWebServer, "VROOT: (%s)=>(%s) perm=%d auth=%d ScriptType=%d", pvr->pszURL, pvr->wszPath, pvr->dwPermissions, pvr->AuthLevel,pvr->ScriptType); done: if(err) { return FALSE; } return TRUE; } VOID CVRoots::Sort() { BOOL fChange; int i=0; // We now want to sort the VRoots in descending order of URL-length so // that when we match we'll find the longest match first!! // Using a slow bubble-sort :-( do { fChange = FALSE; for(i=0; iiPathLen + (iInputLen - pVRoot->iURLLen); PWSTR wszOutPath = MyRgAllocNZ(WCHAR, iOutLen); if (!wszOutPath) { LeaveCriticalSection(&m_csVroot); return NULL; } // assemble the path. First, the mapped base path memcpy(wszOutPath, pVRoot->wszPath, sizeof(WCHAR)*pVRoot->iPathLen); if(pdwPerm) *pdwPerm = pVRoot->dwPermissions; if(pAuthLevel) *pAuthLevel = pVRoot->AuthLevel; if (pScriptType) *pScriptType = pVRoot->ScriptType; if (ppwszUserList) *ppwszUserList = pVRoot->wszUserList; // If the vroot specifies an ISAPI dll or ASP page don't copy path info over. if (pVRoot->ScriptType != SCRIPT_TYPE_NONE) { if ( ppszPathInfo && pszInputURL[pVRoot->iURLLen] != 0) { SetPathInfo(ppszPathInfo,pszInputURL,pVRoot->iURLLen); } wszOutPath[pVRoot->iPathLen] = L'\0'; LeaveCriticalSection(&m_csVroot); return wszOutPath; } // next the remainder of the URL, converted to wide if (iOutLen-pVRoot->iPathLen == 0) { wszOutPath[pVRoot->iPathLen] = L'\0'; } else { int iRet = MyA2W(pszInputURL+pVRoot->iURLLen, wszOutPath+pVRoot->iPathLen, iOutLen-pVRoot->iPathLen); DEBUGCHK(iRet); } LeaveCriticalSection(&m_csVroot); // Flip forward slashes around to backslashes LPWSTR pchFlip = wszOutPath; while (*pchFlip) { if (*pchFlip == '/') { *pchFlip = '\\'; } pchFlip++; } return wszOutPath; } BOOL CVRoots::LoadExtensionMap(PVROOTINFO pvr, HKEY rootreg) { BOOL bWildCard; WCHAR wszExt[MAX_PATH+1]; WCHAR wszPath[MAX_PATH+1]; CReg mapreg(rootreg, RV_EXTENSIONMAP); if (!mapreg.IsOK()) { pvr->nExtensions = 0; return FALSE; } if (bWildCard = mapreg.ValueSZ(NULL, wszPath, CCHSIZEOF(wszPath))) pvr->nExtensions = 1; else pvr->nExtensions = mapreg.NumValues(); if ((pvr->pExtMap = MyRgAllocZ(EXTMAP, pvr->nExtensions)) == NULL) return FALSE; if (bWildCard) { pvr->pExtMap[0].wszExt = MySzDupW(L"*"); pvr->pExtMap[0].wszPath = MySzDupW(wszPath); return TRUE; } for (int i = 0; i < pvr->nExtensions; i++) { if (mapreg.EnumValue(wszExt, CCHSIZEOF(wszExt), wszPath, CCHSIZEOF(wszPath))) { if (wszExt[0] != 0 && wszPath[0] != 0) { pvr->pExtMap[i].wszExt = MySzDupW(wszExt); pvr->pExtMap[i].wszPath = MySzDupW(wszPath); } else { pvr->pExtMap[i].wszExt = MySzDupW(L""); pvr->pExtMap[i].wszPath = MySzDupW(L""); } } } return TRUE; } void CVRoots::FreeExtensionMap (PVROOTINFO pvr) { if (pvr->pExtMap == NULL) return; for (int i=0; i < pvr->nExtensions; i++) { MyFree(pvr->pExtMap[i].wszExt); MyFree(pvr->pExtMap[i].wszPath); } MyFree(pvr->pExtMap); pvr->pExtMap = NULL; } PWSTR CVRoots::MapExtToPath (PSTR pszInputURL, PSTR *ppszEndOfURL) { PSTR pszTemp, pszStart, pszEnd; PWSTR wszPath = NULL; WCHAR wszExt[MAX_PATH+1]; PVROOTINFO pvr; int iInputLen = strlen(pszInputURL); if (!FindExtInURL (pszInputURL, &pszStart, &pszEnd)) return NULL; MyA2W (pszStart, wszExt, (int)((INT_PTR)(pszEnd - pszStart))); wszExt [pszEnd - pszStart] = L'\0'; EnterCriticalSection(&m_csVroot); if (!(pvr = MatchVRoot(pszInputURL, iInputLen))) { LeaveCriticalSection(&m_csVroot); return NULL; } for (int i = 0; i < pvr->nExtensions; i++) { if ((i == 0 && pvr->pExtMap[i].wszExt[0] == L'*') || 0 == _wcsicmp (pvr->pExtMap[i].wszExt, wszExt)) { wszPath = pvr->pExtMap[i].wszPath; if (ppszEndOfURL != NULL) *ppszEndOfURL = pszEnd; break; } } LeaveCriticalSection(&m_csVroot); return wszPath; } BOOL CVRoots::FindExtInURL (PSTR pszInputURL, PSTR *ppszStart, PSTR *ppszEnd) { *ppszStart = strrchr (pszInputURL, '.'); if ((*ppszEnd = *ppszStart) == NULL) return FALSE; while (**ppszEnd != '/' && **ppszEnd != '\0') *ppszEnd = *ppszEnd + 1; return TRUE; }