/* * Speech API (SAPI) token implementation. * * Copyright (C) 2017 Huw Davies * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #define COBJMACROS #include "windef.h" #include "winbase.h" #include "objbase.h" #include "sapiddk.h" #include "sperror.h" #include "wine/debug.h" #include "sapi_private.h" WINE_DEFAULT_DEBUG_CHANNEL(sapi); struct data_key { ISpRegDataKey ISpRegDataKey_iface; LONG ref; HKEY key; }; static struct data_key *impl_from_ISpRegDataKey( ISpRegDataKey *iface ) { return CONTAINING_RECORD( iface, struct data_key, ISpRegDataKey_iface ); } struct object_token { ISpObjectToken ISpObjectToken_iface; LONG ref; ISpRegDataKey *data_key; WCHAR *token_id; }; static struct object_token *impl_from_ISpObjectToken( ISpObjectToken *iface ) { return CONTAINING_RECORD( iface, struct object_token, ISpObjectToken_iface ); } static HRESULT WINAPI data_key_QueryInterface( ISpRegDataKey *iface, REFIID iid, void **obj ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); if (IsEqualIID( iid, &IID_IUnknown ) || IsEqualIID( iid, &IID_ISpDataKey ) || IsEqualIID( iid, &IID_ISpRegDataKey )) { ISpRegDataKey_AddRef( iface ); *obj = iface; return S_OK; } FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI data_key_AddRef( ISpRegDataKey *iface ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); ULONG ref = InterlockedIncrement( &This->ref ); TRACE( "(%p) ref = %lu\n", This, ref ); return ref; } static ULONG WINAPI data_key_Release( ISpRegDataKey *iface ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); ULONG ref = InterlockedDecrement(&This->ref); TRACE( "(%p) ref = %lu\n", This, ref ); if (!ref) { if (This->key) RegCloseKey( This->key ); free( This ); } return ref; } static HRESULT WINAPI data_key_SetData( ISpRegDataKey *iface, LPCWSTR name, ULONG size, const BYTE *data ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_GetData( ISpRegDataKey *iface, LPCWSTR name, ULONG *size, BYTE *data ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_SetStringValue( ISpRegDataKey *iface, LPCWSTR name, LPCWSTR value ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); DWORD ret, size; TRACE( "%p, %s, %s\n", This, debugstr_w(name), debugstr_w(value) ); if (!This->key) return E_HANDLE; size = (wcslen(value) + 1) * sizeof(WCHAR); ret = RegSetValueExW( This->key, name, 0, REG_SZ, (BYTE *)value, size ); return HRESULT_FROM_WIN32(ret); } static HRESULT WINAPI data_key_GetStringValue( ISpRegDataKey *iface, LPCWSTR name, LPWSTR *value ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); DWORD ret, size; WCHAR *content; TRACE( "%p, %s, %p\n", This, debugstr_w(name), value); if (!This->key) return E_HANDLE; size = 0; ret = RegGetValueW( This->key, NULL, name, RRF_RT_REG_SZ, NULL, NULL, &size ); if (ret != ERROR_SUCCESS) return SPERR_NOT_FOUND; content = CoTaskMemAlloc(size); if (!content) return E_OUTOFMEMORY; ret = RegGetValueW( This->key, NULL, name, RRF_RT_REG_SZ, NULL, content, &size ); if (ret != ERROR_SUCCESS) { CoTaskMemFree(content); return HRESULT_FROM_WIN32(ret); } *value = content; return S_OK; } static HRESULT WINAPI data_key_SetDWORD( ISpRegDataKey *iface, LPCWSTR name, DWORD value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_GetDWORD( ISpRegDataKey *iface, LPCWSTR name, DWORD *pdwValue ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_OpenKey( ISpRegDataKey *iface, LPCWSTR name, ISpDataKey **sub_key ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); ISpRegDataKey *spregkey; HRESULT hr; HKEY key; LONG ret; TRACE( "%p, %s, %p\n", This, debugstr_w(name), sub_key ); ret = RegOpenKeyExW( This->key, name, 0, KEY_ALL_ACCESS, &key ); if (ret != ERROR_SUCCESS) return SPERR_NOT_FOUND; hr = data_key_create( NULL, &IID_ISpRegDataKey, (void**)&spregkey ); if (FAILED(hr)) { RegCloseKey( key ); return hr; } hr = ISpRegDataKey_SetKey( spregkey, key, FALSE ); if (FAILED(hr)) { RegCloseKey( key ); ISpRegDataKey_Release( spregkey ); return hr; } hr = ISpRegDataKey_QueryInterface( spregkey, &IID_ISpDataKey, (void**)sub_key ); ISpRegDataKey_Release( spregkey ); return hr; } static HRESULT WINAPI data_key_CreateKey( ISpRegDataKey *iface, LPCWSTR name, ISpDataKey **sub_key ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); ISpRegDataKey *spregkey; HRESULT hr; HKEY key; LONG res; TRACE( "%p, %s, %p\n", This, debugstr_w(name), sub_key ); res = RegCreateKeyExW( This->key, name, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, NULL ); if (res != ERROR_SUCCESS) return HRESULT_FROM_WIN32(res); hr = data_key_create(NULL, &IID_ISpRegDataKey, (void**)&spregkey); if (SUCCEEDED(hr)) { hr = ISpRegDataKey_SetKey(spregkey, key, FALSE); if (SUCCEEDED(hr)) hr = ISpRegDataKey_QueryInterface(spregkey, &IID_ISpDataKey, (void**)sub_key); ISpRegDataKey_Release(spregkey); } return hr; } static HRESULT WINAPI data_key_DeleteKey( ISpRegDataKey *iface, LPCWSTR name ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_DeleteValue( ISpRegDataKey *iface, LPCWSTR name ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_EnumKeys( ISpRegDataKey *iface, ULONG index, LPWSTR *sub_key ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_EnumValues( ISpRegDataKey *iface, ULONG index, LPWSTR *value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_SetKey( ISpRegDataKey *iface, HKEY key, BOOL read_only ) { struct data_key *This = impl_from_ISpRegDataKey( iface ); TRACE( "(%p)->(%p %d)\n", This, key, read_only ); if (This->key) return SPERR_ALREADY_INITIALIZED; /* read_only is ignored in Windows implementations. */ This->key = key; return S_OK; } const struct ISpRegDataKeyVtbl data_key_vtbl = { data_key_QueryInterface, data_key_AddRef, data_key_Release, data_key_SetData, data_key_GetData, data_key_SetStringValue, data_key_GetStringValue, data_key_SetDWORD, data_key_GetDWORD, data_key_OpenKey, data_key_CreateKey, data_key_DeleteKey, data_key_DeleteValue, data_key_EnumKeys, data_key_EnumValues, data_key_SetKey }; HRESULT data_key_create( IUnknown *outer, REFIID iid, void **obj ) { struct data_key *This = malloc( sizeof(*This) ); HRESULT hr; if (!This) return E_OUTOFMEMORY; This->ISpRegDataKey_iface.lpVtbl = &data_key_vtbl; This->ref = 1; This->key = NULL; hr = ISpRegDataKey_QueryInterface( &This->ISpRegDataKey_iface, iid, obj ); ISpRegDataKey_Release( &This->ISpRegDataKey_iface ); return hr; } struct token_category { ISpObjectTokenCategory ISpObjectTokenCategory_iface; LONG ref; ISpRegDataKey *data_key; WCHAR *id; }; static struct token_category *impl_from_ISpObjectTokenCategory( ISpObjectTokenCategory *iface ) { return CONTAINING_RECORD( iface, struct token_category, ISpObjectTokenCategory_iface ); } static HRESULT WINAPI token_category_QueryInterface( ISpObjectTokenCategory *iface, REFIID iid, void **obj ) { struct token_category *This = impl_from_ISpObjectTokenCategory( iface ); TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); if (IsEqualIID( iid, &IID_IUnknown ) || IsEqualIID( iid, &IID_ISpDataKey ) || IsEqualIID( iid, &IID_ISpObjectTokenCategory )) { ISpObjectTokenCategory_AddRef( iface ); *obj = iface; return S_OK; } FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI token_category_AddRef( ISpObjectTokenCategory *iface ) { struct token_category *This = impl_from_ISpObjectTokenCategory( iface ); ULONG ref = InterlockedIncrement( &This->ref ); TRACE( "(%p) ref = %lu\n", This, ref ); return ref; } static ULONG WINAPI token_category_Release( ISpObjectTokenCategory *iface ) { struct token_category *This = impl_from_ISpObjectTokenCategory( iface ); ULONG ref = InterlockedDecrement(&This->ref); TRACE( "(%p) ref = %lu\n", This, ref ); if (!ref) { if (This->data_key) ISpRegDataKey_Release( This->data_key ); free( This->id ); free( This ); } return ref; } static HRESULT WINAPI token_category_SetData( ISpObjectTokenCategory *iface, LPCWSTR name, ULONG size, const BYTE *data ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_GetData( ISpObjectTokenCategory *iface, LPCWSTR name, ULONG *size, BYTE *data ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_SetStringValue( ISpObjectTokenCategory *iface, LPCWSTR name, LPCWSTR value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_GetStringValue( ISpObjectTokenCategory *iface, LPCWSTR name, LPWSTR *value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_SetDWORD( ISpObjectTokenCategory *iface, LPCWSTR name, DWORD value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_GetDWORD( ISpObjectTokenCategory *iface, LPCWSTR name, DWORD *pdwValue ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_OpenKey( ISpObjectTokenCategory *iface, LPCWSTR name, ISpDataKey **sub_key ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_CreateKey( ISpObjectTokenCategory *iface, LPCWSTR name, ISpDataKey **sub_key ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_DeleteKey( ISpObjectTokenCategory *iface, LPCWSTR name ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_DeleteValue( ISpObjectTokenCategory *iface, LPCWSTR name ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_EnumKeys( ISpObjectTokenCategory *iface, ULONG index, LPWSTR *sub_key ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_EnumValues( ISpObjectTokenCategory *iface, ULONG index, LPWSTR *value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT parse_cat_id( const WCHAR *str, HKEY *root, const WCHAR **sub_key ) { struct table { const WCHAR *name; unsigned int len; HKEY key; } table[] = { #define X(s) s, ARRAY_SIZE(s) - 1 { X(L"HKEY_LOCAL_MACHINE\\"), HKEY_LOCAL_MACHINE }, { X(L"HKEY_CURRENT_USER\\"), HKEY_CURRENT_USER }, { NULL } #undef X }; struct table *ptr; int len = lstrlenW( str ); for (ptr = table; ptr->name; ptr++) { if (len >= ptr->len && !wcsncmp( str, ptr->name, ptr->len )) { *root = ptr->key; *sub_key = str + ptr->len; return S_OK; } } return S_FALSE; } static HRESULT WINAPI create_data_key_with_hkey( HKEY key, ISpRegDataKey **data_key ) { HRESULT hr; if (FAILED(hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, &IID_ISpRegDataKey, (void **)data_key ) )) return hr; if (FAILED(hr = ISpRegDataKey_SetKey( *data_key, key, TRUE ))) { ISpRegDataKey_Release( *data_key ); *data_key = NULL; } return hr; } static HRESULT WINAPI token_category_SetId( ISpObjectTokenCategory *iface, LPCWSTR id, BOOL create ) { struct token_category *This = impl_from_ISpObjectTokenCategory( iface ); HKEY root, key; const WCHAR *subkey; LONG res; HRESULT hr; TRACE( "(%p)->(%s %d)\n", This, debugstr_w( id ), create ); if (This->data_key) return SPERR_ALREADY_INITIALIZED; hr = parse_cat_id( id, &root, &subkey ); if (hr != S_OK) return SPERR_INVALID_REGISTRY_KEY; if (create) res = RegCreateKeyExW( root, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, NULL ); else res = RegOpenKeyExW( root, subkey, 0, KEY_ALL_ACCESS, &key ); if (res) return SPERR_INVALID_REGISTRY_KEY; if (FAILED(hr = create_data_key_with_hkey( key, &This->data_key ))) { RegCloseKey( key ); return hr; } This->id = wcsdup( id ); return hr; } static HRESULT WINAPI token_category_GetId( ISpObjectTokenCategory *iface, LPWSTR *id ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_GetDataKey( ISpObjectTokenCategory *iface, SPDATAKEYLOCATION location, ISpDataKey **data_key ) { FIXME( "stub\n" ); return E_NOTIMPL; } struct token_with_score { ISpObjectToken *token; uint64_t score; }; struct token_enum { ISpObjectTokenEnumBuilder ISpObjectTokenEnumBuilder_iface; LONG ref; BOOL init; WCHAR *req, *opt; struct token_with_score *tokens; ULONG capacity, count; DWORD index; }; static struct token_enum *impl_from_ISpObjectTokenEnumBuilder( ISpObjectTokenEnumBuilder *iface ) { return CONTAINING_RECORD( iface, struct token_enum, ISpObjectTokenEnumBuilder_iface ); } static HRESULT WINAPI token_category_EnumTokens( ISpObjectTokenCategory *iface, LPCWSTR req, LPCWSTR opt, IEnumSpObjectTokens **enum_tokens ) { struct token_category *This = impl_from_ISpObjectTokenCategory( iface ); ISpObjectTokenEnumBuilder *builder; struct data_key *this_data_key; HKEY tokens_key; DWORD count, max_subkey_size, root_len, token_id_size; DWORD size, i; WCHAR *token_id = NULL; ISpObjectToken *token = NULL; HRESULT hr; TRACE( "(%p)->(%s %s %p)\n", This, debugstr_w( req ), debugstr_w( opt ), enum_tokens ); if (!This->data_key) return SPERR_UNINITIALIZED; hr = CoCreateInstance( &CLSID_SpObjectTokenEnum, NULL, CLSCTX_ALL, &IID_ISpObjectTokenEnumBuilder, (void **)&builder ); if (FAILED(hr)) return hr; hr = ISpObjectTokenEnumBuilder_SetAttribs( builder, req, opt ); if (FAILED(hr)) goto fail; this_data_key = impl_from_ISpRegDataKey( This->data_key ); if (!RegOpenKeyExW( this_data_key->key, L"Tokens", 0, KEY_ALL_ACCESS, &tokens_key )) { RegQueryInfoKeyW( tokens_key, NULL, NULL, NULL, &count, &max_subkey_size, NULL, NULL, NULL, NULL, NULL, NULL ); max_subkey_size++; root_len = wcslen( This->id ); token_id_size = root_len + sizeof("\\Tokens\\") + max_subkey_size; token_id = malloc( token_id_size * sizeof(WCHAR) ); if (!token_id) { hr = E_OUTOFMEMORY; goto fail; } root_len = swprintf( token_id, token_id_size, L"%ls%lsTokens\\", This->id, This->id[root_len - 1] == L'\\' ? L"" : L"\\" ); for ( i = 0; i < count; i++ ) { size = max_subkey_size; hr = HRESULT_FROM_WIN32(RegEnumKeyExW( tokens_key, i, token_id + root_len, &size, NULL, NULL, NULL, NULL )); if (FAILED(hr)) goto fail; hr = token_create( NULL, &IID_ISpObjectToken, (void **)&token ); if (FAILED(hr)) goto fail; hr = ISpObjectToken_SetId( token, NULL, token_id, FALSE ); if (FAILED(hr)) goto fail; hr = ISpObjectTokenEnumBuilder_AddTokens( builder, 1, &token ); if (FAILED(hr)) goto fail; ISpObjectToken_Release( token ); token = NULL; } hr = ISpObjectTokenEnumBuilder_Sort( builder, NULL ); if (FAILED(hr)) goto fail; } hr = ISpObjectTokenEnumBuilder_QueryInterface( builder, &IID_IEnumSpObjectTokens, (void **)enum_tokens ); fail: ISpObjectTokenEnumBuilder_Release( builder ); if ( token ) ISpObjectToken_Release( token ); free( token_id ); return hr; } static HRESULT WINAPI token_category_SetDefaultTokenId( ISpObjectTokenCategory *iface, LPCWSTR id ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_category_GetDefaultTokenId( ISpObjectTokenCategory *iface, LPWSTR *id ) { struct token_category *This = impl_from_ISpObjectTokenCategory( iface ); struct data_key *this_data_key; LONG res; WCHAR regvalue[512]; DWORD regvalue_size = sizeof( regvalue ); FIXME( "(%p)->(%p): semi-stub\n", iface, id ); if (!This->data_key) return SPERR_UNINITIALIZED; if (!id) return E_POINTER; /* todo: check HKCU's DefaultTokenId before */ this_data_key = impl_from_ISpRegDataKey( This->data_key ); res = RegGetValueW( this_data_key->key, NULL, L"DefaultDefaultTokenId", RRF_RT_REG_SZ, NULL, ®value, ®value_size); if (res == ERROR_FILE_NOT_FOUND) { return SPERR_NOT_FOUND; } else if (res != ERROR_SUCCESS) { /* probably not the correct return value */ FIXME( "returning %08lx\n", res ); return res; } *id = CoTaskMemAlloc( regvalue_size ); wcscpy( *id, regvalue ); return S_OK; } const struct ISpObjectTokenCategoryVtbl token_category_vtbl = { token_category_QueryInterface, token_category_AddRef, token_category_Release, token_category_SetData, token_category_GetData, token_category_SetStringValue, token_category_GetStringValue, token_category_SetDWORD, token_category_GetDWORD, token_category_OpenKey, token_category_CreateKey, token_category_DeleteKey, token_category_DeleteValue, token_category_EnumKeys, token_category_EnumValues, token_category_SetId, token_category_GetId, token_category_GetDataKey, token_category_EnumTokens, token_category_SetDefaultTokenId, token_category_GetDefaultTokenId, }; HRESULT token_category_create( IUnknown *outer, REFIID iid, void **obj ) { struct token_category *This = malloc( sizeof(*This) ); HRESULT hr; if (!This) return E_OUTOFMEMORY; This->ISpObjectTokenCategory_iface.lpVtbl = &token_category_vtbl; This->ref = 1; This->data_key = NULL; This->id = NULL; hr = ISpObjectTokenCategory_QueryInterface( &This->ISpObjectTokenCategory_iface, iid, obj ); ISpObjectTokenCategory_Release( &This->ISpObjectTokenCategory_iface ); return hr; } static HRESULT WINAPI token_enum_QueryInterface( ISpObjectTokenEnumBuilder *iface, REFIID iid, void **obj ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); if (IsEqualIID( iid, &IID_IUnknown ) || IsEqualIID( iid, &IID_IEnumSpObjectTokens ) || IsEqualIID( iid, &IID_ISpObjectTokenEnumBuilder )) { ISpObjectTokenEnumBuilder_AddRef( iface ); *obj = iface; return S_OK; } FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI token_enum_AddRef( ISpObjectTokenEnumBuilder *iface ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); ULONG ref = InterlockedIncrement( &This->ref ); TRACE( "(%p) ref = %lu\n", This, ref ); return ref; } static ULONG WINAPI token_enum_Release( ISpObjectTokenEnumBuilder *iface ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); ULONG ref = InterlockedDecrement(&This->ref); TRACE( "(%p) ref = %lu\n", This, ref ); if (!ref) { free( This->req ); free( This->opt ); if (This->tokens) { ULONG i; for ( i = 0; i < This->count; i++ ) if ( This->tokens[i].token ) ISpObjectToken_Release( This->tokens[i].token ); free( This->tokens ); } free( This ); } return ref; } static HRESULT WINAPI token_enum_Next( ISpObjectTokenEnumBuilder *iface, ULONG num, ISpObjectToken **tokens, ULONG *fetched ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); ULONG i; TRACE( "(%p)->(%lu %p %p)\n", This, num, tokens, fetched ); if (!This->init) return SPERR_UNINITIALIZED; if (!fetched && num != 1) return E_POINTER; if (!tokens) return E_POINTER; for ( i = 0; i < num && This->index < This->count; i++, This->index++ ) { ISpObjectToken_AddRef( This->tokens[This->index].token ); tokens[i] = This->tokens[This->index].token; } if (fetched) *fetched = i; return i == num ? S_OK : S_FALSE; } static HRESULT WINAPI token_enum_Skip( ISpObjectTokenEnumBuilder *iface, ULONG num ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_enum_Reset( ISpObjectTokenEnumBuilder *iface) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_enum_Clone( ISpObjectTokenEnumBuilder *iface, IEnumSpObjectTokens **clone ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_enum_Item( ISpObjectTokenEnumBuilder *iface, ULONG index, ISpObjectToken **token ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); TRACE( "(%p)->(%lu %p)\n", This, index, token ); if (!This->init) return SPERR_UNINITIALIZED; if (!token) return E_POINTER; if (index >= This->count) return SPERR_NO_MORE_ITEMS; ISpObjectToken_AddRef( This->tokens[index].token ); *token = This->tokens[index].token; return S_OK; } static HRESULT WINAPI token_enum_GetCount( ISpObjectTokenEnumBuilder *iface, ULONG *count ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); TRACE( "(%p)->(%p)\n", This, count ); if (!This->init) return SPERR_UNINITIALIZED; *count = This->count; return S_OK; } static HRESULT WINAPI token_enum_SetAttribs( ISpObjectTokenEnumBuilder *iface, LPCWSTR req, LPCWSTR opt) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); TRACE( "(%p)->(%s %s)\n", This, debugstr_w( req ), debugstr_w( opt ) ); if (This->init) return SPERR_ALREADY_INITIALIZED; if (req) { This->req = wcsdup( req ); if (!This->req) goto out_of_mem; } if (opt) { This->opt = wcsdup( opt ); if (!This->opt) goto out_of_mem; } This->init = TRUE; return S_OK; out_of_mem: free( This->req ); return E_OUTOFMEMORY; } static HRESULT score_attributes( ISpObjectToken *token, const WCHAR *attrs, BOOL match_all, uint64_t *score ) { ISpDataKey *attrs_key; WCHAR *attr, *attr_ctx, *buf; BOOL match[64]; unsigned int i, j; HRESULT hr; if (!attrs) { *score = 1; return S_OK; } *score = 0; if (FAILED(hr = ISpObjectToken_OpenKey( token, L"Attributes", &attrs_key ))) return hr == SPERR_NOT_FOUND ? S_OK : hr; memset( match, 0, sizeof(match) ); /* attrs is a semicolon-separated list of attribute clauses. * Each clause consists of an attribute name and an optional operator and value. * The meaning of a clause depends on the operator given: * If no operator is given, the attribute must exist. * If the operator is '=', the attribute must contain the given value. * If the operator is '!=', the attribute must not exist or contain the given value. */ if (!(buf = wcsdup( attrs ))) return E_OUTOFMEMORY; for ( attr = wcstok_s( buf, L";", &attr_ctx ), i = 0; attr && i < 64; attr = wcstok_s( NULL, L";", &attr_ctx ), i++ ) { WCHAR *p = wcspbrk( attr, L"!=" ); WCHAR op = p ? *p : L'\0'; WCHAR *value = NULL, *res; if ( p ) { if ( op == L'=' ) value = p + 1; else if ( op == L'!' ) { if ( *(p + 1) != L'=' ) { WARN( "invalid attr operator '!%lc'.\n", *(p + 1) ); hr = E_INVALIDARG; goto done; } value = p + 2; } *p = L'\0'; } hr = ISpDataKey_GetStringValue( attrs_key, attr, &res ); if ( p ) *p = op; if (SUCCEEDED(hr)) { if ( !op ) match[i] = TRUE; else { WCHAR *val, *val_ctx; match[i] = FALSE; for ( val = wcstok_s( res, L";", &val_ctx ); val && !match[i]; val = wcstok_s( NULL, L";", &val_ctx ) ) match[i] = !wcscmp( val, value ); if (op == L'!') match[i] = !match[i]; } CoTaskMemFree( res ); } else if (hr == SPERR_NOT_FOUND) { hr = S_OK; if (op == L'!') match[i] = TRUE; } else goto done; if ( match_all && !match[i] ) goto done; } if ( attr ) hr = E_INVALIDARG; else { /* Attributes in attrs are ordered from highest to lowest priority. */ for ( j = 0; j < i; j++ ) if ( match[j] ) *score |= 1ULL << (i - 1 - j); } done: free( buf ); return hr; } static BOOL grow_tokens_array( struct token_enum *This ) { struct token_with_score *new_tokens; ULONG new_cap; if (This->count < This->capacity) return TRUE; if (This->capacity > 0) { new_cap = This->capacity * 2; new_tokens = realloc( This->tokens, new_cap * sizeof(*new_tokens) ); } else { new_cap = 1; new_tokens = malloc( sizeof(*new_tokens) ); } if (!new_tokens) return FALSE; This->tokens = new_tokens; This->capacity = new_cap; return TRUE; } static HRESULT WINAPI token_enum_AddTokens( ISpObjectTokenEnumBuilder *iface, ULONG num, ISpObjectToken **tokens ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); ULONG i; uint64_t score; HRESULT hr; TRACE( "(%p)->(%lu %p)\n", iface, num, tokens ); if (!This->init) return SPERR_UNINITIALIZED; if (!tokens) return E_POINTER; for ( i = 0; i < num; i++ ) { if (!tokens[i]) return E_POINTER; hr = score_attributes( tokens[i], This->req, TRUE, &score ); if (FAILED(hr)) return hr; if (!score) continue; hr = score_attributes( tokens[i], This->opt, FALSE, &score ); if (FAILED(hr)) return hr; if (!grow_tokens_array( This )) return E_OUTOFMEMORY; ISpObjectToken_AddRef( tokens[i] ); This->tokens[This->count].token = tokens[i]; This->tokens[This->count].score = score; This->count++; } return S_OK; } static HRESULT WINAPI token_enum_AddTokensFromDataKey( ISpObjectTokenEnumBuilder *iface, ISpDataKey *data_key, LPCWSTR sub_key, LPCWSTR cat_id ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_enum_AddTokensFromTokenEnum( ISpObjectTokenEnumBuilder *iface, IEnumSpObjectTokens *token_enum ) { FIXME( "stub\n" ); return E_NOTIMPL; } static int __cdecl token_with_score_cmp( const void *a, const void *b ) { const struct token_with_score *ta = a, *tb = b; if (ta->score > tb->score) return -1; else if (ta->score < tb->score) return 1; else return 0; } static HRESULT WINAPI token_enum_Sort( ISpObjectTokenEnumBuilder *iface, LPCWSTR first ) { struct token_enum *This = impl_from_ISpObjectTokenEnumBuilder( iface ); TRACE( "(%p)->(%s).\n", iface, debugstr_w(first) ); if (!This->init) return SPERR_UNINITIALIZED; if (!This->tokens) return S_OK; if (first) { FIXME( "first != NULL is not implemented.\n" ); return E_NOTIMPL; } if (This->opt) qsort( This->tokens, This->count, sizeof(*This->tokens), token_with_score_cmp ); return S_OK; } const struct ISpObjectTokenEnumBuilderVtbl token_enum_vtbl = { token_enum_QueryInterface, token_enum_AddRef, token_enum_Release, token_enum_Next, token_enum_Skip, token_enum_Reset, token_enum_Clone, token_enum_Item, token_enum_GetCount, token_enum_SetAttribs, token_enum_AddTokens, token_enum_AddTokensFromDataKey, token_enum_AddTokensFromTokenEnum, token_enum_Sort }; HRESULT token_enum_create( IUnknown *outer, REFIID iid, void **obj ) { struct token_enum *This = malloc( sizeof(*This) ); HRESULT hr; if (!This) return E_OUTOFMEMORY; This->ISpObjectTokenEnumBuilder_iface.lpVtbl = &token_enum_vtbl; This->ref = 1; This->req = NULL; This->opt = NULL; This->init = FALSE; This->tokens = NULL; This->capacity = This->count = 0; This->index = 0; hr = ISpObjectTokenEnumBuilder_QueryInterface( &This->ISpObjectTokenEnumBuilder_iface, iid, obj ); ISpObjectTokenEnumBuilder_Release( &This->ISpObjectTokenEnumBuilder_iface ); return hr; } static HRESULT WINAPI token_QueryInterface( ISpObjectToken *iface, REFIID iid, void **obj ) { struct object_token *This = impl_from_ISpObjectToken( iface ); TRACE( "(%p)->(%s %p)\n", This, debugstr_guid( iid ), obj ); if (IsEqualIID( iid, &IID_IUnknown ) || IsEqualIID( iid, &IID_ISpDataKey ) || IsEqualIID( iid, &IID_ISpObjectToken )) { ISpObjectToken_AddRef( iface ); *obj = iface; return S_OK; } FIXME( "interface %s not implemented\n", debugstr_guid( iid ) ); *obj = NULL; return E_NOINTERFACE; } static ULONG WINAPI token_AddRef( ISpObjectToken *iface ) { struct object_token *This = impl_from_ISpObjectToken( iface ); ULONG ref = InterlockedIncrement( &This->ref ); TRACE( "(%p) ref = %lu\n", This, ref ); return ref; } static ULONG WINAPI token_Release( ISpObjectToken *iface ) { struct object_token *This = impl_from_ISpObjectToken( iface ); ULONG ref = InterlockedDecrement(&This->ref); TRACE( "(%p) ref = %lu\n", This, ref ); if (!ref) { if (This->data_key) ISpRegDataKey_Release( This->data_key ); free( This->token_id ); free( This ); } return ref; } static HRESULT WINAPI token_SetData( ISpObjectToken *iface, LPCWSTR name, ULONG size, const BYTE *data ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_GetData( ISpObjectToken *iface, LPCWSTR name, ULONG *size, BYTE *data ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_SetStringValue( ISpObjectToken *iface, LPCWSTR name, LPCWSTR value ) { struct object_token *This = impl_from_ISpObjectToken( iface ); TRACE( "%p, %s, %s\n", This, debugstr_w(name), debugstr_w(value) ); return ISpRegDataKey_SetStringValue( This->data_key, name, value ); } static HRESULT WINAPI token_GetStringValue( ISpObjectToken *iface, LPCWSTR name, LPWSTR *value ) { struct object_token *This = impl_from_ISpObjectToken( iface ); TRACE( "%p, %s, %p\n", This, debugstr_w(name), value ); return ISpRegDataKey_GetStringValue( This->data_key, name, value ); } static HRESULT WINAPI token_SetDWORD( ISpObjectToken *iface, LPCWSTR name, DWORD value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_GetDWORD( ISpObjectToken *iface, LPCWSTR name, DWORD *pdwValue ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_OpenKey( ISpObjectToken *iface, LPCWSTR name, ISpDataKey **sub_key ) { struct object_token *This = impl_from_ISpObjectToken( iface ); TRACE( "%p, %s, %p\n", This, debugstr_w(name), sub_key ); return ISpRegDataKey_OpenKey( This->data_key, name, sub_key ); } static HRESULT WINAPI token_CreateKey( ISpObjectToken *iface, LPCWSTR name, ISpDataKey **sub_key ) { struct object_token *This = impl_from_ISpObjectToken( iface ); TRACE( "%p, %s, %p\n", iface, debugstr_w(name), sub_key ); return ISpRegDataKey_CreateKey( This->data_key, name, sub_key ); } static HRESULT WINAPI token_DeleteKey( ISpObjectToken *iface, LPCWSTR name ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_DeleteValue( ISpObjectToken *iface, LPCWSTR name ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_EnumKeys( ISpObjectToken *iface, ULONG index, LPWSTR *sub_key ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_EnumValues( ISpObjectToken *iface, ULONG index, LPWSTR *value ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_SetId( ISpObjectToken *iface, LPCWSTR category_id, LPCWSTR token_id, BOOL create ) { struct object_token *This = impl_from_ISpObjectToken( iface ); BOOL res; HRESULT hr; HKEY root, key; const WCHAR *subkey; FIXME( "(%p)->(%s %s %d): semi-stub\n", This, debugstr_w( category_id ), debugstr_w(token_id), create ); if (This->data_key) return SPERR_ALREADY_INITIALIZED; if (!token_id) return E_POINTER; hr = parse_cat_id( token_id, &root, &subkey ); if (hr != S_OK) return SPERR_NOT_FOUND; if (create) res = RegCreateKeyExW( root, subkey, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, NULL); else res = RegOpenKeyExW( root, subkey, 0, KEY_ALL_ACCESS, &key ); if (res) return SPERR_NOT_FOUND; hr = create_data_key_with_hkey( key, &This->data_key ); if (FAILED(hr)) { RegCloseKey( key ); return hr; } This->token_id = wcsdup(token_id); return S_OK; } static HRESULT WINAPI token_GetId( ISpObjectToken *iface, LPWSTR *token_id ) { struct object_token *This = impl_from_ISpObjectToken( iface ); TRACE( "%p, %p\n", This, token_id); if (!This->data_key) return SPERR_UNINITIALIZED; if (!token_id) return E_POINTER; if (!This->token_id) { FIXME("Loading default category not supported.\n"); return E_POINTER; } *token_id = CoTaskMemAlloc( (wcslen(This->token_id) + 1) * sizeof(WCHAR)); if (!*token_id) return E_OUTOFMEMORY; wcscpy(*token_id, This->token_id); return S_OK; } static HRESULT WINAPI token_GetCategory( ISpObjectToken *iface, ISpObjectTokenCategory **category ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_CreateInstance( ISpObjectToken *iface, IUnknown *outer, DWORD class_context, REFIID riid, void **object ) { WCHAR *clsid_str; CLSID clsid; IUnknown *unk; ISpObjectWithToken *obj_token_iface; HRESULT hr; TRACE( "%p, %p, %#lx, %s, %p\n", iface, outer, class_context, debugstr_guid( riid ), object ); if (FAILED(hr = ISpObjectToken_GetStringValue( iface, L"CLSID", &clsid_str ))) return hr; hr = CLSIDFromString( clsid_str, &clsid ); CoTaskMemFree( clsid_str ); if (FAILED(hr)) return hr; if (FAILED(hr = CoCreateInstance( &clsid, outer, class_context, &IID_IUnknown, (void **)&unk ))) return hr; /* Call ISpObjectWithToken::SetObjectToken if the interface is available. */ if (SUCCEEDED(IUnknown_QueryInterface( unk, &IID_ISpObjectWithToken, (void **)&obj_token_iface ))) { hr = ISpObjectWithToken_SetObjectToken( obj_token_iface, iface ); ISpObjectWithToken_Release( obj_token_iface ); if (FAILED(hr)) goto done; } hr = IUnknown_QueryInterface( unk, riid, object ); done: IUnknown_Release( unk ); return hr; } static HRESULT WINAPI token_GetStorageFileName( ISpObjectToken *iface, REFCLSID caller, LPCWSTR key_name, LPCWSTR filename, ULONG folder, LPWSTR *filepath ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_RemoveStorageFileName( ISpObjectToken *iface, REFCLSID caller, LPCWSTR key_name, BOOL delete_file ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_Remove( ISpObjectToken *iface, REFCLSID caller ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_IsUISupported( ISpObjectToken *iface, LPCWSTR ui_type, void *extra_data, ULONG extra_data_size, IUnknown *object, BOOL *supported ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_DisplayUI( ISpObjectToken *iface, HWND parent, LPCWSTR title, LPCWSTR ui_type, void *extra_data, ULONG extra_data_size, IUnknown *object ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI token_MatchesAttributes( ISpObjectToken *iface, LPCWSTR attributes, BOOL *matches ) { FIXME( "stub\n" ); return E_NOTIMPL; } const struct ISpObjectTokenVtbl token_vtbl = { token_QueryInterface, token_AddRef, token_Release, token_SetData, token_GetData, token_SetStringValue, token_GetStringValue, token_SetDWORD, token_GetDWORD, token_OpenKey, token_CreateKey, token_DeleteKey, token_DeleteValue, token_EnumKeys, token_EnumValues, token_SetId, token_GetId, token_GetCategory, token_CreateInstance, token_GetStorageFileName, token_RemoveStorageFileName, token_Remove, token_IsUISupported, token_DisplayUI, token_MatchesAttributes }; HRESULT token_create( IUnknown *outer, REFIID iid, void **obj ) { struct object_token *This = malloc( sizeof(*This) ); HRESULT hr; if (!This) return E_OUTOFMEMORY; This->ISpObjectToken_iface.lpVtbl = &token_vtbl; This->ref = 1; This->data_key = NULL; This->token_id = NULL; hr = ISpObjectToken_QueryInterface( &This->ISpObjectToken_iface, iid, obj ); ISpObjectToken_Release( &This->ISpObjectToken_iface ); return hr; }