msxml3: COM cleanup in domdoc.c.
[wine] / dlls / sxs / cache.c
1 /*
2  * IAssemblyCache implementation
3  *
4  * Copyright 2010 Hans Leidekker for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define INITGUID
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "ole2.h"
29 #include "winsxs.h"
30 #include "msxml2.h"
31
32 #include "wine/debug.h"
33 #include "wine/list.h"
34 #include "wine/unicode.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(sxs);
37
38 static inline WCHAR *strdupW( const WCHAR *s )
39 {
40     WCHAR *t;
41     if (!s) return NULL;
42     if ((t = HeapAlloc( GetProcessHeap(), 0, (strlenW( s ) + 1) * sizeof(WCHAR) ))) strcpyW( t, s );
43     return t;
44 }
45
46 struct cache
47 {
48     IAssemblyCache IAssemblyCache_iface;
49     LONG refs;
50 };
51
52 static inline struct cache *impl_from_IAssemblyCache(IAssemblyCache *iface)
53 {
54     return CONTAINING_RECORD(iface, struct cache, IAssemblyCache_iface);
55 }
56
57 static HRESULT WINAPI cache_QueryInterface(
58     IAssemblyCache *iface,
59     REFIID riid,
60     void **obj )
61 {
62     struct cache *cache = impl_from_IAssemblyCache(iface);
63
64     TRACE("%p, %s, %p\n", cache, debugstr_guid(riid), obj);
65
66     *obj = NULL;
67
68     if (IsEqualIID(riid, &IID_IUnknown) ||
69         IsEqualIID(riid, &IID_IAssemblyCache))
70     {
71         IUnknown_AddRef( iface );
72         *obj = cache;
73         return S_OK;
74     }
75
76     return E_NOINTERFACE;
77 }
78
79 static ULONG WINAPI cache_AddRef( IAssemblyCache *iface )
80 {
81     struct cache *cache = impl_from_IAssemblyCache(iface);
82     return InterlockedIncrement( &cache->refs );
83 }
84
85 static ULONG WINAPI cache_Release( IAssemblyCache *iface )
86 {
87     struct cache *cache = impl_from_IAssemblyCache(iface);
88     ULONG refs = InterlockedDecrement( &cache->refs );
89
90     if (!refs)
91     {
92         TRACE("destroying %p\n", cache);
93         HeapFree( GetProcessHeap(), 0, cache );
94     }
95     return refs;
96 }
97
98 static HRESULT WINAPI cache_UninstallAssembly(
99     IAssemblyCache *iface,
100     DWORD flags,
101     LPCWSTR name,
102     LPCFUSION_INSTALL_REFERENCE ref,
103     ULONG *disp )
104 {
105     FIXME("%p, 0x%08x, %s, %p, %p\n", iface, flags, debugstr_w(name), ref, disp);
106     return E_NOTIMPL;
107 }
108
109 static HRESULT WINAPI cache_QueryAssemblyInfo(
110     IAssemblyCache *iface,
111     DWORD flags,
112     LPCWSTR name,
113     ASSEMBLY_INFO *info )
114 {
115     FIXME("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(name), info);
116     return E_NOTIMPL;
117 }
118
119 static HRESULT WINAPI cache_CreateAssemblyCacheItem(
120     IAssemblyCache *iface,
121     DWORD flags,
122     PVOID reserved,
123     IAssemblyCacheItem **item,
124     LPCWSTR name )
125 {
126     FIXME("%p, 0x%08x, %p, %p, %s\n", iface, flags, reserved, item, debugstr_w(name));
127     return E_NOTIMPL;
128 }
129
130 static HRESULT WINAPI cache_Reserved(
131     IAssemblyCache *iface,
132     IUnknown **reserved)
133 {
134     FIXME("%p\n", reserved);
135     return E_NOTIMPL;
136 }
137
138 static BSTR get_attribute_value( IXMLDOMNamedNodeMap *map, const WCHAR *value_name )
139 {
140     HRESULT hr;
141     IXMLDOMNode *attr;
142     VARIANT var;
143     BSTR str;
144
145     str = SysAllocString( value_name );
146     hr = IXMLDOMNamedNodeMap_getNamedItem( map, str, &attr );
147     SysFreeString( str );
148     if (hr != S_OK) return NULL;
149
150     hr = IXMLDOMNode_get_nodeValue( attr, &var );
151     IXMLDOMNode_Release( attr );
152     if (hr != S_OK) return NULL;
153     if (V_VT(&var) != VT_BSTR)
154     {
155         VariantClear( &var );
156         return NULL;
157     }
158     TRACE("%s=%s\n", debugstr_w(value_name), debugstr_w(V_BSTR( &var )));
159     return V_BSTR( &var );
160 }
161
162 struct file
163 {
164     struct list entry;
165     BSTR name;
166 };
167
168 struct assembly
169 {
170     BSTR type;
171     BSTR name;
172     BSTR version;
173     BSTR arch;
174     BSTR token;
175     struct list files;
176 };
177
178 static void free_assembly( struct assembly *assembly )
179 {
180     struct list *item, *cursor;
181
182     if (!assembly) return;
183     SysFreeString( assembly->type );
184     SysFreeString( assembly->name );
185     SysFreeString( assembly->version );
186     SysFreeString( assembly->arch );
187     SysFreeString( assembly->token );
188     LIST_FOR_EACH_SAFE( item, cursor, &assembly->files )
189     {
190         struct file *file = LIST_ENTRY( item, struct file, entry );
191         list_remove( &file->entry );
192         SysFreeString( file->name );
193         HeapFree( GetProcessHeap(), 0, file );
194     }
195     HeapFree( GetProcessHeap(), 0, assembly );
196 }
197
198 static HRESULT parse_files( IXMLDOMDocument *doc, struct assembly *assembly )
199 {
200     static const WCHAR fileW[] = {'f','i','l','e',0};
201     static const WCHAR nameW[] = {'n','a','m','e',0};
202     IXMLDOMNamedNodeMap *attrs;
203     IXMLDOMNodeList *list;
204     IXMLDOMNode *node;
205     struct file *f;
206     BSTR str;
207     HRESULT hr;
208     LONG len;
209
210     str = SysAllocString( fileW );
211     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
212     SysFreeString( str );
213     if (hr != S_OK) return hr;
214
215     hr = IXMLDOMNodeList_get_length( list, &len );
216     if (hr != S_OK) goto done;
217     TRACE("found %d files\n", len);
218     if (!len)
219     {
220         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
221         goto done;
222     }
223
224     for (;;)
225     {
226         hr = IXMLDOMNodeList_nextNode( list, &node );
227         if (hr != S_OK || !node)
228         {
229             hr = S_OK;
230             break;
231         }
232
233         /* FIXME: validate node type */
234
235         hr = IXMLDOMNode_get_attributes( node, &attrs );
236         IXMLDOMNode_Release( node );
237         if (hr != S_OK)
238             goto done;
239
240         if (!(f = HeapAlloc( GetProcessHeap(), 0, sizeof(struct file) )))
241         {
242             IXMLDOMNamedNodeMap_Release( attrs );
243             hr = E_OUTOFMEMORY;
244             goto done;
245         }
246
247         f->name = get_attribute_value( attrs, nameW );
248         IXMLDOMNamedNodeMap_Release( attrs );
249         if (!f->name)
250         {
251             HeapFree( GetProcessHeap(), 0, f );
252             hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
253             goto done;
254         }
255         list_add_tail( &assembly->files, &f->entry );
256     }
257
258     if (list_empty( &assembly->files ))
259     {
260         WARN("no files found\n");
261         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
262     }
263
264 done:
265     IXMLDOMNodeList_Release( list );
266     return hr;
267 }
268
269 static HRESULT parse_assembly( IXMLDOMDocument *doc, struct assembly **assembly )
270 {
271     static const WCHAR identityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
272     static const WCHAR typeW[] = {'t','y','p','e',0};
273     static const WCHAR nameW[] = {'n','a','m','e',0};
274     static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
275     static const WCHAR architectureW[] = {'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e',0};
276     static const WCHAR tokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
277     static const WCHAR win32W[] = {'w','i','n','3','2',0};
278     static const WCHAR policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
279     IXMLDOMNodeList *list = NULL;
280     IXMLDOMNode *node = NULL;
281     IXMLDOMNamedNodeMap *attrs = NULL;
282     struct assembly *a = NULL;
283     BSTR str;
284     HRESULT hr;
285     LONG len;
286
287     str = SysAllocString( identityW );
288     hr = IXMLDOMDocument_getElementsByTagName( doc, str, &list );
289     SysFreeString( str );
290     if (hr != S_OK) goto done;
291
292     hr = IXMLDOMNodeList_get_length( list, &len );
293     if (hr != S_OK) goto done;
294     if (!len)
295     {
296         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
297         goto done;
298     }
299     hr = IXMLDOMNodeList_nextNode( list, &node );
300     if (hr != S_OK) goto done;
301     if (!node)
302     {
303         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
304         goto done;
305     }
306     if (!(a = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct assembly) )))
307     {
308         hr = E_OUTOFMEMORY;
309         goto done;
310     }
311     list_init( &a->files );
312
313     hr = IXMLDOMNode_get_attributes( node, &attrs );
314     if (hr != S_OK) goto done;
315
316     a->type    = get_attribute_value( attrs, typeW );
317     a->name    = get_attribute_value( attrs, nameW );
318     a->version = get_attribute_value( attrs, versionW );
319     a->arch    = get_attribute_value( attrs, architectureW );
320     a->token   = get_attribute_value( attrs, tokenW );
321
322     if (!a->type || (strcmpW( a->type, win32W ) && strcmpW( a->type, policyW )) ||
323         !a->name || !a->version || !a->arch || !a->token)
324     {
325         WARN("invalid win32 assembly\n");
326         hr = ERROR_SXS_MANIFEST_FORMAT_ERROR;
327         goto done;
328     }
329     if (!strcmpW( a->type, win32W )) hr = parse_files( doc, a );
330
331 done:
332     if (attrs) IXMLDOMNamedNodeMap_Release( attrs );
333     if (node) IXMLDOMNode_Release( node );
334     if (list) IXMLDOMNodeList_Release( list );
335     if (hr == S_OK) *assembly = a;
336     else free_assembly( a );
337     return hr;
338 }
339
340 static WCHAR *build_sxs_path( void )
341 {
342     static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\',0};
343     WCHAR sxsdir[MAX_PATH];
344
345     GetWindowsDirectoryW( sxsdir, MAX_PATH );
346     strcatW( sxsdir, winsxsW );
347     return strdupW( sxsdir );
348 }
349
350 static WCHAR *build_assembly_name( struct assembly *assembly )
351 {
352     static const WCHAR fmtW[] =
353         {'%','s','_','%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
354     WCHAR *ret, *p;
355     int len;
356
357     len = strlenW( fmtW );
358     len += strlenW( assembly->arch );
359     len += strlenW( assembly->name );
360     len += strlenW( assembly->token );
361     len += strlenW( assembly->version );
362
363     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
364     sprintfW( ret, fmtW, assembly->arch, assembly->name, assembly->token, assembly->version );
365     for (p = ret; *p; p++) *p = tolowerW( *p );
366     return ret;
367 }
368
369 static WCHAR *build_policy_name( struct assembly *assembly )
370 {
371     static const WCHAR fmtW[] =
372         {'%','s','_','%','s','_','%','s','_','n','o','n','e','_','d','e','a','d','b','e','e','f',0};
373     WCHAR *ret, *p;
374     int len;
375
376     len = strlenW( fmtW );
377     len += strlenW( assembly->arch );
378     len += strlenW( assembly->name );
379     len += strlenW( assembly->token );
380
381     if (!(ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
382     sprintfW( ret, fmtW, assembly->arch, assembly->name, assembly->token );
383     for (p = ret; *p; p++) *p = tolowerW( *p );
384     return ret;
385 }
386
387 static HRESULT install_policy( const WCHAR *manifest, struct assembly *assembly )
388 {
389     static const WCHAR policiesW[] = {'p','o','l','i','c','i','e','s','\\',0};
390     static const WCHAR suffixW[] = {'.','p','o','l','i','c','y',0};
391     static const WCHAR backslashW[] = {'\\',0};
392     WCHAR *sxsdir, *name, *dst;
393     HRESULT hr = E_OUTOFMEMORY;
394     BOOL ret;
395     int len;
396
397     /* FIXME: handle catalog file */
398
399     if (!(sxsdir = build_sxs_path())) return E_OUTOFMEMORY;
400     if (!(name = build_policy_name( assembly ))) goto done;
401
402     len = strlenW( sxsdir );
403     len += strlenW( policiesW );
404     len += strlenW( name ) + 1;
405     len += strlenW( assembly->version );
406     len += strlenW( suffixW );
407
408     if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
409     strcpyW( dst, sxsdir );
410     strcatW( dst, policiesW );
411     CreateDirectoryW( dst, NULL );
412     strcatW( dst, name );
413     CreateDirectoryW( dst, NULL );
414     strcatW( dst, backslashW );
415     strcatW( dst, assembly->version );
416     strcatW( dst, suffixW );
417
418     ret = CopyFileW( manifest, dst, FALSE );
419     HeapFree( GetProcessHeap(), 0, dst );
420     if (!ret)
421     {
422         hr = HRESULT_FROM_WIN32( GetLastError() );
423         WARN("failed to copy policy manifest file 0x%08x\n", hr);
424     }
425     hr = S_OK;
426
427 done:
428     HeapFree( GetProcessHeap(), 0, sxsdir );
429     HeapFree( GetProcessHeap(), 0, name );
430     return hr;
431 }
432
433 static WCHAR *build_source_filename( const WCHAR *manifest, struct file *file )
434 {
435     WCHAR *src;
436     const WCHAR *p;
437     int len;
438
439     p = strrchrW( manifest, '\\' );
440     if (!p) p = strrchrW( manifest, '/' );
441     if (!p) return strdupW( manifest );
442
443     len = p - manifest + 1;
444     if (!(src = HeapAlloc( GetProcessHeap(), 0, (len + strlenW( file->name ) + 1) * sizeof(WCHAR) )))
445         return NULL;
446
447     memcpy( src, manifest, len * sizeof(WCHAR) );
448     strcpyW( src + len, file->name );
449     return src;
450 }
451
452 static HRESULT install_assembly( const WCHAR *manifest, struct assembly *assembly )
453 {
454     static const WCHAR manifestsW[] = {'m','a','n','i','f','e','s','t','s','\\',0};
455     static const WCHAR suffixW[] = {'.','m','a','n','i','f','e','s','t',0};
456     static const WCHAR backslashW[] = {'\\',0};
457     WCHAR *sxsdir, *p, *name, *dst, *src;
458     struct file *file;
459     HRESULT hr = E_OUTOFMEMORY;
460     BOOL ret;
461     int len;
462
463     if (!(sxsdir = build_sxs_path())) return E_OUTOFMEMORY;
464     if (!(name = build_assembly_name( assembly ))) goto done;
465
466     len = strlenW( sxsdir );
467     len += strlenW( manifestsW );
468     len += strlenW( name );
469     len += strlenW( suffixW );
470     if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) goto done;
471     strcpyW( dst, sxsdir );
472     strcatW( dst, manifestsW );
473     strcatW( dst, name );
474     strcatW( dst, suffixW );
475
476     ret = CopyFileW( manifest, dst, FALSE );
477     HeapFree( GetProcessHeap(), 0, dst );
478     if (!ret)
479     {
480         hr = HRESULT_FROM_WIN32( GetLastError() );
481         WARN("failed to copy manifest file 0x%08x\n", hr);
482         goto done;
483     }
484
485     /* FIXME: this should be a transaction */
486     LIST_FOR_EACH_ENTRY( file, &assembly->files, struct file, entry )
487     {
488         if (!(src = build_source_filename( manifest, file )))
489         {
490             hr = E_OUTOFMEMORY;
491             goto done;
492         }
493         len = strlenW( sxsdir ) + strlenW( name ) + strlenW( file->name );
494         if (!(dst = HeapAlloc( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) )))
495         {
496             HeapFree( GetProcessHeap(), 0, src );
497             hr = E_OUTOFMEMORY;
498             goto done;
499         }
500         strcpyW( dst, sxsdir );
501         strcatW( dst, name );
502         CreateDirectoryW( dst, NULL );
503
504         strcatW( dst, backslashW );
505         strcatW( dst, file->name );
506         for (p = dst; *p; p++) *p = tolowerW( *p );
507
508         ret = CopyFileW( src, dst, FALSE );
509         HeapFree( GetProcessHeap(), 0, src );
510         HeapFree( GetProcessHeap(), 0, dst );
511         if (!ret)
512         {
513             hr = HRESULT_FROM_WIN32( GetLastError() );
514             WARN("failed to copy file 0x%08x\n", hr);
515             goto done;
516         }
517     }
518     hr = S_OK;
519
520 done:
521     HeapFree( GetProcessHeap(), 0, sxsdir );
522     HeapFree( GetProcessHeap(), 0, name );
523     return hr;
524 }
525
526 static HRESULT WINAPI cache_InstallAssembly(
527     IAssemblyCache *iface,
528     DWORD flags,
529     LPCWSTR path,
530     LPCFUSION_INSTALL_REFERENCE ref )
531 {
532     static const WCHAR policyW[] = {'w','i','n','3','2','-','p','o','l','i','c','y',0};
533     HRESULT hr, init;
534     IXMLDOMDocument *doc = NULL;
535     struct assembly *assembly = NULL;
536     BSTR str;
537     VARIANT var;
538     VARIANT_BOOL b;
539
540     TRACE("%p, 0x%08x, %s, %p\n", iface, flags, debugstr_w(path), ref);
541
542     init = CoInitialize( NULL );
543
544     hr = CoCreateInstance( &CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&doc );
545     if (hr != S_OK)
546         goto done;
547
548     str = SysAllocString( path );
549     VariantInit( &var );
550     V_VT( &var ) = VT_BSTR;
551     V_BSTR( &var ) = str;
552     hr = IXMLDOMDocument_load( doc, var, &b );
553     SysFreeString( str );
554     if (hr != S_OK) goto done;
555     if (!b)
556     {
557         WARN("failed to load manifest\n");
558         hr = S_FALSE;
559         goto done;
560     }
561
562     hr = parse_assembly( doc, &assembly );
563     if (hr != S_OK)
564         goto done;
565
566     /* FIXME: verify name attributes */
567
568     if (!strcmpW( assembly->type, policyW ))
569         hr = install_policy( path, assembly );
570     else
571         hr = install_assembly( path, assembly );
572
573 done:
574     free_assembly( assembly );
575     if (doc) IXMLDOMDocument_Release( doc );
576
577     if (SUCCEEDED(init))
578         CoUninitialize();
579
580     return hr;
581 }
582
583 static const IAssemblyCacheVtbl cache_vtbl =
584 {
585     cache_QueryInterface,
586     cache_AddRef,
587     cache_Release,
588     cache_UninstallAssembly,
589     cache_QueryAssemblyInfo,
590     cache_CreateAssemblyCacheItem,
591     cache_Reserved,
592     cache_InstallAssembly
593 };
594
595 /******************************************************************
596  *  CreateAssemblyCache   (SXS.@)
597  */
598 HRESULT WINAPI CreateAssemblyCache( IAssemblyCache **obj, DWORD reserved )
599 {
600     struct cache *cache;
601
602     TRACE("%p, %u\n", obj, reserved);
603
604     if (!obj)
605         return E_INVALIDARG;
606
607     *obj = NULL;
608
609     cache = HeapAlloc( GetProcessHeap(), 0, sizeof(struct cache) );
610     if (!cache)
611         return E_OUTOFMEMORY;
612
613     cache->IAssemblyCache_iface.lpVtbl = &cache_vtbl;
614     cache->refs = 1;
615
616     *obj = &cache->IAssemblyCache_iface;
617     return S_OK;
618 }