/*++ Copyright (c) 1999 Microsoft Corporation Module Name : DAVPropBag.cxx Abstract : This Module is used by DAV to retrieve properties stored within files on NTFS NT 5 systems. Author: Emily Kruglick (emilyk) 08-Sept-1999 Project: Web Server --*/ /************************************************************ * Include Headers ************************************************************/ #include "precomp.hxx" #include #include #include #include // Function declaration for the StgOpenStoreageOnHandle // it is supported from OLE32.dll, but is not exposed or // documented. typedef HRESULT (__stdcall * STGOPENSTORAGEONHANDLE)( IN HANDLE hStream, IN DWORD grfMode, IN void *reserved1, IN void *reserved2, IN REFIID riid, OUT void **ppObjectOpen ); #define DEC_CONST extern const __declspec(selectany) // Must match the constant values defined in managed code. DEC_CONST int gc_iDavType_String = 1; DEC_CONST int gc_iDavType_String_XML_TAG = 2; DEC_CONST int gc_iDavType_String_XML_FULL = 3; DEC_CONST int gc_iDavType_Date_ISO8601 = 4; DEC_CONST int gc_iDavType_Date_Rfc1123 = 5; DEC_CONST int gc_iDavType_Float = 6; DEC_CONST int gc_iDavType_Boolean = 7; DEC_CONST int gc_iDavType_Int = 8; /********************************************************************++ Routines borrowed from DAVFS that manage converting data for properties ++********************************************************************/ #define CchConstString(_s) ((sizeof(_s)/sizeof(_s[0])) - 1) DEC_CONST WCHAR gc_wszIso8601_min[] = L"yyyy-mm-ddThh:mm:ssZ"; DEC_CONST UINT gc_cchszIso8601_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"%hs, %02d %hs %04d %02d:%02d:%02d GMT"; DEC_CONST CHAR gc_szRfc1123_fmt[] = "%hs, %02d %hs %04d %02d:%02d:%02d GMT"; DEC_CONST int PROP_BAG_SIZE = 16; DEC_CONST int HTTP_STATUS_OK = 200; BOOL __fastcall FGetDateIso8601FromSystime(SYSTEMTIME * psystime, LPWSTR pwszDate, ULONG cSize) { // If there is not enough space... // if (cSize <= gc_cchszIso8601_min) 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)); } /********************************************************************++ Global Entry Points for callback from XSP ++********************************************************************/ # if !defined( dllexp) # define dllexp __declspec( dllexport) # endif // !defined( dllexp) /********************************************************************++ ++********************************************************************/ // Used to store linked list of property information. struct PropNode { BSTR PropName; BSTR PropValue; int PropType; int ResultCode; PropNode* next; }; const int ALL_PROPS_REQUESTED = 1; const int PROP_NAMES_ONLY_REQUESTED = 2; // Class is used to hold information about a URI's properties // so we don't have to evaluate from the URI twice when we need // to get the count of values that we should expect back. class DAVPropertyManager { public: DAVPropertyManager(); ~DAVPropertyManager(); HRESULT GetCountOfProperties(BSTR uri, int flag, BSTR* RequestedProps, int ReqPropsCount, int* count); HRESULT GetProperties( BSTR uri , int flag , BSTR* RequestedProps , int RequestedPropsCount , BSTR* names , BSTR* values , int* types , int* codes , int count); private: HRESULT EvaluateURI(BSTR bstrURI, int flag, BSTR* propnames, int propnamescount); HRESULT GetPropertyBag( LPCWSTR pwszPath, IPropertyBagEx** ppBag); HRESULT StoreProps( IPropertyBagEx* pBag, BSTR* RequestedProps, int RequestedPropCount, int flag); HRESULT AddPropToList(LPWSTR name, PROPVARIANT* var, int resultcode); HRESULT CopyValue (LPWSTR origVal, BSTR* pnewValue); HRESULT SavePropValue ( PROPVARIANT* var, BSTR* ppValue, int* ppType ); int _count; // number of properties found LPWSTR _uri; // uri the properties were retrieved from PropNode* _pPropertyList; // saved property listing PropNode* _pLastPropertyAdded; // last spot in property listing }; DAVPropertyManager::DAVPropertyManager() { _count = 0; _uri = NULL; _pPropertyList = NULL; _pLastPropertyAdded = NULL; } DAVPropertyManager::~DAVPropertyManager() { delete[] _uri; PropNode* tpi; while (_pPropertyList != NULL) { tpi = _pPropertyList->next; SysFreeString(_pPropertyList->PropName); SysFreeString(_pPropertyList->PropValue); // Types are set to integers so they don't need freeing!! // so they are never freed!! // delete[] _pPropertyList->PropType; delete _pPropertyList; _pPropertyList = tpi; } } HRESULT DAVPropertyManager::EvaluateURI(BSTR bstrURI, int flag, BSTR* RequestedProps, int RequestedPropsCount) { HRESULT hr = S_OK; IPropertyBagEx* pBag = NULL; if (bstrURI == NULL) return E_INVALIDARG; // Save the URI for later use. (Right now we don't have a later use, but we might someday.) _uri = new WCHAR[wcslen(bstrURI) + 1]; if (_uri == NULL) { hr = E_OUTOFMEMORY; goto cleanup; } wcscpy(_uri, bstrURI); hr = GetPropertyBag(bstrURI, &pBag); if (SUCCEEDED (hr)) { hr = StoreProps(pBag, RequestedProps, RequestedPropsCount, flag); } cleanup: if (pBag) pBag->Release(); return hr; } // BUGBUG: This is an extremely simplified version just to get us running, it will need to be compared with ScGetPropertyBag // and have all the complexity added back in. HRESULT DAVPropertyManager::GetPropertyBag( LPCWSTR pwszPath, IPropertyBagEx** ppBag) { HRESULT hr = S_OK; DWORD dwResult = 0; HANDLE hFile = NULL; HINSTANCE hLib = NULL; hFile = CreateFileW (pwszPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == NULL) { dwResult = GetLastError(); hr = HRESULT_FROM_WIN32(dwResult); goto cleanup; } hLib = LoadLibrary (L"ole32.dll"); if (hLib != NULL) { // Physical Address does not need to be freed (I believe). STGOPENSTORAGEONHANDLE pfnStorage = (STGOPENSTORAGEONHANDLE) GetProcAddress (hLib, "StgOpenStorageOnHandle"); hr = (*pfnStorage) (hFile, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, NULL, IID_IPropertyBagEx, (LPVOID *)ppBag); if (FAILED(hr)) goto cleanup; } else { dwResult = GetLastError(); hr = HRESULT_FROM_WIN32(dwResult); goto cleanup; } if (*ppBag == NULL) { hr = E_FAIL; goto cleanup; } cleanup: if (hFile) CloseHandle(hFile); if (hLib) FreeLibrary(hLib); return hr; } HRESULT DAVPropertyManager::StoreProps( IPropertyBagEx* pBag, BSTR* RequestedProps, int RequestedPropCount, int flag ) { HRESULT hr = S_OK; IEnumSTATPROPBAG* enumBag = NULL; STATPROPBAG sp[PROP_BAG_SIZE]; ULONG cprops = 0; LPWSTR propnames[PROP_BAG_SIZE] = {0}; LPWSTR* passprops = NULL; PROPVARIANT propvariants[PROP_BAG_SIZE]; // Need to clear out the propvariants memory. ZeroMemory (&propvariants, sizeof(PROPVARIANT) * PROP_BAG_SIZE ); ULONG i = 0; // Figure out if we want all properties or just selected ones and if we want the property values as well. bool fAllProps = ((flag == ALL_PROPS_REQUESTED) || (flag == PROP_NAMES_ONLY_REQUESTED)); bool fPropValues = flag != PROP_NAMES_ONLY_REQUESTED; // Validate that either we want all properties or have been given specific properties we want to get ASSERT (fAllProps || (RequestedProps && RequestedPropCount > 0)); bool fMorePropertiesExist = fAllProps; // If we are looking for all properties then // we need an enum bag to use to get the properties if (fAllProps) { hr = pBag->Enum (NULL, 0, &enumBag); if (FAILED(hr)) goto cleanup; } do { if (fAllProps) { // Use the enumBag to get the properties names we need. hr = enumBag->Next(PROP_BAG_SIZE, sp, &cprops); if (FAILED(hr)) goto cleanup; fMorePropertiesExist = (hr == S_OK); // hr will be S_FALSE when we have processed all the properties. // Need to first go through and create a list of the properties I am going to ask // for when I read multiple property info from the bag. for (i = 0 ; i < cprops ; i++) { // grab the pointer to the property name propnames[i] = sp[i].lpwstrName; } // set it into the variable we will use below. passprops = propnames; } else { // if we already had the property names set them into the correct variables. passprops = (LPWSTR*) RequestedProps; cprops = RequestedPropCount; } // Now that i have set the property names in I can ask // for the properties back. hr = pBag->ReadMultiple (cprops, passprops, propvariants, NULL); if (FAILED(hr)) goto cleanup; for (i = 0 ; i < cprops ; i++) { if (propvariants[i].vt != VT_EMPTY) { if (fPropValues) AddPropToList(passprops[i], &(propvariants[i]), HTTP_STATUS_OK); else AddPropToList(passprops[i], NULL, HTTP_STATUS_OK); } // If the enumBag->Next created property names then we need to clean them up. if (fAllProps) { CoTaskMemFree(passprops[i]); passprops[i] = NULL; } // MessageBox(NULL, sp[i].lpwstrName, L"Names", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK | MB_ICONHAND); } // Free the memory for the PROPVARIANTS FreePropVariantArray(cprops, propvariants); } while (fMorePropertiesExist); cleanup: if (enumBag) enumBag->Release(); if ((fAllProps) && (passprops != NULL)) { for (i = 0; iPropValue), &(tmp->PropType)); if (FAILED(hr)) { // if we had any problem with saving the property value then we // will just ignore the property being returned to the user. delete tmp; return S_OK; } } else { tmp->PropValue = NULL; tmp->PropType = 0; } // Make copy of names and values, so we don't end up with someone changing our values later // like when another enumBag->Next is called. hr = CopyValue(name, &(tmp->PropName)); if (FAILED(hr)) { SysFreeString(tmp->PropValue) ; delete tmp; return hr; } tmp->ResultCode = resultcode; tmp->next = NULL; if (_pLastPropertyAdded) { _pLastPropertyAdded->next = tmp; _pLastPropertyAdded = tmp; } else { _pPropertyList = tmp; _pLastPropertyAdded = tmp; } // Make sure we record that we have another property. _count++; return hr; } HRESULT DAVPropertyManager::CopyValue (LPWSTR origVal, BSTR* pnewValue) { ASSERT (pnewValue); *pnewValue = SysAllocString(origVal); if (*pnewValue == NULL) return E_OUTOFMEMORY; return S_OK; } HRESULT DAVPropertyManager::SavePropValue ( PROPVARIANT* var, BSTR* ppValue, int* ppType ) { HRESULT hr = S_OK; ULONG cch = 0; // Count of characters needed for new string WCHAR wszBuf[100]; // Buffer for converting number into a string VARIANT* pvarTrue = reinterpret_cast(var); // Used for reaching numeric types. CHAR szBuf[100]; // Buffer for convertint asci values. LPWSTR tmp = NULL; // Validate we have the correct variables coming in. ASSERT (ppValue && ppType); ASSERT (var); if (ppValue==NULL || ppType ==NULL) return E_INVALIDARG; if (var==NULL) return E_INVALIDARG; // Initalize outgoing variables. *ppValue = NULL; *ppType = 0; switch (var->vt) { case VT_NULL: case VT_EMPTY: break; case VT_BSTR: hr = CopyValue(static_cast(var->bstrVal), ppValue); break; case VT_LPWSTR: hr = CopyValue(var->pwszVal, ppValue); break; case VT_LPSTR: if (!var->pszVal) break; cch = strlen (var->pszVal) + 1; tmp = new WCHAR[cch]; if (tmp == NULL) hr = E_OUTOFMEMORY; else { MultiByteToWideChar (CP_ACP, 0, var->pszVal, -1, tmp, cch); *ppValue = SysAllocString(tmp); delete[] tmp; } break; case VT_I1: // BUGBUG: Do we want to new the wszBuf each time // and then itow directly into it, instead of // having to do the new and copy after the itow? *ppType = gc_iDavType_Int; _itow (pvarTrue->cVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_UI1: *ppType = gc_iDavType_Int; _ultow (var->bVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_I2: *ppType = gc_iDavType_Int; _itow (var->iVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_UI2: *ppType = gc_iDavType_Int; _ultow (var->uiVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_I4: *ppType = gc_iDavType_Int; _ltow (var->lVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_UI4: *ppType = gc_iDavType_Int; _ultow (var->ulVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_I8: *ppType = gc_iDavType_Int; _i64tow (var->hVal.QuadPart, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_UI8: *ppType = gc_iDavType_Int; _ui64tow (var->uhVal.QuadPart, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_INT: *ppType = gc_iDavType_Int; _itow (pvarTrue->intVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_UINT: *ppType = gc_iDavType_Int; _ultow (pvarTrue->uintVal, wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_BOOL: *ppType = gc_iDavType_Boolean; _itow (!(VARIANT_FALSE == var->boolVal), wszBuf, 10); hr = CopyValue(wszBuf, ppValue); break; case VT_R4: case VT_R8: if (VT_R4 == var->vt) _gcvt (var->fltVal, 99, szBuf); else _gcvt (var->dblVal, 99 , szBuf); MultiByteToWideChar (CP_ACP, 0, szBuf, -1, wszBuf, 100); *ppType = gc_iDavType_Float; hr = CopyValue(wszBuf, ppValue); break; case VT_FILETIME: SYSTEMTIME st; FileTimeToSystemTime (&var->filetime, &st); if (!FGetDateIso8601FromSystime(&st, wszBuf, 100)) { hr = E_INVALIDARG; } else { *ppType = gc_iDavType_Date_ISO8601; hr = CopyValue(wszBuf, ppValue); } break; case VT_CY: case VT_DATE: case VT_DISPATCH: case VT_ERROR: case VT_VARIANT: case VT_UNKNOWN: case VT_DECIMAL: case VT_RECORD: case VT_BLOB: case VT_STREAM: case VT_STORAGE: case VT_STREAMED_OBJECT: case VT_STORED_OBJECT: case VT_BLOB_OBJECT: case VT_CF: case VT_CLSID: // DAVFS supported vectors of wstrs because at one point you could save them, however that was long ago // JoelS didn't have any problem with my decision not to support reading these back so we are not. // They complicate property management since they require multiple value support. case VT_VECTOR | VT_LPWSTR: default: hr = E_UNEXPECTED; break; } return hr; } HRESULT DAVPropertyManager::GetCountOfProperties( BSTR uri , int flag , BSTR* propnames , int propnamescount , int* count) { HRESULT hr = S_OK; if (_uri == NULL) hr = EvaluateURI(uri, flag, propnames, propnamescount); *count = _count; return hr; } HRESULT DAVPropertyManager::GetProperties( BSTR uri , int flag , BSTR* propnames , int propnamescount , BSTR* names , BSTR* values , int* types , int* codes , int count) { HRESULT hr = S_OK; if (_uri == NULL) hr = EvaluateURI(uri, flag, propnames, propnamescount); if (SUCCEEDED(hr)) { if (count != _count) return E_INVALIDARG; // MessageBox(NULL, L"GetProperties", L"IN", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK | MB_ICONHAND); PropNode* tpi = _pPropertyList; int i = 0; while ((tpi != NULL) && (i < count)) { names[i] = tpi->PropName; values[i] = tpi->PropValue; types[i] = tpi->PropType; codes[i] = tpi->ResultCode; i++; tpi = tpi->next; } } return hr; } // Exported functions for interacting with the DAVPropertyManager Class. dllexp HRESULT DAVGetPropertyCount (BSTR uri, BSTR* propnames, int propnamescount, int flag, int* count, int* addr) { HRESULT hr = S_OK; DAVPropertyManager* pm = NULL; *count = 0; //initalize out parameter. if (*addr == NULL) { pm = new DAVPropertyManager(); *addr = (int) pm; } else { pm = (DAVPropertyManager*) *addr; } hr = pm->GetCountOfProperties(uri, flag, propnames, propnamescount, count); return hr; } dllexp HRESULT DAVGetProperties ( BSTR uri , BSTR* propnames , int propnamescount , int flag , BSTR* names , BSTR* values , int* types , int* codes , int count , int* addr) { HRESULT hr = S_OK; DAVPropertyManager* pm = NULL; if (*addr == NULL) { pm = new DAVPropertyManager(); *addr = (int) pm; } else { pm = (DAVPropertyManager*) *addr; } hr = pm->GetProperties(uri, flag, propnames, propnamescount, names, values, types, codes, count); return hr; } dllexp HRESULT DAVFreePropManager (int addr) { HRESULT hr = S_OK; if (addr != NULL) { DAVPropertyManager* pm = (DAVPropertyManager*) addr; delete pm; } return hr; }