/*
 * Copyright 2012 Hans Leidekker for CodeWeavers
 *
 * 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
 */

#define COBJMACROS

#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "ole2.h"
#include "wbemcli.h"
#include "wmiutils.h"

#include "wine/debug.h"
#include "wmiutils_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(wmiutils);

struct keylist
{
    IWbemPathKeyList IWbemPathKeyList_iface;
    IWbemPath       *parent;
    LONG             refs;
};

struct key
{
    WCHAR *name;
    int    len_name;
    WCHAR *value;
    int    len_value;
};

struct path
{
    IWbemPath        IWbemPath_iface;
    LONG             refs;
    CRITICAL_SECTION cs;
    WCHAR           *text;
    int              len_text;
    WCHAR           *server;
    int              len_server;
    WCHAR          **namespaces;
    int             *len_namespaces;
    int              num_namespaces;
    WCHAR           *class;
    int              len_class;
    struct key      *keys;
    unsigned int     num_keys;
    ULONGLONG        flags;
};

static inline struct keylist *impl_from_IWbemPathKeyList( IWbemPathKeyList *iface )
{
    return CONTAINING_RECORD(iface, struct keylist, IWbemPathKeyList_iface);
}

static inline struct path *impl_from_IWbemPath( IWbemPath *iface )
{
    return CONTAINING_RECORD(iface, struct path, IWbemPath_iface);
}

static ULONG WINAPI keylist_AddRef(
    IWbemPathKeyList *iface )
{
    struct keylist *keylist = impl_from_IWbemPathKeyList( iface );
    return InterlockedIncrement( &keylist->refs );
}

static ULONG WINAPI keylist_Release(
    IWbemPathKeyList *iface )
{
    struct keylist *keylist = impl_from_IWbemPathKeyList( iface );
    LONG refs = InterlockedDecrement( &keylist->refs );
    if (!refs)
    {
        TRACE("destroying %p\n", keylist);
        IWbemPath_Release( keylist->parent );
        heap_free( keylist );
    }
    return refs;
}

static HRESULT WINAPI keylist_QueryInterface(
    IWbemPathKeyList *iface,
    REFIID riid,
    void **ppvObject )
{
    struct keylist *keylist = impl_from_IWbemPathKeyList( iface );

    TRACE("%p, %s, %p\n", keylist, debugstr_guid(riid), ppvObject);

    if (IsEqualGUID( riid, &IID_IWbemPathKeyList ) ||
        IsEqualGUID( riid, &IID_IUnknown ))
    {
        *ppvObject = iface;
    }
    else
    {
        FIXME("interface %s not implemented\n", debugstr_guid(riid));
        return E_NOINTERFACE;
    }
    IWbemPathKeyList_AddRef( iface );
    return S_OK;
}

static HRESULT WINAPI keylist_GetCount(
    IWbemPathKeyList *iface,
    ULONG *puKeyCount )
{
    struct keylist *keylist = impl_from_IWbemPathKeyList( iface );
    struct path *parent = impl_from_IWbemPath( keylist->parent );

    TRACE("%p, %p\n", iface, puKeyCount);

    if (!puKeyCount) return WBEM_E_INVALID_PARAMETER;

    EnterCriticalSection( &parent->cs );

    *puKeyCount = parent->num_keys;

    LeaveCriticalSection( &parent->cs );
    return S_OK;
}

static HRESULT WINAPI keylist_SetKey(
    IWbemPathKeyList *iface,
    LPCWSTR wszName,
    ULONG uFlags,
    ULONG uCimType,
    LPVOID pKeyVal )
{
    FIXME("%p, %s, 0x%x, %u, %p\n", iface, debugstr_w(wszName), uFlags, uCimType, pKeyVal);
    return E_NOTIMPL;
}

static HRESULT WINAPI keylist_SetKey2(
    IWbemPathKeyList *iface,
    LPCWSTR wszName,
    ULONG uFlags,
    ULONG uCimType,
    VARIANT *pKeyVal )
{
    FIXME("%p, %s, 0x%x, %u, %p\n", iface, debugstr_w(wszName), uFlags, uCimType, pKeyVal);
    return E_NOTIMPL;
}

static HRESULT WINAPI keylist_GetKey(
    IWbemPathKeyList *iface,
    ULONG uKeyIx,
    ULONG uFlags,
    ULONG *puNameBufSize,
    LPWSTR pszKeyName,
    ULONG *puKeyValBufSize,
    LPVOID pKeyVal,
    ULONG *puApparentCimType )
{
    FIXME("%p, %u, 0x%x, %p, %p, %p, %p, %p\n", iface, uKeyIx, uFlags, puNameBufSize,
          pszKeyName, puKeyValBufSize, pKeyVal, puApparentCimType);
    return E_NOTIMPL;
}

