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