d3d8: Don't print the ResourceManagerDiscardBytes fixme when Bytes == 0.
[wine] / dlls / fusion / asmname.c
1 /*
2  * IAssemblyName implementation
3  *
4  * Copyright 2008 James Hawkins
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 #include <assert.h>
23
24 #define COBJMACROS
25 #define INITGUID
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "guiddef.h"
32 #include "fusion.h"
33 #include "corerror.h"
34
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37 #include "fusionpriv.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(fusion);
40
41 typedef struct {
42     IAssemblyName IAssemblyName_iface;
43
44     LPWSTR path;
45
46     LPWSTR displayname;
47     LPWSTR name;
48     LPWSTR culture;
49     LPWSTR procarch;
50
51     WORD version[4];
52     DWORD versize;
53
54     BYTE pubkey[8];
55     BOOL haspubkey;
56
57     LONG ref;
58 } IAssemblyNameImpl;
59
60 static const WCHAR separator[] = {',',' ',0};
61 static const WCHAR version[] = {'V','e','r','s','i','o','n',0};
62 static const WCHAR culture[] = {'C','u','l','t','u','r','e',0};
63 static const WCHAR pubkey[] =
64     {'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
65 static const WCHAR procarch[] = {'p','r','o','c','e','s','s','o','r',
66     'A','r','c','h','i','t','e','c','t','u','r','e',0};
67
68 #define CHARS_PER_PUBKEY 16
69
70 static inline IAssemblyNameImpl *impl_from_IAssemblyName(IAssemblyName *iface)
71 {
72     return CONTAINING_RECORD(iface, IAssemblyNameImpl, IAssemblyName_iface);
73 }
74
75 static HRESULT WINAPI IAssemblyNameImpl_QueryInterface(IAssemblyName *iface,
76                                                        REFIID riid, LPVOID *ppobj)
77 {
78     IAssemblyNameImpl *This = impl_from_IAssemblyName(iface);
79
80     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
81
82     *ppobj = NULL;
83
84     if (IsEqualIID(riid, &IID_IUnknown) ||
85         IsEqualIID(riid, &IID_IAssemblyName))
86     {
87         IUnknown_AddRef(iface);
88         *ppobj = This;
89         return S_OK;
90     }
91
92     WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj);
93     return E_NOINTERFACE;
94 }
95
96 static ULONG WINAPI IAssemblyNameImpl_AddRef(IAssemblyName *iface)
97 {
98     IAssemblyNameImpl *This = impl_from_IAssemblyName(iface);
99     ULONG refCount = InterlockedIncrement(&This->ref);
100
101     TRACE("(%p)->(ref before = %u)\n", This, refCount - 1);
102
103     return refCount;
104 }
105
106 static ULONG WINAPI IAssemblyNameImpl_Release(IAssemblyName *iface)
107 {
108     IAssemblyNameImpl *This = impl_from_IAssemblyName(iface);
109     ULONG refCount = InterlockedDecrement(&This->ref);
110
111     TRACE("(%p)->(ref before = %u)\n", This, refCount + 1);
112
113     if (!refCount)
114     {
115         HeapFree(GetProcessHeap(), 0, This->path);
116         HeapFree(GetProcessHeap(), 0, This->displayname);
117         HeapFree(GetProcessHeap(), 0, This->name);
118         HeapFree(GetProcessHeap(), 0, This->culture);
119         HeapFree(GetProcessHeap(), 0, This->procarch);
120         HeapFree(GetProcessHeap(), 0, This);
121     }
122
123     return refCount;
124 }
125
126 static HRESULT WINAPI IAssemblyNameImpl_SetProperty(IAssemblyName *iface,
127                                                     DWORD PropertyId,
128                                                     LPVOID pvProperty,
129                                                     DWORD cbProperty)
130 {
131     FIXME("(%p, %d, %p, %d) stub!\n", iface, PropertyId, pvProperty, cbProperty);
132     return E_NOTIMPL;
133 }
134
135 static HRESULT WINAPI IAssemblyNameImpl_GetProperty(IAssemblyName *iface,
136                                                     DWORD PropertyId,
137                                                     LPVOID pvProperty,
138                                                     LPDWORD pcbProperty)
139 {
140     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
141
142     TRACE("(%p, %d, %p, %p)\n", iface, PropertyId, pvProperty, pcbProperty);
143
144     *((LPWSTR)pvProperty) = '\0';
145
146     switch (PropertyId)
147     {
148         case ASM_NAME_NULL_PUBLIC_KEY:
149         case ASM_NAME_NULL_PUBLIC_KEY_TOKEN:
150             if (name->haspubkey)
151                 return S_OK;
152             return S_FALSE;
153
154         case ASM_NAME_NULL_CUSTOM:
155             return S_OK;
156
157         case ASM_NAME_NAME:
158             *pcbProperty = 0;
159             if (name->name)
160             {
161                 lstrcpyW(pvProperty, name->name);
162                 *pcbProperty = (lstrlenW(name->name) + 1) * 2;
163             }
164             break;
165
166         case ASM_NAME_MAJOR_VERSION:
167             *pcbProperty = 0;
168             *((WORD *)pvProperty) = name->version[0];
169             if (name->versize >= 1)
170                 *pcbProperty = sizeof(WORD);
171             break;
172
173         case ASM_NAME_MINOR_VERSION:
174             *pcbProperty = 0;
175             *((WORD *)pvProperty) = name->version[1];
176             if (name->versize >= 2)
177                 *pcbProperty = sizeof(WORD);
178             break;
179
180         case ASM_NAME_BUILD_NUMBER:
181             *pcbProperty = 0;
182             *((WORD *)pvProperty) = name->version[2];
183             if (name->versize >= 3)
184                 *pcbProperty = sizeof(WORD);
185             break;
186
187         case ASM_NAME_REVISION_NUMBER:
188             *pcbProperty = 0;
189             *((WORD *)pvProperty) = name->version[3];
190             if (name->versize >= 4)
191                 *pcbProperty = sizeof(WORD);
192             break;
193
194         case ASM_NAME_CULTURE:
195             *pcbProperty = 0;
196             if (name->culture)
197             {
198                 lstrcpyW(pvProperty, name->culture);
199                 *pcbProperty = (lstrlenW(name->culture) + 1) * 2;
200             }
201             break;
202
203         case ASM_NAME_PUBLIC_KEY_TOKEN:
204             *pcbProperty = 0;
205             if (name->haspubkey)
206             {
207                 memcpy(pvProperty, name->pubkey, sizeof(DWORD) * 2);
208                 *pcbProperty = sizeof(DWORD) * 2;
209             }
210             break;
211
212         default:
213             *pcbProperty = 0;
214             break;
215     }
216
217     return S_OK;
218 }
219
220 static HRESULT WINAPI IAssemblyNameImpl_Finalize(IAssemblyName *iface)
221 {
222     FIXME("(%p) stub!\n", iface);
223     return E_NOTIMPL;
224 }
225
226 static HRESULT WINAPI IAssemblyNameImpl_GetDisplayName(IAssemblyName *iface,
227                                                        LPOLESTR szDisplayName,
228                                                        LPDWORD pccDisplayName,
229                                                        DWORD dwDisplayFlags)
230 {
231     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
232     WCHAR verstr[30];
233     DWORD size;
234     LPWSTR cultureval = 0;
235
236     static const WCHAR equals[] = {'=',0};
237
238     TRACE("(%p, %p, %p, %d)\n", iface, szDisplayName,
239           pccDisplayName, dwDisplayFlags);
240
241     if (dwDisplayFlags == 0)
242     {
243         if (!name->displayname || !*name->displayname)
244             return FUSION_E_INVALID_NAME;
245
246         size = min(*pccDisplayName, lstrlenW(name->displayname) + 1);
247
248         lstrcpynW(szDisplayName, name->displayname, size);
249         *pccDisplayName = size;
250
251         return S_OK;
252     }
253
254     if (!name->name || !*name->name)
255         return FUSION_E_INVALID_NAME;
256
257     /* Verify buffer size is sufficient */
258     size = lstrlenW(name->name) + 1;
259
260     if ((dwDisplayFlags & ASM_DISPLAYF_VERSION) && (name->versize > 0))
261     {
262         static const WCHAR spec[] = {'%','d',0};
263         static const WCHAR period[] = {'.',0};
264         int i;
265
266         wsprintfW(verstr, spec, name->version[0]);
267
268         for (i = 1; i < name->versize; i++)
269         {
270             WCHAR value[6];
271             wsprintfW(value, spec, name->version[i]);
272
273             lstrcatW(verstr, period);
274             lstrcatW(verstr, value);
275         }
276
277         size += lstrlenW(separator) + lstrlenW(version) + lstrlenW(equals) + lstrlenW(verstr);
278     }
279
280     if ((dwDisplayFlags & ASM_DISPLAYF_CULTURE) && (name->culture))
281     {
282         static const WCHAR neutral[] = {'n','e','u','t','r','a','l', 0};
283
284         cultureval = (lstrlenW(name->culture) == 2) ? name->culture : (LPWSTR) neutral;
285         size += lstrlenW(separator) + lstrlenW(culture) + lstrlenW(equals) + lstrlenW(cultureval);
286     }
287
288     if ((dwDisplayFlags & ASM_DISPLAYF_PUBLIC_KEY_TOKEN) && (name->haspubkey))
289         size += lstrlenW(separator) + lstrlenW(pubkey) + lstrlenW(equals) + CHARS_PER_PUBKEY;
290
291     if ((dwDisplayFlags & ASM_DISPLAYF_PROCESSORARCHITECTURE) && (name->procarch))
292         size += lstrlenW(separator) + lstrlenW(procarch) + lstrlenW(equals) + lstrlenW(name->procarch);
293
294     if (size > *pccDisplayName)
295         return S_FALSE;
296
297     /* Construct the string */
298     lstrcpyW(szDisplayName, name->name);
299
300     if ((dwDisplayFlags & ASM_DISPLAYF_VERSION) && (name->versize > 0))
301     {
302         lstrcatW(szDisplayName, separator);
303
304         lstrcatW(szDisplayName, version);
305         lstrcatW(szDisplayName, equals);
306         lstrcatW(szDisplayName, verstr);
307     }
308
309     if ((dwDisplayFlags & ASM_DISPLAYF_CULTURE) && (name->culture))
310     {
311         lstrcatW(szDisplayName, separator);
312
313         lstrcatW(szDisplayName, culture);
314         lstrcatW(szDisplayName, equals);
315         lstrcatW(szDisplayName, cultureval);
316     }
317
318     if ((dwDisplayFlags & ASM_DISPLAYF_PUBLIC_KEY_TOKEN) && (name->haspubkey))
319     {
320         WCHAR pkt[CHARS_PER_PUBKEY + 1];
321         static const WCHAR spec[] = {'%','0','x','%','0','x','%','0','x',
322             '%','0','x','%','0','x','%','0','x','%','0','x','%','0','x',0};
323
324         lstrcatW(szDisplayName, separator);
325
326         lstrcatW(szDisplayName, pubkey);
327         lstrcatW(szDisplayName, equals);
328
329         wsprintfW(pkt, spec, name->pubkey[0], name->pubkey[1], name->pubkey[2],
330             name->pubkey[3], name->pubkey[4], name->pubkey[5], name->pubkey[6],
331             name->pubkey[7]);
332
333         lstrcatW(szDisplayName, pkt);
334     }
335
336     if ((dwDisplayFlags & ASM_DISPLAYF_PROCESSORARCHITECTURE) && (name->procarch))
337     {
338         lstrcatW(szDisplayName, separator);
339
340         lstrcatW(szDisplayName, procarch);
341         lstrcatW(szDisplayName, equals);
342         lstrcatW(szDisplayName, name->procarch);
343     }
344
345     *pccDisplayName = size;
346     return S_OK;
347 }
348
349 static HRESULT WINAPI IAssemblyNameImpl_Reserved(IAssemblyName *iface,
350                                                  REFIID refIID,
351                                                  IUnknown *pUnkReserved1,
352                                                  IUnknown *pUnkReserved2,
353                                                  LPCOLESTR szReserved,
354                                                  LONGLONG llReserved,
355                                                  LPVOID pvReserved,
356                                                  DWORD cbReserved,
357                                                  LPVOID *ppReserved)
358 {
359     TRACE("(%p, %s, %p, %p, %s, %x%08x, %p, %d, %p)\n", iface,
360           debugstr_guid(refIID), pUnkReserved1, pUnkReserved2,
361           debugstr_w(szReserved), (DWORD)(llReserved >> 32), (DWORD)llReserved,
362           pvReserved, cbReserved, ppReserved);
363
364     return E_NOTIMPL;
365 }
366
367 static HRESULT WINAPI IAssemblyNameImpl_GetName(IAssemblyName *iface,
368                                                 LPDWORD lpcwBuffer,
369                                                 WCHAR *pwzName)
370 {
371     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
372
373     TRACE("(%p, %p, %p)\n", iface, lpcwBuffer, pwzName);
374
375     if (!name->name)
376     {
377         *pwzName = '\0';
378         *lpcwBuffer = 0;
379         return S_OK;
380     }
381
382     lstrcpyW(pwzName, name->name);
383     *lpcwBuffer = lstrlenW(pwzName) + 1;
384
385     return S_OK;
386 }
387
388 static HRESULT WINAPI IAssemblyNameImpl_GetVersion(IAssemblyName *iface,
389                                                    LPDWORD pdwVersionHi,
390                                                    LPDWORD pdwVersionLow)
391 {
392     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
393
394     TRACE("(%p, %p, %p)\n", iface, pdwVersionHi, pdwVersionLow);
395
396     *pdwVersionHi = 0;
397     *pdwVersionLow = 0;
398
399     if (name->versize != 4)
400         return FUSION_E_INVALID_NAME;
401
402     *pdwVersionHi = (name->version[0] << 16) + name->version[1];
403     *pdwVersionLow = (name->version[2] << 16) + name->version[3];
404
405     return S_OK;
406 }
407
408 static HRESULT WINAPI IAssemblyNameImpl_IsEqual(IAssemblyName *iface,
409                                                 IAssemblyName *pName,
410                                                 DWORD dwCmpFlags)
411 {
412     FIXME("(%p, %p, %d) stub!\n", iface, pName, dwCmpFlags);
413     return E_NOTIMPL;
414 }
415
416 static HRESULT WINAPI IAssemblyNameImpl_Clone(IAssemblyName *iface,
417                                               IAssemblyName **pName)
418 {
419     FIXME("(%p, %p) stub!\n", iface, pName);
420     return E_NOTIMPL;
421 }
422
423 static const IAssemblyNameVtbl AssemblyNameVtbl = {
424     IAssemblyNameImpl_QueryInterface,
425     IAssemblyNameImpl_AddRef,
426     IAssemblyNameImpl_Release,
427     IAssemblyNameImpl_SetProperty,
428     IAssemblyNameImpl_GetProperty,
429     IAssemblyNameImpl_Finalize,
430     IAssemblyNameImpl_GetDisplayName,
431     IAssemblyNameImpl_Reserved,
432     IAssemblyNameImpl_GetName,
433     IAssemblyNameImpl_GetVersion,
434     IAssemblyNameImpl_IsEqual,
435     IAssemblyNameImpl_Clone
436 };
437
438 /* Internal methods */
439 static inline IAssemblyNameImpl *unsafe_impl_from_IAssemblyName(IAssemblyName *iface)
440 {
441     assert(iface->lpVtbl == &AssemblyNameVtbl);
442
443     return impl_from_IAssemblyName(iface);
444 }
445
446 HRESULT IAssemblyName_SetPath(IAssemblyName *iface, LPCWSTR path)
447 {
448     IAssemblyNameImpl *name = unsafe_impl_from_IAssemblyName(iface);
449
450     name->path = strdupW(path);
451     if (!name->path)
452         return E_OUTOFMEMORY;
453
454     return S_OK;
455 }
456
457 HRESULT IAssemblyName_GetPath(IAssemblyName *iface, LPWSTR buf, ULONG *len)
458 {
459     ULONG buffer_size = *len;
460     IAssemblyNameImpl *name = unsafe_impl_from_IAssemblyName(iface);
461
462     if (!name->path)
463         return S_OK;
464
465     if (!buf)
466         buffer_size = 0;
467
468     *len = lstrlenW(name->path) + 1;
469
470     if (*len <= buffer_size)
471         lstrcpyW(buf, name->path);
472     else
473         return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
474
475     return S_OK;
476 }
477
478 static HRESULT parse_version(IAssemblyNameImpl *name, LPWSTR version)
479 {
480     LPWSTR beg, end;
481     int i;
482
483     for (i = 0, beg = version; i < 4; i++)
484     {
485         if (!*beg)
486             return S_OK;
487
488         end = strchrW(beg, '.');
489
490         if (end) *end = '\0';
491         name->version[i] = atolW(beg);
492         name->versize++;
493
494         if (!end && i < 3)
495             return S_OK;
496
497         beg = end + 1;
498     }
499
500     return S_OK;
501 }
502
503 static HRESULT parse_culture(IAssemblyNameImpl *name, LPCWSTR culture)
504 {
505     static const WCHAR empty[] = {0};
506
507     if (lstrlenW(culture) == 2)
508         name->culture = strdupW(culture);
509     else
510         name->culture = strdupW(empty);
511
512     return S_OK;
513 }
514
515 static BOOL is_hex(WCHAR c)
516 {
517     return ((c >= 'a' && c <= 'f') ||
518             (c >= 'A' && c <= 'F') ||
519             (c >= '0' && c <= '9'));
520 }
521
522 static BYTE hextobyte(WCHAR c)
523 {
524     if(c >= '0' && c <= '9')
525         return c - '0';
526     if(c >= 'A' && c <= 'F')
527         return c - 'A' + 10;
528     if(c >= 'a' && c <= 'f')
529         return c - 'a' + 10;
530     return 0;
531 }
532
533 static HRESULT parse_pubkey(IAssemblyNameImpl *name, LPCWSTR pubkey)
534 {
535     int i;
536     BYTE val;
537
538     if (lstrlenW(pubkey) < CHARS_PER_PUBKEY)
539         return FUSION_E_INVALID_NAME;
540
541     for (i = 0; i < CHARS_PER_PUBKEY; i++)
542         if (!is_hex(pubkey[i]))
543             return FUSION_E_INVALID_NAME;
544
545     name->haspubkey = TRUE;
546
547     for (i = 0; i < CHARS_PER_PUBKEY; i += 2)
548     {
549         val = (hextobyte(pubkey[i]) << 4) + hextobyte(pubkey[i + 1]);
550         name->pubkey[i / 2] = val;
551     }
552
553     return S_OK;
554 }
555
556 static HRESULT parse_display_name(IAssemblyNameImpl *name, LPCWSTR szAssemblyName)
557 {
558     LPWSTR str, save;
559     LPWSTR ptr, ptr2;
560     HRESULT hr = S_OK;
561     BOOL done = FALSE;
562
563     if (!szAssemblyName)
564         return S_OK;
565
566     name->displayname = strdupW(szAssemblyName);
567     if (!name->displayname)
568         return E_OUTOFMEMORY;
569
570     str = strdupW(szAssemblyName);
571     save = str;
572     if (!str)
573     {
574         hr = E_OUTOFMEMORY;
575         goto done;
576     }
577
578     ptr = strchrW(str, ',');
579     if (ptr) *ptr = '\0';
580
581     /* no ',' but ' ' only */
582     if( !ptr && strchrW(str, ' ') )
583     {
584         hr = FUSION_E_INVALID_NAME;
585         goto done;
586     }
587
588     name->name = strdupW(str);
589     if (!name->name)
590     {
591         hr = E_OUTOFMEMORY;
592         goto done;
593     }
594
595     if (!ptr)
596         goto done;
597
598     str = ptr + 2;
599     while (!done)
600     {
601         ptr = strchrW(str, '=');
602         if (!ptr)
603         {
604             hr = FUSION_E_INVALID_NAME;
605             goto done;
606         }
607
608         *(ptr++) = '\0';
609         if (!*ptr)
610         {
611             hr = FUSION_E_INVALID_NAME;
612             goto done;
613         }
614
615         if (!(ptr2 = strstrW(ptr, separator)))
616         {
617             if (!(ptr2 = strchrW(ptr, '\0')))
618             {
619                 hr = FUSION_E_INVALID_NAME;
620                 goto done;
621             }
622
623             done = TRUE;
624         }
625
626         *ptr2 = '\0';
627
628         while (*str == ' ') str++;
629
630         if (!lstrcmpW(str, version))
631             hr = parse_version(name, ptr);
632         else if (!lstrcmpW(str, culture))
633             hr = parse_culture(name, ptr);
634         else if (!lstrcmpW(str, pubkey))
635             hr = parse_pubkey(name, ptr);
636         else if (!lstrcmpW(str, procarch))
637         {
638             name->procarch = strdupW(ptr);
639             if (!name->procarch)
640                 hr = E_OUTOFMEMORY;
641         }
642
643         if (FAILED(hr))
644             goto done;
645
646         str = ptr2 + 1;
647     }
648
649 done:
650     HeapFree(GetProcessHeap(), 0, save);
651     if (FAILED(hr))
652     {
653         HeapFree(GetProcessHeap(), 0, name->displayname);
654         HeapFree(GetProcessHeap(), 0, name->name);
655     }
656     return hr;
657 }
658
659 /******************************************************************
660  *  CreateAssemblyNameObject   (FUSION.@)
661  */
662 HRESULT WINAPI CreateAssemblyNameObject(LPASSEMBLYNAME *ppAssemblyNameObj,
663                                         LPCWSTR szAssemblyName, DWORD dwFlags,
664                                         LPVOID pvReserved)
665 {
666     IAssemblyNameImpl *name;
667     HRESULT hr;
668
669     TRACE("(%p, %s, %08x, %p) stub!\n", ppAssemblyNameObj,
670           debugstr_w(szAssemblyName), dwFlags, pvReserved);
671
672     if (!ppAssemblyNameObj)
673         return E_INVALIDARG;
674
675     if ((dwFlags & CANOF_PARSE_DISPLAY_NAME) &&
676         (!szAssemblyName || !*szAssemblyName))
677         return E_INVALIDARG;
678
679     name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAssemblyNameImpl));
680     if (!name)
681         return E_OUTOFMEMORY;
682
683     name->IAssemblyName_iface.lpVtbl = &AssemblyNameVtbl;
684     name->ref = 1;
685
686     hr = parse_display_name(name, szAssemblyName);
687     if (FAILED(hr))
688     {
689         HeapFree(GetProcessHeap(), 0, name);
690         return hr;
691     }
692
693     *ppAssemblyNameObj = &name->IAssemblyName_iface;
694
695     return S_OK;
696 }