Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1123 lines
28 KiB

/*
* C N V T . C P P
*
* Data conversion routines
*
* Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
*/
#include "_xmllib.h"
#include <string.h>
#include <stdio.h>
// Month names ---------------------------------------------------------------
//
DEC_CONST LPCWSTR c_rgwszMonthNames[] =
{
L"Jan",
L"Feb",
L"Mar",
L"Apr",
L"May",
L"Jun",
L"Jul",
L"Aug",
L"Sep",
L"Oct",
L"Nov",
L"Dec",
};
DEC_CONST ULONG c_cMonthNames = CElems(c_rgwszMonthNames);
DEC_CONST ULONG c_cchMonthName = 3;
DEC_CONST LPCWSTR c_rgwszDayNames[] =
{
L"Sun",
L"Mon",
L"Tue",
L"Wed",
L"Thu",
L"Fri",
L"Sat",
};
DEC_CONST UINT c_cDayNames = CElems(c_rgwszDayNames);
DEC_CONST UINT c_cchDayName = 3;
// Date formats --------------------------------------------------------------
//
DEC_CONST WCHAR gc_wszIso8601_min[] = L"yyyy-mm-ddThh:mm:ssZ";
DEC_CONST UINT gc_cchIso8601_min = CchConstString(gc_wszIso8601_min);
DEC_CONST WCHAR gc_wszIso8601_scanfmt[] = L"%04hu-%02hu-%02huT%02hu:%02hu:%02hu";
DEC_CONST WCHAR gc_wszIso8601_tz_scanfmt[] = L"%02hu:%02hu";
DEC_CONST WCHAR gc_wszIso8601_fmt[] = L"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ";
DEC_CONST WCHAR gc_wszRfc1123_min[] = L"www, dd mmm yyyy hh:mm:ss GMT";
DEC_CONST UINT gc_cchRfc1123_min = CchConstString (gc_wszRfc1123_min);
DEC_CONST WCHAR gc_wszRfc1123_fmt[] = L"%ls, %02d %ls %04d %02d:%02d:%02d GMT";
enum {
tf_year,
tf_month,
tf_day,
tf_hour,
tf_minute,
tf_second,
cTimeFields,
tz_hour = 0,
tz_minute,
cTzDeltaFields,
RADIX_BASE = 10,
};
// Conversion functions ------------------------------------------------------
//
/*
* CchFindChar
*
* Look for the given char, obeying the cbMax limit.
* If the char is not found, return INVALID_INDEX.
*/
UINT __fastcall
CchFindChar(WCHAR wch, LPCWSTR pwszData, UINT cchMax)
{
UINT cchParsed = 0;
while (cchParsed < cchMax &&
wch != *pwszData)
{
cchParsed++;
pwszData++;
}
if (cchParsed == cchMax)
cchParsed = INVALID_INDEX;
return cchParsed;
}
/*
* CchSkipWhitespace
*
* Skips whitespace, obeying the cbMax limit.
* Returns the number of bytes parsed.
*/
UINT __fastcall
CchSkipWhitespace(LPCWSTR pwszData, UINT cchMax)
{
UINT cchParsed = 0;
while (cchParsed < cchMax &&
(L' ' == *pwszData ||
L'\t' == *pwszData ||
L'\n' == *pwszData ||
L'\r' == *pwszData))
{
cchParsed++;
pwszData++;
}
return cchParsed;
}
LONG __fastcall
LNumberFromParam(LPCWSTR pwszData, UINT cchMax)
{
LONG lReturn = 0;
UINT cchCurrent = 0;
BOOL fNegative = FALSE;
if (0 < cchMax)
{
// Get any sign char.
//
if (L'-' == *pwszData)
{
// Set the negative flag to true.
//
fNegative = TRUE;
// Skip this valid character.
//
cchCurrent++;
// Skip any whitespace.
//
cchCurrent += CchSkipWhitespace(&pwszData[1], cchMax - 1);
}
else if (L'+' == *pwszData)
{
// Skip any whitespace.
//
cchCurrent += CchSkipWhitespace(&pwszData[1], cchMax - 1);
}
}
// From here, any non-number chars are invalid & mean we
// should stop parsing.
// Get the magnitude of the number.
//
while (cchCurrent < cchMax)
{
if (L'0' <= static_cast<USHORT>(pwszData[cchCurrent]) &&
L'9' >= static_cast<USHORT>(pwszData[cchCurrent]))
{
lReturn *= 10;
lReturn += (pwszData[cchCurrent] - L'0');
}
else
{
// Not a number char. Time to quit parsing.
//
break;
}
// Move to the next char.
//
cchCurrent++;
}
// Apply the negative sign, if any.
//
if (fNegative)
lReturn = (0 - lReturn);
return lReturn;
}
HRESULT __fastcall
HrHTTPDateToFileTime(LPCWSTR pwszDate,
FILETIME * pft)
{
HRESULT hr;
SYSTEMTIME systime;
UINT cchDate;
// Make sure we were passed something as a date string.
//
Assert(pwszDate);
Assert(pft);
// Zero out the structure.
//
memset(&systime, 0, sizeof(SYSTEMTIME));
// Get the length of the date string.
//
cchDate = static_cast<UINT>(wcslen(pwszDate));
// Get the date and time pieces. If either fails, return its
// error code. Otherwise, convert to a file time at the end,
// return E_FAIL if the conversion fails, S_OK otherwise.
//
hr = GetFileDateFromParam(pwszDate,
cchDate,
&systime);
if (FAILED(hr))
return hr;
hr = GetFileTimeFromParam(pwszDate,
cchDate,
&systime);
if (FAILED(hr))
return hr;
if (!SystemTimeToFileTime(&systime, pft))
return E_FAIL;
return S_OK;
}
HRESULT __fastcall
GetFileDateFromParam (LPCWSTR pwszData,
UINT cchTotal,
SYSTEMTIME * psystime)
{
LPCWSTR pwszCurrent;
UINT cchLeft;
UINT cchTemp;
Assert(pwszData);
Assert(psystime);
// Skip leading whitespace.
//
cchTemp = CchSkipWhitespace(pwszData, cchTotal);
pwszCurrent = pwszData + cchTemp;
cchLeft = cchTotal - cchTemp;
// If we've hit the end of our buffer already, this was an invalid date
// string.
//
if (0 == cchLeft)
return E_FAIL;
// If the first char's of the date are ddd, then the day of the
// week is a part of the date, and we really do not care.
//
if (L'9' < static_cast<USHORT>(*pwszCurrent))
{
// Find the day
//
UINT uiDay;
for (uiDay = 0; uiDay < c_cDayNames; uiDay++)
{
// Compare the month names.
//
if (*pwszCurrent == *(c_rgwszDayNames[uiDay]) &&
(c_cchDayName <= cchLeft) &&
!_wcsnicmp(pwszCurrent, c_rgwszDayNames[uiDay], c_cchDayName))
{
// Found the right month. This index tells us the month number.
//
psystime->wDayOfWeek = static_cast<WORD>(uiDay); // Sunday is 0
break;
}
}
if (uiDay == c_cDayNames)
return E_FAIL;
// Look for our space delimiter.
//
cchTemp = CchFindChar(L' ', pwszCurrent, cchLeft);
if (INVALID_INDEX == cchTemp)
{
// Invalid format to this data. Fail here.
//
return E_FAIL;
}
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// CchFindChar will return INVALID_INDEX if we hit the end of the
// string, so we can assert that we have more space in the string.
//
Assert(0 < cchLeft);
// Again, skip whitespace.
//
cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
}
// The date format is dd month yyyy. Anything else is invalid.
// Get the day-of-the-month number.
//
psystime->wDay = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
// Look for our space delimiter.
//
cchTemp = CchFindChar(L' ', pwszCurrent, cchLeft);
if (INVALID_INDEX == cchTemp)
{
// Invalid format to this data. Fail here.
//
return E_FAIL;
}
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// CchFindChar will return INVALID_INDEX if we hit the end of the
// string, so we can assert that we have more space in the string.
//
Assert(0 < cchLeft);
// Again, skip whitespace.
//
cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Find the month number.
//
for (UINT uiMonth = 0; uiMonth < c_cMonthNames; uiMonth++)
{
// Compare the month names.
//
if (*pwszCurrent == *(c_rgwszMonthNames[uiMonth]) &&
(c_cchMonthName <= cchLeft) &&
!_wcsnicmp(pwszCurrent, c_rgwszMonthNames[uiMonth], c_cchMonthName))
{
// Found the right month. This index tells us the month number.
//
psystime->wMonth = static_cast<WORD>(uiMonth + 1); // January is 1.
break;
}
}
// Look for our space delimiter.
//
cchTemp = CchFindChar(L' ', pwszCurrent, cchLeft);
if (INVALID_INDEX == cchTemp)
{
// Invalid format to this data. Fail here.
//
return E_FAIL;
}
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// CchFindChar will return INVALID_INDEX if we hit the end of the
// string, so we can assert that we have more space in the string.
//
Assert(0 < cchLeft);
// Again, skip whitespace.
//
cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Now get the year.
//
psystime->wYear = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
return S_OK;
}
HRESULT __fastcall
GetFileTimeFromParam (LPCWSTR pwszData,
UINT cchTotal,
SYSTEMTIME * psystime)
{
LPCWSTR pwszCurrent;
UINT cchLeft;
UINT cchTemp;
Assert(pwszData);
Assert(psystime);
// Skip leading whitespace.
//
cchTemp = CchSkipWhitespace(pwszData, cchTotal);
pwszCurrent = pwszData + cchTemp;
cchLeft = cchTotal - cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Skip any date information. This could get called for date-time params!
// Look for the first colon delimiter. Yes, we assume no colons in date info!
//
cchTemp = CchFindChar(L':', pwszCurrent, cchLeft);
if (INVALID_INDEX == cchTemp)
{
// No time info available. Fail here.
//
return E_FAIL;
}
// Make sure we've got room to back up
//
if (2 > cchTemp)
{
return E_FAIL;
}
cchTemp--; // Back up to get the hours digits.
cchTemp--;
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// CchFindChar will return INVALID_INDEX if we hit the end of the
// string, so we can assert that we have at least two digits plus a
// ':' still in the string.
//
Assert(2 < cchLeft);
// Skip whitespace (in case the parm is h:mm:ss).
//
cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Time format is hh:mm:ss UT, GMT, +- hh:mm, anything else is invalid.
// (Actually, we allow [h]h:mm[:ss], and whitespace around the colons.)
// Get the hours.
//
psystime->wHour = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
// Look for our colon delimiter.
//
cchTemp = CchFindChar(L':', pwszCurrent, cchLeft);
if (INVALID_INDEX == cchTemp)
{
// No minutes specified. This is not allowed. Fail here.
//
return E_FAIL;
}
cchTemp++; // Skip the found character also.
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Again, skip whitespace.
//
cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Get the minutes.
//
psystime->wMinute = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
// NOTE: The seconds are optional. Don't fail here!
// Look for our colon delimiter.
//
cchTemp = CchFindChar(L':', pwszCurrent, cchLeft);
if (INVALID_INDEX == cchTemp)
{
// No seconds specified. This is allowed. Return success.
//
return S_OK;
}
cchTemp++; // Skip the found character also.
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Again, skip whitespace.
//
cchTemp = CchSkipWhitespace(pwszCurrent, cchLeft);
pwszCurrent += cchTemp;
cchLeft -= cchTemp;
// If we've hit the end of our buffer already, this was an invalid
// date string.
//
if (0 == cchLeft)
return E_FAIL;
// Get the seconds, if any.
//
psystime->wSecond = static_cast<WORD>(LNumberFromParam(pwszCurrent, cchLeft));
// LATER: Get the timezone spec from the line and shift this data into our timezone...
return S_OK;
}
BOOL __fastcall
FGetSystimeFromDateIso8601(LPCWSTR pwszDate, SYSTEMTIME * psystime)
{
UINT i;
// Iso8601 is a fixed digit format: "yyyy-mm-ddThh:mm:ssZ"
// we require the date strings has at least the required
// chars (we allow for the ommission of the fractional
// seconds, and the time delta), otherwise it is an error.
//
if (gc_cchIso8601_min > static_cast<UINT>(wcslen(pwszDate)))
{
DebugTrace ("Dav: date length < than minimal\n");
return FALSE;
}
// Scan the first bit of date information up to the
// optional bits
//
psystime->wMilliseconds = 0;
if (cTimeFields != swscanf (pwszDate,
gc_wszIso8601_scanfmt,
&psystime->wYear,
&psystime->wMonth,
&psystime->wDay,
&psystime->wHour,
&psystime->wMinute,
&psystime->wSecond))
{
DebugTrace ("Dav: minimal scan failed\n");
return FALSE;
}
// Take a look at what is next and process accordingly.
//
// ('Z'), ('.'), ('+') and ('-').
//
// The ('Z') element signifies ZULU time and completes
// the time string. The ('.') element signifies that a
// fractional second value follows. And either a ('+')
// or ('-') element indicates that a timezone delta will
// follow.
//
i = gc_cchIso8601_min - 1;
if (pwszDate[i] == L'Z')
goto ret;
else if (pwszDate[i] == L'.')
goto frac_sec;
else if ((pwszDate[i] == L'+') || (pwszDate[i] == L'+'))
goto tz_delta;
DebugTrace ("Dav: minimal date not terminated properly\n");
return FALSE;
frac_sec:
Assert (pwszDate[i] == L'.');
{
UINT iFrac;
for (iFrac = ++i; pwszDate[i]; i++)
{
// Any non-digit terminates the fractional seconds time
//
if ((pwszDate[i] > L'9') || (pwszDate[i] < L'0'))
{
// At this point, we are expecting ('Z') or a timezone
// delta ('+') or ('-')
//
if (pwszDate[i] == L'Z')
goto ret;
else if ((pwszDate[i] == L'+') || (pwszDate[i] == L'-'))
goto tz_delta;
break;
}
// It turns out, our granularity is only milliseconds, so
// we cannot keep any better precision than that. However,
// we can round the last digit, so at best we will process
// the next four digits
//
if (i - iFrac < 3)
{
// As many digits remain, comprise the fractional
//
psystime->wMilliseconds = static_cast<WORD>(
psystime->wMilliseconds * RADIX_BASE + (pwszDate[i]-L'0'));
}
else if (i - iFrac < 4)
{
// Our granularity is only milliseconds, so we cannot keep
// any better precision than that. However, we can round this
// digit.
//
psystime->wMilliseconds = static_cast<WORD>(
psystime->wMilliseconds + (((pwszDate[i]-L'0')>4)?1:0));
}
}
// We ran out of string before the time was terminated
//
return FALSE;
}
tz_delta:
Assert ((pwszDate[i] == L'+') || (pwszDate[i] == L'-'));
{
WORD wHr;
WORD wMin;
__int64 tm;
__int64 tzDelta;
static const __int64 sc_i64Min = 600000000;
static const __int64 sc_i64Hr = 36000000000;
FILETIME ft;
// Find the time delta in terms of FILETIME units
//
if (cTzDeltaFields != swscanf (pwszDate + i + 1,
gc_wszIso8601_tz_scanfmt,
&wHr,
&wMin))
{
DebugTrace ("Dav: tz delta scan failed\n");
return FALSE;
}
tzDelta = (sc_i64Hr * wHr) + (sc_i64Min * wMin);
// Convert the time into a FILETIME, and stuff it into
// a 64bit integer
//
if (!SystemTimeToFileTime (psystime, &ft))
{
DebugTrace ("Dav: invalid time specified\n");
return FALSE;
}
tm = FileTimeCastToI64(ft);
// Apply the delta
//
if (pwszDate[i] == L'+')
tm = tm + tzDelta;
else
{
Assert (pwszDate[i] == L'-');
tm = tm - tzDelta;
}
// Return the value converted back into a SYSTEMTIME
//
ft = I64CastToFileTime(tm);
if (!FileTimeToSystemTime (&ft, psystime))
{
DebugTrace ("Dav: delta invalidated time\n");
return FALSE;
}
}
ret:
return TRUE;
}
BOOL __fastcall
FGetDateIso8601FromSystime(SYSTEMTIME * psystime, LPWSTR pwszDate, UINT cchSize)
{
// If there is not enough space...
//
if (gc_cchIso8601_min >= cchSize)
return FALSE;
// Format it and return...
//
return (!!wsprintfW (pwszDate,
gc_wszIso8601_fmt,
psystime->wYear,
psystime->wMonth,
psystime->wDay,
psystime->wHour,
psystime->wMinute,
psystime->wSecond,
psystime->wMilliseconds));
}
BOOL __fastcall
FGetDateRfc1123FromSystime (SYSTEMTIME * psystime, LPWSTR pwszDate, UINT cchSize)
{
// If there is not enough space...
//
if (gc_cchRfc1123_min >= cchSize)
return FALSE;
// If wDayOfWeek (Sun-Sat: 0-7) or wMonth (Jan-Dec: 1-12) is out of range,
// we'll fail here (to protect our const array lookups below).
// Note: psystime->wMonth is unsigned, so if there's an invalid value
// of 0, then we'll still catch it.
//
if (c_cDayNames <= psystime->wDayOfWeek)
return FALSE;
if (c_cMonthNames <= (psystime->wMonth - 1))
return FALSE;
// Format it and return...
//
return (!!wsprintfW (pwszDate,
gc_wszRfc1123_fmt,
c_rgwszDayNames[psystime->wDayOfWeek],
psystime->wDay,
c_rgwszMonthNames[psystime->wMonth - 1],
psystime->wYear,
psystime->wHour,
psystime->wMinute,
psystime->wSecond));
}
// BCharToHalfByte -----------------------------------------------------------
//
// Switches a wide char to a half-byte hex value. The incoming char
// MUST be in the "ASCII-encoded hex digit" range: 0-9, A-F, a-f.
//
DEC_CONST BYTE gc_mpbchCharToHalfByte[] = {
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7, 0x8,0x9,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0xa,0xb,0xc,0xd,0xe,0xf,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // Caps here.
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
0x0,0xa,0xb,0xc,0xd,0xe,0xf,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // Lowercase here.
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
};
inline BYTE BCharToHalfByte(WCHAR ch)
{
// gc_mpbchCharToHalfByte - map a ASCII-encoded char representing a single hex
// digit to a half-byte value. Used to convert hex represented strings into a
// binary representation.
//
// Reference values:
//
// '0' = 49, 0x31;
// 'A' = 65, 0x41;
// 'a' = 97, 0x61;
//
AssertSz (!(ch & 0xFF00), "BCharToHalfByte: char upper bits non-zero");
AssertSz (iswxdigit(ch), "Char out of hex digit range.");
return gc_mpbchCharToHalfByte[ch];
}
// ------------------------------------------------------------------------
// c_mpwchbStringize - map a half-byte (low nibble) value to
// the correspoding ASCII-encoded wide char.
// Used to convert binary data into Unicode URL strings.
//
DEC_CONST WCHAR c_mpwchhbStringize[] =
{
L'0', L'1', L'2', L'3',
L'4', L'5', L'6', L'7',
L'8', L'9', L'a', L'b',
L'c', L'd', L'e', L'f',
};
// ------------------------------------------------------------------------
// WchHalfByteToWideChar
// Switches a half-byte to an ACSII-encoded wide char.
// NOTE: The caller must mask out the "other half" of the byte!
//
inline WCHAR WchHalfByteToWideChar(BYTE b)
{
AssertSz(!(b & 0xF0), "Garbage in upper nibble.");
return c_mpwchhbStringize[b];
};
// ==========================================================================
//
// UTILITY FUNCTIONS
// Used in building some props -- like getetag, resourcetag and flat url.
// This code has been moved from calcprops.cpp to exprops.cpp and now to
// cnvt.cpp. The Flat URL code lives in this file because it is needed
// by _storext, exdav and davex. _props is the other component which is
// shared by all of them. But _cnvt seemed like a better place to put
// it. The other utility functions are needed by the flat url generation
// code and by davex in processing parameterized URLs.
//
// ==========================================================================
// ------------------------------------------------------------------------
// Un-stringiz-ing support functions
// (Stringize = dump a binary blob to a string.
// Unstringize = make it a binary blob again.)
//
inline
void
AssertCharInHexRange (char ch)
{
Assert ((ch >= '0' && ch <= '9') ||
(ch >= 'A' && ch <= 'F') ||
(ch >= 'a' && ch <= 'f'));
}
inline
BYTE
NibbleFromChar (char ch)
{
// Assumes data is already in range....
//
return static_cast<BYTE>((ch <= '9')
? ch - '0'
: ((ch >= 'a')
? ch - 'W' // 'W' = 'a' - 0xa
: ch - '7')); // '7' = 'A' - 0xa
}
inline
BYTE
ByteFromTwoChars (char chLow, char chHigh)
{
BYTE nibbleLow;
BYTE nibbleHigh;
nibbleLow = NibbleFromChar(chLow);
nibbleHigh = NibbleFromChar(chHigh);
return static_cast<BYTE>(nibbleLow | (nibbleHigh << 4));
}
//$REVIEW: The following two functions really does not belong to any common libraries
//$REVIEW: that are shared by davex, exdav and exoledb. (other options are _prop, _sql)
//$REVIEW: On the other hand, we definitely don't want add a new lib for this. so just
//$REVIEW: add it here. Feel free to move them to a better location if you find one
//
// ------------------------------------------------------------------------
//
// ScDupPsid()
//
// Copies a SID properly (using CopySid()) into a heap-allocated buffer
// that is returned to the caller. The caller must free the buffer when
// it is done using it.
//
SCODE
ScDupPsid (PSID psidSrc,
DWORD dwcbSID,
PSID * ppsidDst)
{
PSID psidDst;
Assert (psidSrc);
Assert (IsValidSid(psidSrc));
Assert (GetLengthSid(psidSrc) == dwcbSID);
psidDst = static_cast<PSID>(ExAlloc(dwcbSID));
if (!psidDst)
{
DebugTrace ("ScDupPsid() - OOM allocating memory for dup'd SID\n");
return E_OUTOFMEMORY;
}
// "Right way" -- since MSDN says not to touch the SID directly.
if (!CopySid (dwcbSID, psidDst, psidSrc))
{
DWORD dwLastError = GetLastError();
DebugTrace ("ScDupPsid() - CopySid() failed %d\n", dwLastError);
ExFree (psidDst);
return HRESULT_FROM_WIN32(dwLastError);
}
*ppsidDst = psidDst;
return S_OK;
}
// ------------------------------------------------------------------------
//
// ScGetTokenInfo()
//
// Extracts a user's security ID (SID) from a security token. Returns the SID
// in a heap-allocated buffer which the caller must free.
//
SCODE
ScGetTokenInfo (HANDLE hTokenUser,
DWORD * pdwcbSIDUser,
PSID * ppsidUser)
{
CStackBuffer<TOKEN_USER> pTokenUser;
DWORD dwcbTokenUser = pTokenUser.size(); //$OPT What is a good initial guess?
Assert (pdwcbSIDUser);
Assert (ppsidUser);
// Fetch the token info into local memory. GetTokenInformation()
// returns the size of the buffer needed if the one passed in is
// not large enough so this loop should execute no more than twice.
//
#ifdef DBG
for ( UINT iPass = 0;
(Assert (iPass < 2), TRUE);
++iPass )
#else
for ( ;; )
#endif
{
if (NULL == pTokenUser.resize(dwcbTokenUser))
return E_OUTOFMEMORY;
if (GetTokenInformation (hTokenUser,
TokenUser,
pTokenUser.get(),
dwcbTokenUser,
&dwcbTokenUser))
{
break;
}
else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return HRESULT_FROM_WIN32(GetLastError());
}
}
// Dup and return the SID from the token info.
//
*pdwcbSIDUser = GetLengthSid(pTokenUser->User.Sid);
return ScDupPsid (pTokenUser->User.Sid,
*pdwcbSIDUser,
ppsidUser);
}
// Our own version of WideCharToMultiByte(CP_UTF8, ...)
//
// It returns similarly to the system call WideCharToMultiByte:
//
// If the function succeeds, and cbDest is nonzero, the return value is
// the number of bytes written to the buffer pointed to by psz.
//
// If the function succeeds, and cbDest is zero, the return value is
// the required size, in bytes, for a buffer that can receive the translated
// string.
//
// If the function fails, the return value is zero. To get extended error
// information, call GetLastError. GetLastError may return one of the
// following error codes:
//
// ERROR_INSUFFICIENT_BUFFER
// ERROR_INVALID_FLAGS
// ERROR_INVALID_PARAMETER
//
// See the WideCharToMultiByte MSDN pages to find out more about
// this function and its use.
//
UINT WideCharToUTF8(/* [in] */ LPCWSTR pwszSrc,
/* [in] */ UINT cchSrc,
/* [out] */ LPSTR pszDest,
/* [in] */ UINT cbDest)
{
// UTF-8 multi-byte encoding. See Appendix A.2 of the Unicode book for
// more info.
//
// Unicode value 1st byte 2nd byte 3rd byte
// 000000000xxxxxxx 0xxxxxxx
// 00000yyyyyxxxxxx 110yyyyy 10xxxxxx
// zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
//
// If cbDest == 0 is passed in then we should only calculate the length
// needed, not use the "pszDest" parameter.
//
BOOL fCalculateOnly = FALSE;
// (comment from nt\private\windows\winnls\mbcs.c, corrected for accuracy):
// Invalid Parameter Check:
// - length of WC string is 0
// - multibyte buffer size is negative
// - WC string is NULL
// - length of MB string is NOT zero AND
// (MB string is NULL OR src and dest pointers equal)
//
if ( (cchSrc == 0) ||
(pwszSrc == NULL) ||
((cbDest != 0) &&
((pszDest == NULL) ||
(reinterpret_cast<VOID *>(pszDest) ==
reinterpret_cast<VOID *>(const_cast<LPWSTR>(pwszSrc))))) )
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
#ifdef DBG
// Check our parameters. We must be given a non-NULL pwszSrc.
//
Assert(pwszSrc);
// Make sure we have a valid string.
//
Assert(!IsBadStringPtrW(pwszSrc, (INVALID_INDEX == cchSrc) ? INFINITE : cchSrc));
// If the user says that the length of the multi-byte string is non-Zero,
// we must be given a non-NULL pszDest. We'll also check it with IsBadWritePtr().
//
if (cbDest)
{
Assert(pszDest);
Assert(!IsBadWritePtr(pszDest, cbDest));
}
#endif
// If -1 is passed in as the length of the string, then we calculate the
// length of the string on the fly, and include the NULL terminator.
//
if (INVALID_INDEX == cchSrc)
cchSrc = static_cast<UINT>(wcslen(pwszSrc) + 1);
// If 0 is passed in as cbDest, then we calculate the length of the
// buffer that would be needed to convert the string. We ignore the
// pszDest parameter in this case.
//
if (0 == cbDest)
fCalculateOnly = TRUE;
UINT ich = 0;
UINT iwch = 0;
for (; iwch < cchSrc; iwch++)
{
WCHAR wch = pwszSrc[iwch];
//
// Single-Byte Case:
// Unicode value 1st byte 2nd byte 3rd byte
// 000000000xxxxxxx 0xxxxxxx
//
if (wch < 0x80)
{
if (!fCalculateOnly)
{
if (ich >= cbDest)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
pszDest[ich] = static_cast<BYTE>(wch);
}
ich++;
}
//
// Double-Byte Case:
// Unicode value 1st byte 2nd byte 3rd byte
// 00000yyyyyxxxxxx 110yyyyy 10xxxxxx
//
else if (wch < 0x800)
{
if (!fCalculateOnly)
{
if ((ich + 1) >= cbDest)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
pszDest[ich] = static_cast<BYTE>((wch >> 6) | 0xC0);
pszDest[ich + 1] = static_cast<BYTE>((wch & 0x3F) | 0x80);
}
ich += 2;
}
//
// Triple-Byte Case:
// Unicode value 1st byte 2nd byte 3rd byte
// zzzzyyyyyyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx
//
else
{
if (!fCalculateOnly)
{
if ((ich + 2) >= cbDest)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return 0;
}
pszDest[ich] = static_cast<BYTE>((wch >> 12) | 0xE0);
pszDest[ich + 1] = static_cast<BYTE>(((wch >> 6) & 0x3F) | 0x80);
pszDest[ich + 2] = static_cast<BYTE>((wch & 0x3F) | 0x80);
}
ich += 3;
}
}
return ich;
}