static HRESULT WINAPI keylist_GetKey2(
    IWbemPathKeyList *iface,
    ULONG uKeyIx,
    ULONG uFlags,
    ULONG *puNameBufSize,
    LPWSTR pszKeyName,
    VARIANT *pKeyValue,
    ULONG *puApparentCimType )
{
    FIXME("%p, %u, 0x%x, %p, %p, %p, %p\n", iface, uKeyIx, uFlags, puNameBufSize,
          pszKeyName, pKeyValue, puApparentCimType);
    return E_NOTIMPL;
}

static HRESULT WINAPI keylist_RemoveKey(
    IWbemPathKeyList *iface,
    LPCWSTR wszName,
    ULONG uFlags )
{
    FIXME("%p, %s, 0x%x\n", iface, debugstr_w(wszName), uFlags);
    return E_NOTIMPL;
}

static void free_keys( struct key *keys, unsigned int count )
{
    unsigned int i;

    for (i = 0; i < count; i++)
    {
        heap_free( keys[i].name );
        heap_free( keys[i].value );
    }
    heap_free( keys );
}

static HRESULT WINAPI keylist_RemoveAllKeys(
    IWbemPathKeyList *iface,
    ULONG uFlags )
{
    struct keylist *keylist = impl_from_IWbemPathKeyList( iface );
    struct path *parent = impl_from_IWbemPath( keylist->parent );

    TRACE("%p, 0x%x\n", iface, uFlags);

    if (uFlags) return WBEM_E_INVALID_PARAMETER;

    EnterCriticalSection( &parent->cs );

    free_keys( parent->keys, parent->num_keys );
    parent->num_keys = 0;
    parent->keys = NULL;

    LeaveCriticalSection( &parent->cs );
    return S_OK;
}

static HRESULT WINAPI keylist_MakeSingleton(
    IWbemPathKeyList *iface,
    boolean bSet )
{
    FIXME("%p, %d\n", iface, bSet);
    return E_NOTIMPL;
}

static HRESULT WINAPI keylist_GetInfo(
    IWbemPathKeyList *iface,
    ULONG uRequestedInfo,
    ULONGLONG *puResponse )
{
    FIXME("%p, %u, %p\n", iface, uRequestedInfo, puResponse);
    return E_NOTIMPL;
}

static HRESULT WINAPI keylist_GetText(
    IWbemPathKeyList *iface,
    LONG lFlags,
    ULONG *puBuffLength,
    LPWSTR pszText )
{
    FIXME("%p, 0x%x, %p, %p\n", iface, lFlags, puBuffLength, pszText);
    return E_NOTIMPL;
}

static const struct IWbemPathKeyListVtbl keylist_vtbl =
{
    keylist_QueryInterface,
    keylist_AddRef,
    keylist_Release,
    keylist_GetCount,
    keylist_SetKey,
    keylist_SetKey2,
    keylist_GetKey,
    keylist_GetKey2,
    keylist_RemoveKey,
    keylist_RemoveAllKeys,
    keylist_MakeSingleton,
    keylist_GetInfo,
    keylist_GetText
};

static HRESULT WbemPathKeyList_create( IWbemPath *parent, LPVOID *ppObj )
{
    struct keylist *keylist;

    TRACE("%p\n", ppObj);

    if (!(keylist = heap_alloc( sizeof(*keylist) ))) return E_OUTOFMEMORY;

    keylist->IWbemPathKeyList_iface.lpVtbl = &keylist_vtbl;
    keylist->refs = 1;
    keylist->parent = parent;
    IWbemPath_AddRef( keylist->parent );

    *ppObj = &keylist->IWbemPathKeyList_iface;

    TRACE("returning iface %p\n", *ppObj);
    return S_OK;
}

static void init_path( struct path *path )
{
    path->text           = NULL;
    path->len_text       = 0;
    path->server         = NULL;
    path->len_server     = 0;
    path->namespaces     = NULL;
    path->len_namespaces = NULL;
    path->num_namespaces = 0;
    path->class          = NULL;
    path->len_class      = 0;
    path->keys           = NULL;
    path->num_keys       = 0;
    path->flags          = 0;
}

static void clear_path( struct path *path )
{
    unsigned int i;

    heap_free( path->text );
    heap_free( path->server );
    for (i = 0; i < path->num_namespaces; i++) heap_free( path->namespaces[i] );
    heap_free( path->namespaces );
    heap_free( path->len_namespaces );
    heap_free( path->class );
    free_keys( path->keys, path->num_keys );
    init_path( path );
}

static ULONG WINAPI path_AddRef(
    IWbemPath *iface )
{
    struct path *path = impl_from_IWbemPath( iface );
    return InterlockedIncrement( &path->refs );
}

