/* * 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 #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; BOOL read_only; }; static struct data_key *impl_from_ISpRegDataKey( ISpRegDataKey *iface ) { return CONTAINING_RECORD( iface, struct data_key, ISpRegDataKey_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 = %u\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 = %u\n", This, ref ); if (!ref) { if (This->key) RegCloseKey( This->key ); heap_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 ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_GetStringValue( ISpRegDataKey *iface, LPCWSTR name, LPWSTR *value ) { FIXME( "stub\n" ); return E_NOTIMPL; } 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 ) { FIXME( "stub\n" ); return E_NOTIMPL; } static HRESULT WINAPI data_key_CreateKey( ISpRegDataKey *iface, LPCWSTR name, ISpDataKey **sub_key ) { FIXME( "stub\n" ); return E_NOTIMPL; } 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; This->key = key; This->read_only = read_only; 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 = heap_alloc( sizeof(*This) ); HRESULT hr; if (!This) return E_OUTOFMEMORY; This->ISpRegDataKey_iface.lpVtbl = &data_key_vtbl; This->ref = 1; This->key = NULL; This->read_only = FALSE; 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; }; 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 = %u\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 = %u\n", This, ref ); if (!ref) { if (This->data_key) ISpRegDataKey_Release( This->data_key ); heap_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 ) { static const WCHAR HKLM[] = {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E','\\'}; static const WCHAR HKCU[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\'}; struct table { const WCHAR *name; int size; HKEY key; } table[] = { { HKLM, sizeof(HKLM), HKEY_LOCAL_MACHINE }, { HKCU, sizeof(HKCU), HKEY_CURRENT_USER }, { NULL } }; struct table *ptr; int len = lstrlenW( str ); for (ptr = table; ptr->name; ptr++) { if (len >= ptr->size / sizeof(ptr->name[0]) && !memcmp( str, ptr->name, ptr->size )) { *root = ptr->key; *sub_key = str + ptr->size / sizeof(ptr->name[0]); return S_OK; } } return S_FALSE; } 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) FIXME( "Ignoring create\n" ); res = RegOpenKeyExW( root, subkey, 0, KEY_ALL_ACCESS, &key ); if (res) return SPERR_INVALID_REGISTRY_KEY; hr = CoCreateInstance( &CLSID_SpDataKey, NULL, CLSCTX_ALL, &IID_ISpRegDataKey, (void **)&This->data_key ); if (FAILED(hr)) goto fail; hr = ISpRegDataKey_SetKey( This->data_key, key, FALSE ); if (FAILED(hr)) goto fail; return hr; fail: RegCloseKey( key ); 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; } 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; HRESULT hr; FIXME( "(%p)->(%s %s %p): semi-stub\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; /* FIXME: Build the enumerator */ hr = ISpObjectTokenEnumBuilder_QueryInterface( builder, &IID_IEnumSpObjectTokens, (void **)enum_tokens ); fail: ISpObjectTokenEnumBuilder_Release( builder ); 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 ) { FIXME( "stub\n" ); return E_NOTIMPL; } 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 = heap_alloc( sizeof(*This) ); HRESULT hr; if (!This) return E_OUTOFMEMORY; This->ISpObjectTokenCategory_iface.lpVtbl = &token_category_vtbl; This->ref = 1; This->data_key = NULL; hr = ISpObjectTokenCategory_QueryInterface( &This->ISpObjectTokenCategory_iface, iid, obj ); ISpObjectTokenCategory_Release( &This->ISpObjectTokenCategory_iface ); return hr; } struct token_enum { ISpObjectTokenEnumBuilder ISpObjectTokenEnumBuilder_iface; LONG ref; BOOL init; WCHAR *req, *opt; ULONG count; }; static struct token_enum *impl_from_ISpObjectTokenEnumBuilder( ISpObjectTokenEnumBuilder *iface ) { return CONTAINING_RECORD( iface, struct token_enum, ISpObjectTokenEnumBuilder_iface ); } 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 = %u\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 = %u\n", This, ref ); if (!ref) { heap_free( This->req ); heap_free( This->opt ); heap_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 ); TRACE( "(%p)->(%u %p %p)\n", This, num, tokens, fetched ); if (!This->init) return SPERR_UNINITIALIZED; FIXME( "semi-stub: Returning an empty enumerator\n" ); if (fetched) *fetched = 0; return 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 ) { FIXME( "stub\n" ); return E_NOTIMPL; } 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 = heap_strdupW( req ); if (!This->req) goto out_of_mem; } if (opt) { This->opt = heap_strdupW( opt ); if (!This->opt) goto out_of_mem; } This->init = TRUE; return S_OK; out_of_mem: heap_free( This->req ); return E_OUTOFMEMORY; } static HRESULT WINAPI token_enum_AddTokens( ISpObjectTokenEnumBuilder *iface, ULONG num, ISpObjectToken **tokens ) { FIXME( "stub\n" ); return E_NOTIMPL; } 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 HRESULT WINAPI token_enum_Sort( ISpObjectTokenEnumBuilder *iface, LPCWSTR first ) { FIXME( "stub\n" ); return E_NOTIMPL; } 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 = heap_alloc( 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->count = 0; hr = ISpObjectTokenEnumBuilder_QueryInterface( &This->ISpObjectTokenEnumBuilder_iface, iid, obj ); ISpObjectTokenEnumBuilder_Release( &This->ISpObjectTokenEnumBuilder_iface ); return hr; }