Index: src/include/httpd.h =================================================================== RCS file: /home/cvs/apache-1.3/src/include/httpd.h,v retrieving revision 1.338 diff -u -r1.338 httpd.h --- src/include/httpd.h 2001/02/26 16:59:44 1.338 +++ src/include/httpd.h 2001/05/11 20:14:37 @@ -430,7 +430,7 @@ #define SERVER_BASEVENDOR "Apache Group" #define SERVER_BASEPRODUCT "Apache" -#define SERVER_BASEREVISION "1.3.19" +#define SERVER_BASEREVISION "1.3.19-rev1" #define SERVER_BASEVERSION SERVER_BASEPRODUCT "/" SERVER_BASEREVISION #define SERVER_PRODUCT SERVER_BASEPRODUCT @@ -451,7 +451,7 @@ * Always increases along the same track as the source branch. * For example, Apache 1.4.2 would be '10402100', 2.5b7 would be '20500007'. */ -#define APACHE_RELEASE 10319100 +#define APACHE_RELEASE 10319101 #define SERVER_PROTOCOL "HTTP/1.1" #ifndef SERVER_SUPPORT Index: src/os/os2/util_os2.c =================================================================== RCS file: /home/cvs/apache-1.3/src/os/os2/util_os2.c,v retrieving revision 1.10 diff -u -r1.10 util_os2.c --- src/os/os2/util_os2.c 2000/11/04 07:36:21 1.10 +++ src/os/os2/util_os2.c 2001/05/11 20:15:42 @@ -7,14 +7,14 @@ API_EXPORT(char *)ap_os_case_canonical_filename(pool *pPool, const char *szFile) { - char buf[HUGE_STRING_LEN]; - char buf2[HUGE_STRING_LEN]; + char *buf; + char buf2[CCHMAXPATH]; int rc, len; char *pos; /* Remove trailing slash unless it's a root directory */ - strcpy(buf, szFile); - len = strlen(buf); + len = strlen(szFile); + buf = ap_pstrndup(pPool, szFile, len); if (len > 3 && buf[len-1] == '/') buf[--len] = 0; @@ -26,7 +26,7 @@ } } - rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, HUGE_STRING_LEN); + rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, sizeof(buf2)); if (rc) { if ( rc != ERROR_INVALID_NAME ) { Index: src/os/win32/util_win32.c =================================================================== RCS file: /home/cvs/apache-1.3/src/os/win32/util_win32.c,v retrieving revision 1.42 diff -u -r1.42 util_win32.c --- src/os/win32/util_win32.c 2001/02/14 14:22:11 1.42 +++ src/os/win32/util_win32.c 2001/05/11 20:36:09 @@ -84,140 +84,185 @@ return TRUE; } + /* Accepts as input a pathname, and tries to match it to an * existing path and return the pathname in the case that * is present on the existing path. This routine also * converts alias names to long names. + * + * WARNING: Folding to systemcase fails when /path/to/foo/../bar + * is given and foo does not exist, is not a directory. */ API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool, const char *szFile) { - char buf[HUGE_STRING_LEN]; - char *pInputName; - char *p, *q, *t; + char *buf, *t, *r; + const char *q, *p; BOOL bDone = FALSE; BOOL bFileExists = TRUE; HANDLE hFind; WIN32_FIND_DATA wfd; + size_t buflen; + int slack = 0; - if (!szFile || strlen(szFile) == 0 || strlen(szFile) >= sizeof(buf)) + if (!szFile || strlen(szFile) == 0) return ap_pstrdup(pPool, ""); - t = buf; - pInputName = ap_pstrdup(pPool, szFile); + buflen = strlen(szFile); + t = buf = ap_palloc(pPool, buflen + 1); + q = szFile; - /* First convert all slashes to \ so Win32 calls work OK */ - for (p = pInputName; *p; p++) { - if (*p == '/') - *p = '\\'; - } - - q = p = pInputName; /* If there is drive information, copy it over. */ - if (pInputName[1] == ':') { - /* This is correct - if systemcase is used for + if (szFile[1] == ':') { + /* Lowercase, so that when systemcase is used for * comparison, d: designations will match - */ - *(t++) = tolower(*p++); - *(t++) = *p++; - q = p; - - /* If all we have is a drive letter, then we are done */ - if (!*p) - bDone = TRUE; - } - - if (*p == '\\') { - ++p; - if (*p == '\\') /* UNC name */ + */ + *(t++) = tolower(*(q++)); + *(t++) = *(q++); + } + else if ((*q == '/') || (*q == '\\')) { + /* Get past the root path (/ or //foo/bar/) so we can go + * on to normalize individual path elements. + */ + *(t++) = '\\', ++q; + if ((*q == '/') || (*q == '\\')) /* UNC name */ { - /* Get past the machine name. FindFirstFile */ - /* will not find a machine name only */ - *(t++) = '\\'; - ++q; - p = strchr(p + 1, '\\'); - if (p) + /* Lower-case the machine name, so compares match. + * FindFirstFile won't parse \\machine alone + */ + *(t++) = '\\', ++q; + for (p = q; *p && (*p != '/') && (*p != '\\'); ++p) + /* continue */ ; + if (*p || p > q) { - p++; - /* Get past the share name. FindFirstFile */ - /* will not find a \\machine\share name only */ - p = strchr(p, '\\'); - if (p) { - /* This was faulty - as of 1.3.13 \\machine\share - * name is now always lowercased - */ - strncpy(t,q,p-q); - strlwr(t); - t += p - q; - q = p; - p++; + /* Lower-case the machine name, so compares match. + * FindFirstFile won't parse \\machine\share alone + */ + memcpy(t, q, p - q); + t[p - q] = '\0'; + strlwr(t); + t += p - q; + q = p; + if (*p) { + *(t++) = '\\', ++q; + for (p = q; *p && (*p != '/') && (*p != '\\'); ++p) + /* continue */ ; + if (*p || p > q) + { + /* Copy the lower-cased share name. FindFirstFile + * cannot not find a \\machine\share name only + */ + memcpy(t, q, p - q); + t[p - q] = '\0'; + strlwr(t); + t += p - q; + q = p; + if (*p) + *(t++) = '\\', ++q; + else + bFileExists = FALSE; + } + else + bFileExists = FALSE; } + else + bFileExists = FALSE; } - - if (!p) { + else bFileExists = FALSE; - p = q; - } } } - p = strchr(p, '\\'); + while (bFileExists) { - while (!bDone) { - if (p) - *p = '\0'; + /* parse past any leading slashes */ + for (; (*q == '/') || (*q == '\\'); ++q) + *(t++) = '\\'; - if (strchr(q, '*') || strchr(q, '?')) - bFileExists = FALSE; + /* break on end of string */ + if (!*q) + break; + + /* get to the end of this path segment */ + for (p = q; *p && (*p != '/') && (*p != '\\'); ++p) + /* continue */ ; + + /* copy the segment */ + memcpy(t, q, p - q); + t[p - q] = '\0'; + + /* Test for nasties that can exhibit undesired effects */ + if (strpbrk(t, "?\"<>*|:")) { + t += p - q; + q = p; + break; + } /* If the path exists so far, call FindFirstFile * again. However, if this portion of the path contains * only '.' charaters, skip the call to FindFirstFile * since it will convert '.' and '..' to actual names. - * Note: in the call to OnlyDots, we may have to skip - * a leading slash. + * On win32, '...' is an alias for '..', so we gain + * a bit of slack. */ - if (bFileExists && !OnlyDots((*q == '.' ? q : q+1))) { - hFind = FindFirstFile(pInputName, &wfd); - - if (hFind == INVALID_HANDLE_VALUE) { - bFileExists = FALSE; + if (*t == '.' && OnlyDots(t)) { + if (p - q == 3) { + t += 2; + q = p; + ++slack; } else { - FindClose(hFind); - - if (*q == '\\') - *(t++) = '\\'; - t = strchr(strcpy(t, wfd.cFileName), '\0'); + t += p - q; + q = p; } + /* Paths of 4 dots or more are invalid */ + if (p - q > 3) + break; } - - if (!bFileExists || OnlyDots((*q == '.' ? q : q+1))) { - /* XXX: Comparison could be faulty ...\unknown - * names may not match! - */ - strcpy(t, q); - t = strchr(t, '\0'); - } - - if (p) { - q = p; - *p++ = '\\'; - p = strchr(p, '\\'); - } else { - bDone = TRUE; + if ((hFind = FindFirstFile(buf, &wfd)) == INVALID_HANDLE_VALUE) { + t += p - q; + q = p; + break; + } + else { + size_t fnlen = strlen(wfd.cFileName); + FindClose(hFind); + /* the string length just changed, could have shrunk + * (trailing spaces or dots) or could have grown + * (longer filename aliases). Realloc as necessary + */ + slack -= fnlen - (p - q); + if (slack < 0) { + char *n; + slack += buflen + fnlen - (p - q); + buflen += buflen + fnlen - (p - q); + n = ap_palloc(pPool, buflen + 1); + memcpy (n, buf, t - buf); + t = n + (t - buf); + buf = n; + } + memcpy(t, wfd.cFileName, fnlen); + t += fnlen; + q = p; + if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + break; + } } } - *t = '\0'; - - /* Finally, convert all slashes to / so server code handles it ok */ - for (p = buf; *p; p++) { - if (*p == '\\') - *p = '/'; + + /* Convert all parsed '\'s to '/' for canonical form (doesn't touch + * the non-existant portion of the path whatsoever.) + */ + for (r = buf; r < t; ++r) { + if (*r == '\\') + *r = '/'; } - return ap_pstrdup(pPool, buf); + /* Copy the non-existant portion (minimally nul-terminates the string) */ + strcpy(t, q); + + return buf; } @@ -294,56 +339,15 @@ * simply truncated, with no embedded '~'. Further, this behavior * can be modified on WinNT volumes. This was not a safe test, * therefore exclude the '~' pretest. - */ + */ #ifdef WIN32_SHORT_FILENAME_INSECURE_BEHAVIOR p = strchr(pNewStr, '~'); if (p != NULL) #endif - { - char *pConvertedName, *pQstr, *pPstr; - char buf[HUGE_STRING_LEN]; - /* We potentially have a short name. Call - * ap_os_systemcase_filename to examine the filesystem - * and possibly extract the long name. - */ - pConvertedName = ap_os_systemcase_filename(pPool, pNewStr); - - /* Since we want to preserve the incoming case as much - * as we can, compare for differences in the string and - * only substitute in the path names that changed. - */ - if (stricmp(pNewStr, pConvertedName)) { - buf[0] = '\0'; - - q = pQstr = pConvertedName; - p = pPstr = pNewStr; - do { - q = strchr(q,'/'); - p = strchr(p,'/'); - - if (p != NULL) { - *q = '\0'; - *p = '\0'; - } - - if (stricmp(pQstr, pPstr)) - strcat(buf, pQstr); /* Converted name */ - else - strcat(buf, pPstr); /* Original name */ - - - if (p != NULL) { - pQstr = q; - pPstr = p; - *q++ = '/'; - *p++ = '/'; - } - - } while (p != NULL); - - pNewStr = ap_pstrdup(pPool, buf); - } - } + /* ap_os_systemcase_filename now changes the case of only + * the pathname elements that are found. + */ + pNewStr = ap_os_systemcase_filename(pPool, pNewStr); return pNewStr; }