static ULONG WINAPI path_Release(
    IWbemPath *iface )
{
    struct path *path = impl_from_IWbemPath( iface );
    LONG refs = InterlockedDecrement( &path->refs );
    if (!refs)
    {
        TRACE("destroying %p\n", path);
        clear_path( path );
        path->cs.DebugInfo->Spare[0] = 0;
        DeleteCriticalSection( &path->cs );
        heap_free( path );
    }
    return refs;
}

static HRESULT WINAPI path_QueryInterface(
    IWbemPath *iface,
    REFIID riid,
    void **ppvObject )
{
    struct path *path = impl_from_IWbemPath( iface );

    TRACE("%p, %s, %p\n", path, debugstr_guid( riid ), ppvObject );

    if ( IsEqualGUID( riid, &IID_IWbemPath ) ||
         IsEqualGUID( riid, &IID_IUnknown ) )
    {
        *ppvObject = iface;
    }
    else
    {
        FIXME("interface %s not implemented\n", debugstr_guid(riid));
        return E_NOINTERFACE;
    }
    IWbemPath_AddRef( iface );
    return S_OK;
}

static HRESULT parse_key( struct key *key, const WCHAR *str, unsigned int *ret_len )
{
    const WCHAR *p, *q;
    unsigned int len;

    p = q = str;
    while (*q && *q != '=')
    {
        if (*q == ',' || iswspace( *q )) return WBEM_E_INVALID_PARAMETER;
        q++;
    }
    len = q - p;
    if (!(key->name = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return E_OUTOFMEMORY;
    memcpy( key->name, p, len * sizeof(WCHAR) );
    key->name[len] = 0;
    key->len_name = len;

    p = ++q;
    if (!*p || *p == ',' || iswspace( *p )) return WBEM_E_INVALID_PARAMETER;

    while (*q && *q != ',') q++;
    len = q - p;
    if (!(key->value = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return E_OUTOFMEMORY;
    memcpy( key->value, p, len * sizeof(WCHAR) );
    key->value[len] = 0;
    key->len_value = len;

    *ret_len = q - str;
    if (*q == ',') (*ret_len)++;
    return S_OK;
}

static HRESULT parse_text( struct path *path, ULONG mode, const WCHAR *text )
{
    HRESULT hr = E_OUTOFMEMORY;
    const WCHAR *p, *q;
    unsigned int i, len;

    p = q = text;
    if ((p[0] == '\\' && p[1] == '\\') || (p[0] == '/' && p[1] == '/'))
    {
        p += 2;
        q = p;
        while (*q && *q != '\\' && *q != '/') q++;
        len = q - p;
        if (!(path->server = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto done;
        memcpy( path->server, p, len * sizeof(WCHAR) );
        path->server[len] = 0;
        path->len_server = len;
        path->flags |= WBEMPATH_INFO_PATH_HAD_SERVER;
    }
    p = q;
    if (wcschr( p, '\\' ) || wcschr( p, '/' ))
    {
        if (*q != '\\' && *q != '/' && *q != ':')
        {
            path->num_namespaces = 1;
            q++;
        }
        while (*q && *q != ':')
        {
            if (*q == '\\' || *q == '/') path->num_namespaces++;
            q++;
        }
    }
    if (path->num_namespaces)
    {
        if (!(path->namespaces = heap_alloc( path->num_namespaces * sizeof(WCHAR *) ))) goto done;
        if (!(path->len_namespaces = heap_alloc( path->num_namespaces * sizeof(int) ))) goto done;

        i = 0;
        q = p;
        if (*q && *q != '\\' && *q != '/' && *q != ':')
        {
            p = q;
            while (*p && *p != '\\' && *p != '/' && *p != ':') p++;
            len = p - q;
            if (!(path->namespaces[i] = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto done;
            memcpy( path->namespaces[i], q, len * sizeof(WCHAR) );
            path->namespaces[i][len] = 0;
            path->len_namespaces[i] = len;
            q = p;
            i++;
        }
        while (*q && *q != ':')
        {
            if (*q == '\\' || *q == '/')
            {
                p = q + 1;
                while (*p && *p != '\\' && *p != '/' && *p != ':') p++;
                len = p - q - 1;
                if (!(path->namespaces[i] = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto done;
                memcpy( path->namespaces[i], q + 1, len * sizeof(WCHAR) );
                path->namespaces[i][len] = 0;
                path->len_namespaces[i] = len;
                i++;
            }
            q++;
        }
    }
    if (*q == ':') q++;
    p = q;
    while (*q && *q != '.') q++;
    len = q - p;
    if (!(path->class = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto done;
    memcpy( path->class, p, len * sizeof(WCHAR) );
    path->class[len] = 0;
    path->len_class = len;

    if (*q == '.')
    {
        p = ++q;
        path->num_keys++;
        while (*q)
        {
            if (*q == ',') path->num_keys++;
            q++;
        }
        if (!(path->keys = heap_alloc_zero( path->num_keys * sizeof(struct key) ))) goto done;
        i = 0;
        q = p;
        while (*q)
        {
            if (i >= path->num_keys) break;
            hr = parse_key( &path->keys[i], q, &len );
            if (hr != S_OK) goto done;
            q += len;
            i++;
        }
    }
    hr = S_OK;

done:
    if (hr != S_OK) clear_path( path );
    else path->flags |= WBEMPATH_INFO_CIM_COMPLIANT | WBEMPATH_INFO_V2_COMPLIANT;
    return hr;
}

static HRESULT WINAPI path_SetText(
    IWbemPath *iface,
    ULONG uMode,
    LPCWSTR pszPath)
{
    struct path *path = impl_from_IWbemPath( iface );
    HRESULT hr = S_OK;
    int len;

    TRACE("%p, %u, %s\n", iface, uMode, debugstr_w(pszPath));

    if (!uMode || !pszPath) return WBEM_E_INVALID_PARAMETER;

    EnterCriticalSection( &path->cs );

    clear_path( path );
    if (!pszPath[0]) goto done;
    if ((hr = parse_text( path, uMode, pszPath )) != S_OK) goto done;

    len = lstrlenW( pszPath );
    if (!(path->text = heap_alloc( (len + 1) * sizeof(WCHAR) )))
    {
        clear_path( path );
        hr = E_OUTOFMEMORY;
        goto done;
    }
    lstrcpyW( path->text, pszPath );
    path->len_text = len;

done:
    LeaveCriticalSection( &path->cs );
    return hr;
}

static WCHAR *build_namespace( struct path *path, int *len, BOOL leading_slash )
{
    WCHAR *ret, *p;
    int i;

    *len = 0;
    for (i = 0; i < path->num_namespaces; i++)
    {
        if (i > 0 || leading_slash) *len += 1;
        *len += path->len_namespaces[i];
    }
    if (!(p = ret = heap_alloc( (*len + 1) * sizeof(WCHAR) ))) return NULL;
    for (i = 0; i < path->num_namespaces; i++)
    {
        if (i > 0 || leading_slash) *p++ = '\\';
        memcpy( p, path->namespaces[i], path->len_namespaces[i] * sizeof(WCHAR) );
        p += path->len_namespaces[i];
    }
    *p = 0;
    return ret;
}

static WCHAR *build_server( struct path *path, int *len )
{
    WCHAR *ret, *p;

    *len = 0;
    if (path->len_server) *len += 2 + path->len_server;
    else *len += 3;
    if (!(p = ret = heap_alloc( (*len + 1) * sizeof(WCHAR) ))) return NULL;
    if (path->len_server)
    {
        p[0] = p[1] = '\\';
        lstrcpyW( p + 2, path->server );
    }
    else
    {
        p[0] = p[1] = '\\';
        p[2] = '.';
        p[3] = 0;
    }
    return ret;
}

static WCHAR *build_keylist( struct path *path, int *len )
{
    WCHAR *ret, *p;
    unsigned int i;

    *len = 0;
    for (i = 0; i < path->num_keys; i++)
    {
        if (i > 0) *len += 1;
        *len += path->keys[i].len_name + path->keys[i].len_value + 1;
    }
    if (!(p = ret = heap_alloc( (*len + 1) * sizeof(WCHAR) ))) return NULL;
    for (i = 0; i < path->num_keys; i++)
    {
        if (i > 0) *p++ = ',';
        memcpy( p, path->keys[i].name, path->keys[i].len_name * sizeof(WCHAR) );
        p += path->keys[i].len_name;
        *p++ = '=';
        memcpy( p, path->keys[i].value, path->keys[i].len_value * sizeof(WCHAR) );
        p += path->keys[i].len_value;
    }
    *p = 0;
    return ret;
}

static WCHAR *build_path( struct path *path, LONG flags, int *len )
{
    *len = 0;
    switch (flags)
    {
    case 0:
    {
        int len_namespace, len_keylist;
        WCHAR *ret, *namespace = build_namespace( path, &len_namespace, FALSE );
        WCHAR *keylist = build_keylist( path, &len_keylist );

        if (!namespace || !keylist)
        {
            heap_free( namespace );
            heap_free( keylist );
            return NULL;
        }
        *len = len_namespace;
        if (path->len_class)
        {
            *len += path->len_class + 1;
            if (path->num_keys) *len += len_keylist + 1;
        }
        if (!(ret = heap_alloc( (*len + 1) * sizeof(WCHAR) )))
        {
            heap_free( namespace );
            heap_free( keylist );
            return NULL;
        }
        lstrcpyW( ret, namespace );
        if (path->len_class)
        {
            ret[len_namespace] = ':';
            lstrcpyW( ret + len_namespace + 1, path->class );
            if (path->num_keys)
            {
                ret[len_namespace + path->len_class + 1] = '.';
                lstrcpyW( ret + len_namespace + path->len_class + 2, keylist );
            }
        }
        heap_free( namespace );
        heap_free( keylist );
        return ret;

    }
    case WBEMPATH_GET_RELATIVE_ONLY:
    {
        int len_keylist;
        WCHAR *ret, *keylist;

        if (!path->len_class) return NULL;
        if (!(keylist = build_keylist( path, &len_keylist ))) return NULL;

        *len = path->len_class;
        if (path->num_keys) *len += len_keylist + 1;
        if (!(ret = heap_alloc( (*len + 1) * sizeof(WCHAR) )))
        {
            heap_free( keylist );
            return NULL;
        }
        lstrcpyW( ret, path->class );
        if (path->num_keys)
        {
            ret[path->len_class] = '.';
            lstrcpyW( ret + path->len_class + 1, keylist );
        }
        heap_free( keylist );
        return ret;
    }
    case WBEMPATH_GET_SERVER_TOO:
    {
        int len_namespace, len_server, len_keylist;
        WCHAR *p, *ret, *namespace = build_namespace( path, &len_namespace, TRUE );
        WCHAR *server = build_server( path, &len_server );
        WCHAR *keylist = build_keylist( path, &len_keylist );

        if (!namespace || !server || !keylist)
        {
            heap_free( namespace );
            heap_free( server );
            heap_free( keylist );
            return NULL;
        }
        *len = len_namespace + len_server;
        if (path->len_class)
        {
            *len += path->len_class + 1;
            if (path->num_keys) *len += len_keylist + 1;
        }
        if (!(p = ret = heap_alloc( (*len + 1) * sizeof(WCHAR) )))
        {
            heap_free( namespace );
            heap_free( server );
            heap_free( keylist );
            return NULL;
        }
        lstrcpyW( p, server );
        p += len_server;
        lstrcpyW( p, namespace );
        p += len_namespace;
        if (path->len_class)
        {
            *p++ = ':';
            lstrcpyW( p, path->class );
            if (path->num_keys)
            {
                p[path->len_class] = '.';
                lstrcpyW( p + path->len_class + 1, keylist );
            }
        }
        heap_free( namespace );
        heap_free( server );
        heap_free( keylist );
        return ret;
    }
    case WBEMPATH_GET_SERVER_AND_NAMESPACE_ONLY:
    {
        int len_namespace, len_server;
        WCHAR *p, *ret, *namespace = build_namespace( path, &len_namespace, TRUE );
        WCHAR *server = build_server( path, &len_server );

        if (!namespace || !server)
        {
            heap_free( namespace );
            heap_free( server );
            return NULL;
        }
        *len = len_namespace + len_server;
        if (!(p = ret = heap_alloc( (*len + 1) * sizeof(WCHAR) )))
        {
            heap_free( namespace );
            heap_free( server );
            return NULL;
        }
        lstrcpyW( p, server );
        p += len_server;
        lstrcpyW( p, namespace );
        heap_free( namespace );
        heap_free( server );
        return ret;
    }
    case WBEMPATH_GET_NAMESPACE_ONLY:
        return build_namespace( path, len, FALSE );

    case WBEMPATH_GET_ORIGINAL:
        if (!path->len_text) return NULL;
        *len = path->len_text;
        return strdupW( path->text );

    default:
        ERR("unhandled flags 0x%x\n", flags);
        return NULL;
    }
}

static HRESULT WINAPI path_GetText(
    IWbemPath *iface,
    LONG lFlags,
    ULONG *puBufferLength,
    LPWSTR pszText)
{
    struct path *path = impl_from_IWbemPath( iface );
    HRESULT hr = S_OK;
    WCHAR *str;
    int len;

    TRACE("%p, 0x%x, %p, %p\n", iface, lFlags, puBufferLength, pszText);

    if (!puBufferLength) return WBEM_E_INVALID_PARAMETER;

    EnterCriticalSection( &path->cs );

    str = build_path( path, lFlags, &len );
    if (*puBufferLength < len + 1)
    {
        *puBufferLength = len + 1;
        goto done;
    }
    if (!pszText)
    {
        hr = WBEM_E_INVALID_PARAMETER;
        goto done;
    }
    if (str) lstrcpyW( pszText, str );
    else pszText[0] = 0;
    *puBufferLength = len + 1;

    TRACE("returning %s\n", debugstr_w(pszText));

done:
    heap_free( str );
    LeaveCriticalSection( &path->cs );
    return hr;
}

static HRESULT WINAPI path_GetInfo(
    IWbemPath *iface,
    ULONG info,
    ULONGLONG *response)
{
    struct path *path = impl_from_IWbemPath( iface );

    TRACE("%p, %u, %p\n", iface, info, response);

    if (info || !response) return WBEM_E_INVALID_PARAMETER;

    FIXME("some flags are not implemented\n");

    EnterCriticalSection( &path->cs );

    *response = path->flags;
    if (!path->server || (path->len_server == 1 && path->server[0] == '.'))
        *response |= WBEMPATH_INFO_ANON_LOCAL_MACHINE;
    else
        *response |= WBEMPATH_INFO_HAS_MACHINE_NAME;

    if (!path->class)
        *response |= WBEMPATH_INFO_SERVER_NAMESPACE_ONLY;
    else
    {
        *response |= WBEMPATH_INFO_HAS_SUBSCOPES;
        if (path->num_keys)
            *response |= WBEMPATH_INFO_IS_INST_REF;
        else
            *response |= WBEMPATH_INFO_IS_CLASS_REF;
    }

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_SetServer(
    IWbemPath *iface,
    LPCWSTR name)
{
    struct path *path = impl_from_IWbemPath( iface );
    static const ULONGLONG flags =
        WBEMPATH_INFO_PATH_HAD_SERVER | WBEMPATH_INFO_V1_COMPLIANT |
        WBEMPATH_INFO_V2_COMPLIANT | WBEMPATH_INFO_CIM_COMPLIANT;
    WCHAR *server;

    TRACE("%p, %s\n", iface, debugstr_w(name));

    EnterCriticalSection( &path->cs );

    if (name)
    {
        if (!(server = strdupW( name )))
        {
            LeaveCriticalSection( &path->cs );
            return WBEM_E_OUT_OF_MEMORY;
        }
        heap_free( path->server );
        path->server = server;
        path->len_server = lstrlenW( path->server );
        path->flags |= flags;
    }
    else
    {
        heap_free( path->server );
        path->server = NULL;
        path->len_server = 0;
        path->flags &= ~flags;
    }

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_GetServer(
    IWbemPath *iface,
    ULONG *len,
    LPWSTR name)
{
    struct path *path = impl_from_IWbemPath( iface );

    TRACE("%p, %p, %p\n", iface, len, name);

    if (!len || (*len && !name)) return WBEM_E_INVALID_PARAMETER;

    EnterCriticalSection( &path->cs );

    if (!path->server)
    {
        LeaveCriticalSection( &path->cs );
        return WBEM_E_NOT_AVAILABLE;
    }
    if (*len > path->len_server) lstrcpyW( name, path->server );
    *len = path->len_server + 1;

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_GetNamespaceCount(
    IWbemPath *iface,
    ULONG *puCount)
{
    struct path *path = impl_from_IWbemPath( iface );

    TRACE("%p, %p\n", iface, puCount);

    if (!puCount) return WBEM_E_INVALID_PARAMETER;

    EnterCriticalSection( &path->cs );
    *puCount = path->num_namespaces;
    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_SetNamespaceAt(
    IWbemPath *iface,
    ULONG idx,
    LPCWSTR name)
{
    struct path *path = impl_from_IWbemPath( iface );
    static const ULONGLONG flags =
        WBEMPATH_INFO_V1_COMPLIANT | WBEMPATH_INFO_V2_COMPLIANT |
        WBEMPATH_INFO_CIM_COMPLIANT;
    int i, *tmp_len;
    WCHAR **tmp, *new;
    DWORD size;

    TRACE("%p, %u, %s\n", iface, idx, debugstr_w(name));

    EnterCriticalSection( &path->cs );

    if (idx > path->num_namespaces || !name)
    {
        LeaveCriticalSection( &path->cs );
        return WBEM_E_INVALID_PARAMETER;
    }
    if (!(new = strdupW( name )))
    {
        LeaveCriticalSection( &path->cs );
        return WBEM_E_OUT_OF_MEMORY;
    }
    size = (path->num_namespaces + 1) * sizeof(WCHAR *);
    if (path->namespaces) tmp = heap_realloc( path->namespaces, size );
    else tmp = heap_alloc( size );
    if (!tmp)
    {
        heap_free( new );
        LeaveCriticalSection( &path->cs );
        return WBEM_E_OUT_OF_MEMORY;
    }
    path->namespaces = tmp;
    size = (path->num_namespaces + 1) * sizeof(int);
    if (path->len_namespaces) tmp_len = heap_realloc( path->len_namespaces, size );
    else tmp_len = heap_alloc( size );
    if (!tmp_len)
    {
        heap_free( new );
        LeaveCriticalSection( &path->cs );
        return WBEM_E_OUT_OF_MEMORY;
    }
    path->len_namespaces = tmp_len;
    for (i = idx; i < path->num_namespaces; i++)
    {
        path->namespaces[i + 1] = path->namespaces[i];
        path->len_namespaces[i + 1] = path->len_namespaces[i];
    }
    path->namespaces[idx] = new;
    path->len_namespaces[idx] = lstrlenW( new );
    path->num_namespaces++;
    path->flags |= flags;

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_GetNamespaceAt(
    IWbemPath *iface,
    ULONG idx,
    ULONG *len,
    LPWSTR name)
{
    struct path *path = impl_from_IWbemPath( iface );

    TRACE("%p, %u, %p, %p\n", iface, idx, len, name);

    EnterCriticalSection( &path->cs );

    if (!len || (*len && !name) || idx >= path->num_namespaces)
    {
        LeaveCriticalSection( &path->cs );
        return WBEM_E_INVALID_PARAMETER;
    }
    if (*len > path->len_namespaces[idx]) lstrcpyW( name, path->namespaces[idx] );
    *len = path->len_namespaces[idx] + 1;

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_RemoveNamespaceAt(
    IWbemPath *iface,
    ULONG idx)
{
    struct path *path = impl_from_IWbemPath( iface );

    TRACE("%p, %u\n", iface, idx);

    EnterCriticalSection( &path->cs );

    if (idx >= path->num_namespaces)
    {
        LeaveCriticalSection( &path->cs );
        return WBEM_E_INVALID_PARAMETER;
    }
    heap_free( path->namespaces[idx] );
    while (idx < path->num_namespaces - 1)
    {
        path->namespaces[idx] = path->namespaces[idx + 1];
        path->len_namespaces[idx] = path->len_namespaces[idx + 1];
        idx++;
    }
    path->num_namespaces--;

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_RemoveAllNamespaces(
    IWbemPath *iface)
{
    struct path *path = impl_from_IWbemPath( iface );
    int i;

    TRACE("%p\n", iface);

    EnterCriticalSection( &path->cs );

    for (i = 0; i < path->num_namespaces; i++) heap_free( path->namespaces[i] );
    path->num_namespaces = 0;
    heap_free( path->namespaces );
    path->namespaces = NULL;
    heap_free( path->len_namespaces );
    path->len_namespaces = NULL;

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_GetScopeCount(
    IWbemPath *iface,
    ULONG *puCount)
{
    FIXME("%p, %p\n", iface, puCount);
    return E_NOTIMPL;
}

static HRESULT WINAPI path_SetScope(
    IWbemPath *iface,
    ULONG uIndex,
    LPWSTR pszClass)
{
    FIXME("%p, %u, %s\n", iface, uIndex, debugstr_w(pszClass));
    return E_NOTIMPL;
}

static HRESULT WINAPI path_SetScopeFromText(
    IWbemPath *iface,
    ULONG uIndex,
    LPWSTR pszText)
{
    FIXME("%p, %u, %s\n", iface, uIndex, debugstr_w(pszText));
    return E_NOTIMPL;
}

static HRESULT WINAPI path_GetScope(
    IWbemPath *iface,
    ULONG uIndex,
    ULONG *puClassNameBufSize,
    LPWSTR pszClass,
    IWbemPathKeyList **pKeyList)
{
    FIXME("%p, %u, %p, %p, %p\n", iface, uIndex, puClassNameBufSize, pszClass, pKeyList);
    return E_NOTIMPL;
}

static HRESULT WINAPI path_GetScopeAsText(
    IWbemPath *iface,
    ULONG uIndex,
    ULONG *puTextBufSize,
    LPWSTR pszText)
{
    FIXME("%p, %u, %p, %p\n", iface, uIndex, puTextBufSize, pszText);
    return E_NOTIMPL;
}

static HRESULT WINAPI path_RemoveScope(
    IWbemPath *iface,
    ULONG uIndex)
{
    FIXME("%p, %u\n", iface, uIndex);
    return E_NOTIMPL;
}

static HRESULT WINAPI path_RemoveAllScopes(
    IWbemPath *iface)
{
    FIXME("%p\n", iface);
    return E_NOTIMPL;
}

static HRESULT WINAPI path_SetClassName(
    IWbemPath *iface,
    LPCWSTR name)
{
    struct path *path = impl_from_IWbemPath( iface );
    WCHAR *class;

    TRACE("%p, %s\n", iface, debugstr_w(name));

    if (!name) return WBEM_E_INVALID_PARAMETER;
    if (!(class = strdupW( name ))) return WBEM_E_OUT_OF_MEMORY;

    EnterCriticalSection( &path->cs );

    heap_free( path->class );
    path->class = class;
    path->len_class = lstrlenW( path->class );
    path->flags |= WBEMPATH_INFO_V2_COMPLIANT | WBEMPATH_INFO_CIM_COMPLIANT;

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_GetClassName(
    IWbemPath *iface,
    ULONG *len,
    LPWSTR name)
{
    struct path *path = impl_from_IWbemPath( iface );

    TRACE("%p, %p, %p\n", iface, len, name);

    if (!len || (*len && !name)) return WBEM_E_INVALID_PARAMETER;

    EnterCriticalSection( &path->cs );

    if (!path->class)
    {
        LeaveCriticalSection( &path->cs );
        return WBEM_E_INVALID_OBJECT_PATH;
    }
    if (*len > path->len_class) lstrcpyW( name, path->class );
    *len = path->len_class + 1;

    LeaveCriticalSection( &path->cs );
    return S_OK;
}

static HRESULT WINAPI path_GetKeyList(
    IWbemPath *iface,
    IWbemPathKeyList **pOut)
{
    struct path *path = impl_from_IWbemPath( iface );
    HRESULT hr;

    TRACE("%p, %p\n", iface, pOut);

    EnterCriticalSection( &path->cs );

    if (!path->class)
    {
        LeaveCriticalSection( &path->cs );
        return WBEM_E_INVALID_PARAMETER;
    }
    hr = WbemPathKeyList_create( iface, (void **)pOut );

    LeaveCriticalSection( &path->cs );
    return hr;
}

static HRESULT WINAPI path_CreateClassPart(
    IWbemPath *iface,
    LONG lFlags,
    LPCWSTR Name)
{
    FIXME("%p, 0x%x, %s\n", iface, lFlags, debugstr_w(Name));
    return E_NOTIMPL;
}

static HRESULT WINAPI path_DeleteClassPart(
    IWbemPath *iface,
    LONG lFlags)
{
    FIXME("%p, 0x%x\n", iface, lFlags);
    return E_NOTIMPL;
}

static BOOL WINAPI path_IsRelative(
    IWbemPath *iface,
    LPWSTR wszMachine,
    LPWSTR wszNamespace)
{
    FIXME("%p, %s, %s\n", iface, debugstr_w(wszMachine), debugstr_w(wszNamespace));
    return FALSE;
}

static BOOL WINAPI path_IsRelativeOrChild(
    IWbemPath *iface,
    LPWSTR wszMachine,
    LPWSTR wszNamespace,
    LONG lFlags)
{
    FIXME("%p, %s, %s, 0x%x\n", iface, debugstr_w(wszMachine), debugstr_w(wszNamespace), lFlags);
    return FALSE;
}

static BOOL WINAPI path_IsLocal(
    IWbemPath *iface,
    LPCWSTR wszMachine)
{
    FIXME("%p, %s\n", iface, debugstr_w(wszMachine));
    return FALSE;
}

static BOOL WINAPI path_IsSameClassName(
    IWbemPath *iface,
    LPCWSTR wszClass)
{
    FIXME("%p, %s\n", iface, debugstr_w(wszClass));
    return FALSE;
}

static const struct IWbemPathVtbl path_vtbl =
{
    path_QueryInterface,
    path_AddRef,
    path_Release,
    path_SetText,
    path_GetText,
    path_GetInfo,
    path_SetServer,
    path_GetServer,
    path_GetNamespaceCount,
    path_SetNamespaceAt,
    path_GetNamespaceAt,
    path_RemoveNamespaceAt,
    path_RemoveAllNamespaces,
    path_GetScopeCount,
    path_SetScope,
    path_SetScopeFromText,
    path_GetScope,
    path_GetScopeAsText,
    path_RemoveScope,
    path_RemoveAllScopes,
    path_SetClassName,
    path_GetClassName,
    path_GetKeyList,
    path_CreateClassPart,
    path_DeleteClassPart,
    path_IsRelative,
    path_IsRelativeOrChild,
    path_IsLocal,
    path_IsSameClassName
};

HRESULT WbemPath_create( LPVOID *ppObj )
{
    struct path *path;

    TRACE("%p\n", ppObj);

    if (!(path = heap_alloc( sizeof(*path) ))) return E_OUTOFMEMORY;

    path->IWbemPath_iface.lpVtbl = &path_vtbl;
    path->refs = 1;
    InitializeCriticalSection( &path->cs );
    path->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": wmiutils_path.cs");
    init_path( path );

    *ppObj = &path->IWbemPath_iface;

    TRACE("returning iface %p\n", *ppObj);
    return S_OK;
}