mshtml: Fixed ref count tests on broken IEs.
[wine] / dlls / crypt32 / str.c
1 /*
2  * Copyright 2006 Juan Lang for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #include <stdarg.h>
19
20 #define NONAMELESSUNION
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winnls.h"
25 #include "winuser.h"
26 #include "wincrypt.h"
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
31
32 static inline BOOL is_quotable_char(char c)
33 {
34     switch(c)
35     {
36     case '+':
37     case ',':
38     case '"':
39     case '=':
40     case '<':
41     case '>':
42     case ';':
43     case '#':
44     case '\n':
45         return TRUE;
46     default:
47         return FALSE;
48     }
49 }
50
51 DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
52  LPSTR psz, DWORD csz)
53 {
54     DWORD ret = 0, len, i;
55     BOOL needsQuotes = FALSE;
56
57     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz);
58
59     switch (dwValueType)
60     {
61     case CERT_RDN_ANY_TYPE:
62         break;
63     case CERT_RDN_NUMERIC_STRING:
64     case CERT_RDN_PRINTABLE_STRING:
65     case CERT_RDN_TELETEX_STRING:
66     case CERT_RDN_VIDEOTEX_STRING:
67     case CERT_RDN_IA5_STRING:
68     case CERT_RDN_GRAPHIC_STRING:
69     case CERT_RDN_VISIBLE_STRING:
70     case CERT_RDN_GENERAL_STRING:
71         len = pValue->cbData;
72         if (pValue->cbData && isspace(pValue->pbData[0]))
73             needsQuotes = TRUE;
74         if (pValue->cbData && isspace(pValue->pbData[pValue->cbData - 1]))
75             needsQuotes = TRUE;
76         for (i = 0; i < pValue->cbData; i++)
77         {
78             if (is_quotable_char(pValue->pbData[i]))
79                 needsQuotes = TRUE;
80             if (pValue->pbData[i] == '"')
81                 len += 1;
82         }
83         if (needsQuotes)
84             len += 2;
85         if (!psz || !csz)
86             ret = len;
87         else
88         {
89             char *ptr = psz;
90
91             if (needsQuotes)
92                 *ptr++ = '"';
93             for (i = 0; i < pValue->cbData && ptr - psz < csz; ptr++, i++)
94             {
95                 *ptr = pValue->pbData[i];
96                 if (pValue->pbData[i] == '"' && ptr - psz < csz - 1)
97                     *(++ptr) = '"';
98             }
99             if (needsQuotes && ptr - psz < csz)
100                 *ptr++ = '"';
101             ret = ptr - psz;
102         }
103         break;
104     case CERT_RDN_BMP_STRING:
105     case CERT_RDN_UTF8_STRING:
106         len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pValue->pbData,
107          pValue->cbData / sizeof(WCHAR), NULL, 0, NULL, NULL);
108         if (pValue->cbData && isspaceW(((LPCWSTR)pValue->pbData)[0]))
109             needsQuotes = TRUE;
110         if (pValue->cbData &&
111          isspaceW(((LPCWSTR)pValue->pbData)[pValue->cbData / sizeof(WCHAR)-1]))
112             needsQuotes = TRUE;
113         for (i = 0; i < pValue->cbData / sizeof(WCHAR); i++)
114         {
115             if (is_quotable_char(((LPCWSTR)pValue->pbData)[i]))
116                 needsQuotes = TRUE;
117             if (((LPCWSTR)pValue->pbData)[i] == '"')
118                 len += 1;
119         }
120         if (needsQuotes)
121             len += 2;
122         if (!psz || !csz)
123             ret = len;
124         else
125         {
126             char *dst = psz;
127
128             if (needsQuotes)
129                 *dst++ = '"';
130             for (i = 0; i < pValue->cbData / sizeof(WCHAR) &&
131              dst - psz < csz; dst++, i++)
132             {
133                 LPCWSTR src = (LPCWSTR)pValue->pbData + i;
134
135                 WideCharToMultiByte(CP_ACP, 0, src, 1, dst,
136                  csz - (dst - psz) - 1, NULL, NULL);
137                 if (*src == '"' && dst - psz < csz - 1)
138                     *(++dst) = '"';
139             }
140             if (needsQuotes && dst - psz < csz)
141                 *dst++ = '"';
142             ret = dst - psz;
143         }
144         break;
145     default:
146         FIXME("string type %d unimplemented\n", dwValueType);
147     }
148     if (psz && csz)
149     {
150         *(psz + ret) = '\0';
151         csz--;
152         ret++;
153     }
154     else
155         ret++;
156     TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
157     return ret;
158 }
159
160 DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
161  LPWSTR psz, DWORD csz)
162 {
163     DWORD ret = 0, len, i, strLen;
164     BOOL needsQuotes = FALSE;
165
166     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz);
167
168     switch (dwValueType)
169     {
170     case CERT_RDN_ANY_TYPE:
171         break;
172     case CERT_RDN_NUMERIC_STRING:
173     case CERT_RDN_PRINTABLE_STRING:
174     case CERT_RDN_TELETEX_STRING:
175     case CERT_RDN_VIDEOTEX_STRING:
176     case CERT_RDN_IA5_STRING:
177     case CERT_RDN_GRAPHIC_STRING:
178     case CERT_RDN_VISIBLE_STRING:
179     case CERT_RDN_GENERAL_STRING:
180         len = pValue->cbData;
181         if (pValue->cbData && isspace(pValue->pbData[0]))
182             needsQuotes = TRUE;
183         if (pValue->cbData && isspace(pValue->pbData[pValue->cbData - 1]))
184             needsQuotes = TRUE;
185         for (i = 0; i < pValue->cbData; i++)
186         {
187             if (is_quotable_char(pValue->pbData[i]))
188                 needsQuotes = TRUE;
189             if (pValue->pbData[i] == '"')
190                 len += 1;
191         }
192         if (needsQuotes)
193             len += 2;
194         if (!psz || !csz)
195             ret = len;
196         else
197         {
198             WCHAR *ptr = psz;
199
200             if (needsQuotes)
201                 *ptr++ = '"';
202             for (i = 0; i < pValue->cbData && ptr - psz < csz; ptr++, i++)
203             {
204                 *ptr = pValue->pbData[i];
205                 if (pValue->pbData[i] == '"' && ptr - psz < csz - 1)
206                     *(++ptr) = '"';
207             }
208             if (needsQuotes && ptr - psz < csz)
209                 *ptr++ = '"';
210             ret = ptr - psz;
211         }
212         break;
213     case CERT_RDN_BMP_STRING:
214     case CERT_RDN_UTF8_STRING:
215         strLen = len = pValue->cbData / sizeof(WCHAR);
216         if (pValue->cbData && isspace(pValue->pbData[0]))
217             needsQuotes = TRUE;
218         if (pValue->cbData && isspace(pValue->pbData[strLen - 1]))
219             needsQuotes = TRUE;
220         for (i = 0; i < strLen; i++)
221         {
222             if (is_quotable_char(((LPCWSTR)pValue->pbData)[i]))
223                 needsQuotes = TRUE;
224             if (((LPCWSTR)pValue->pbData)[i] == '"')
225                 len += 1;
226         }
227         if (needsQuotes)
228             len += 2;
229         if (!psz || !csz)
230             ret = len;
231         else
232         {
233             WCHAR *ptr = psz;
234
235             if (needsQuotes)
236                 *ptr++ = '"';
237             for (i = 0; i < strLen && ptr - psz < csz; ptr++, i++)
238             {
239                 *ptr = ((LPCWSTR)pValue->pbData)[i];
240                 if (((LPCWSTR)pValue->pbData)[i] == '"' && ptr - psz < csz - 1)
241                     *(++ptr) = '"';
242             }
243             if (needsQuotes && ptr - psz < csz)
244                 *ptr++ = '"';
245             ret = ptr - psz;
246         }
247         break;
248     default:
249         FIXME("string type %d unimplemented\n", dwValueType);
250     }
251     if (psz && csz)
252     {
253         *(psz + ret) = '\0';
254         csz--;
255         ret++;
256     }
257     else
258         ret++;
259     TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
260     return ret;
261 }
262
263 /* Adds the prefix prefix to the string pointed to by psz, followed by the
264  * character '='.  Copies no more than csz characters.  Returns the number of
265  * characters copied.  If psz is NULL, returns the number of characters that
266  * would be copied.
267  */
268 static DWORD CRYPT_AddPrefixA(LPCSTR prefix, LPSTR psz, DWORD csz)
269 {
270     DWORD chars;
271
272     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz);
273
274     if (psz)
275     {
276         chars = min(strlen(prefix), csz);
277         memcpy(psz, prefix, chars);
278         *(psz + chars) = '=';
279         chars++;
280     }
281     else
282         chars = lstrlenA(prefix) + 1;
283     return chars;
284 }
285
286 DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
287  DWORD dwStrType, LPSTR psz, DWORD csz)
288 {
289     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
290      CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
291     static const char commaSep[] = ", ";
292     static const char semiSep[] = "; ";
293     static const char crlfSep[] = "\r\n";
294     static const char plusSep[] = " + ";
295     static const char spaceSep[] = " ";
296     DWORD ret = 0, bytes = 0;
297     BOOL bRet;
298     CERT_NAME_INFO *info;
299
300     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
301      psz, csz);
302     if (dwStrType & unsupportedFlags)
303         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
304
305     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
306      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
307     if (bRet)
308     {
309         DWORD i, j, sepLen, rdnSepLen;
310         LPCSTR sep, rdnSep;
311         BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG;
312         const CERT_RDN *rdn = info->rgRDN;
313
314         if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1);
315
316         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
317             sep = semiSep;
318         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
319             sep = crlfSep;
320         else
321             sep = commaSep;
322         sepLen = strlen(sep);
323         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
324             rdnSep = spaceSep;
325         else
326             rdnSep = plusSep;
327         rdnSepLen = strlen(rdnSep);
328         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
329         {
330             for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++)
331             {
332                 DWORD chars;
333                 char prefixBuf[10]; /* big enough for GivenName */
334                 LPCSTR prefix = NULL;
335
336                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
337                     prefix = rdn->rgRDNAttr[j].pszObjId;
338                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
339                 {
340                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
341                      CRYPT_OID_INFO_OID_KEY,
342                      rdn->rgRDNAttr[j].pszObjId,
343                      CRYPT_RDN_ATTR_OID_GROUP_ID);
344
345                     if (oidInfo)
346                     {
347                         WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1,
348                          prefixBuf, sizeof(prefixBuf), NULL, NULL);
349                         prefix = prefixBuf;
350                     }
351                     else
352                         prefix = rdn->rgRDNAttr[j].pszObjId;
353                 }
354                 if (prefix)
355                 {
356                     /* - 1 is needed to account for the NULL terminator. */
357                     chars = CRYPT_AddPrefixA(prefix,
358                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
359                     ret += chars;
360                 }
361                 chars = CertRDNValueToStrA(
362                  rdn->rgRDNAttr[j].dwValueType,
363                  &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL,
364                  psz ? csz - ret : 0);
365                 if (chars)
366                     ret += chars - 1;
367                 if (j < rdn->cRDNAttr - 1)
368                 {
369                     if (psz && ret < csz - rdnSepLen - 1)
370                         memcpy(psz + ret, rdnSep, rdnSepLen);
371                     ret += rdnSepLen;
372                 }
373             }
374             if (i < info->cRDN - 1)
375             {
376                 if (psz && ret < csz - sepLen - 1)
377                     memcpy(psz + ret, sep, sepLen);
378                 ret += sepLen;
379             }
380             if(reverse) rdn--;
381             else rdn++;
382         }
383         LocalFree(info);
384     }
385     if (psz && csz)
386     {
387         *(psz + ret) = '\0';
388         ret++;
389     }
390     else
391         ret++;
392     TRACE("Returning %s\n", debugstr_a(psz));
393     return ret;
394 }
395
396 /* Adds the prefix prefix to the wide-character string pointed to by psz,
397  * followed by the character '='.  Copies no more than csz characters.  Returns
398  * the number of characters copied.  If psz is NULL, returns the number of
399  * characters that would be copied.
400  * Assumes the characters in prefix are ASCII (not multibyte characters.)
401  */
402 static DWORD CRYPT_AddPrefixAToW(LPCSTR prefix, LPWSTR psz, DWORD csz)
403 {
404     DWORD chars;
405
406     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz);
407
408     if (psz)
409     {
410         DWORD i;
411
412         chars = min(strlen(prefix), csz);
413         for (i = 0; i < chars; i++)
414             *(psz + i) = prefix[i];
415         *(psz + chars) = '=';
416         chars++;
417     }
418     else
419         chars = lstrlenA(prefix) + 1;
420     return chars;
421 }
422
423 /* Adds the prefix prefix to the string pointed to by psz, followed by the
424  * character '='.  Copies no more than csz characters.  Returns the number of
425  * characters copied.  If psz is NULL, returns the number of characters that
426  * would be copied.
427  */
428 static DWORD CRYPT_AddPrefixW(LPCWSTR prefix, LPWSTR psz, DWORD csz)
429 {
430     DWORD chars;
431
432     TRACE("(%s, %p, %d)\n", debugstr_w(prefix), psz, csz);
433
434     if (psz)
435     {
436         chars = min(strlenW(prefix), csz);
437         memcpy(psz, prefix, chars * sizeof(WCHAR));
438         *(psz + chars) = '=';
439         chars++;
440     }
441     else
442         chars = lstrlenW(prefix) + 1;
443     return chars;
444 }
445
446 static const WCHAR indent[] = { ' ',' ',' ',' ',' ',0 };
447
448 DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel,
449  const CERT_NAME_BLOB *pName, DWORD dwStrType, LPWSTR psz, DWORD csz)
450 {
451     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
452      CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
453     static const WCHAR commaSep[] = { ',',' ',0 };
454     static const WCHAR semiSep[] = { ';',' ',0 };
455     static const WCHAR crlfSep[] = { '\r','\n',0 };
456     static const WCHAR plusSep[] = { ' ','+',' ',0 };
457     static const WCHAR spaceSep[] = { ' ',0 };
458     DWORD ret = 0, bytes = 0;
459     BOOL bRet;
460     CERT_NAME_INFO *info;
461
462     if (dwStrType & unsupportedFlags)
463         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
464
465     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
466      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
467     if (bRet)
468     {
469         DWORD i, j, sepLen, rdnSepLen;
470         LPCWSTR sep, rdnSep;
471         BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG;
472         const CERT_RDN *rdn = info->rgRDN;
473
474         if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1);
475
476         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
477             sep = semiSep;
478         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
479             sep = crlfSep;
480         else
481             sep = commaSep;
482         sepLen = lstrlenW(sep);
483         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
484             rdnSep = spaceSep;
485         else
486             rdnSep = plusSep;
487         rdnSepLen = lstrlenW(rdnSep);
488         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
489         {
490             for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++)
491             {
492                 DWORD chars;
493                 LPCSTR prefixA = NULL;
494                 LPCWSTR prefixW = NULL;
495
496                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
497                     prefixA = rdn->rgRDNAttr[j].pszObjId;
498                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
499                 {
500                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
501                      CRYPT_OID_INFO_OID_KEY,
502                      rdn->rgRDNAttr[j].pszObjId,
503                      CRYPT_RDN_ATTR_OID_GROUP_ID);
504
505                     if (oidInfo)
506                         prefixW = oidInfo->pwszName;
507                     else
508                         prefixA = rdn->rgRDNAttr[j].pszObjId;
509                 }
510                 if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
511                 {
512                     DWORD k;
513
514                     for (k = 0; k < indentLevel; k++)
515                     {
516                         if (psz)
517                         {
518                             chars = min(strlenW(indent), csz - ret - 1);
519                             memcpy(psz + ret, indent, chars * sizeof(WCHAR));
520                         }
521                         else
522                             chars = strlenW(indent);
523                         ret += chars;
524                     }
525                 }
526                 if (prefixW)
527                 {
528                     /* - 1 is needed to account for the NULL terminator. */
529                     chars = CRYPT_AddPrefixW(prefixW,
530                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
531                     ret += chars;
532                 }
533                 else if (prefixA)
534                 {
535                     /* - 1 is needed to account for the NULL terminator. */
536                     chars = CRYPT_AddPrefixAToW(prefixA,
537                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
538                     ret += chars;
539                 }
540                 chars = CertRDNValueToStrW(
541                  rdn->rgRDNAttr[j].dwValueType,
542                  &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL,
543                  psz ? csz - ret : 0);
544                 if (chars)
545                     ret += chars - 1;
546                 if (j < rdn->cRDNAttr - 1)
547                 {
548                     if (psz && ret < csz - rdnSepLen - 1)
549                         memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR));
550                     ret += rdnSepLen;
551                 }
552             }
553             if (i < info->cRDN - 1)
554             {
555                 if (psz && ret < csz - sepLen - 1)
556                     memcpy(psz + ret, sep, sepLen * sizeof(WCHAR));
557                 ret += sepLen;
558             }
559             if(reverse) rdn--;
560             else rdn++;
561         }
562         LocalFree(info);
563     }
564     if (psz && csz)
565     {
566         *(psz + ret) = '\0';
567         ret++;
568     }
569     else
570         ret++;
571     return ret;
572 }
573
574 DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
575  DWORD dwStrType, LPWSTR psz, DWORD csz)
576 {
577     BOOL ret;
578
579     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
580      psz, csz);
581
582     ret = cert_name_to_str_with_indent(dwCertEncodingType, 0, pName, dwStrType,
583      psz, csz);
584     TRACE("Returning %s\n", debugstr_w(psz));
585     return ret;
586 }
587
588 BOOL WINAPI CertStrToNameA(DWORD dwCertEncodingType, LPCSTR pszX500,
589  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
590  LPCSTR *ppszError)
591 {
592     BOOL ret;
593     int len;
594
595     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
596      debugstr_a(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
597      ppszError);
598
599     len = MultiByteToWideChar(CP_ACP, 0, pszX500, -1, NULL, 0);
600     if (len)
601     {
602         LPWSTR x500, errorStr;
603
604         if ((x500 = CryptMemAlloc(len * sizeof(WCHAR))))
605         {
606             MultiByteToWideChar(CP_ACP, 0, pszX500, -1, x500, len);
607             ret = CertStrToNameW(dwCertEncodingType, x500, dwStrType,
608              pvReserved, pbEncoded, pcbEncoded,
609              ppszError ? (LPCWSTR *)&errorStr : NULL);
610             if (ppszError)
611             {
612                 if (!ret)
613                 {
614                     LONG i;
615
616                     *ppszError = pszX500;
617                     for (i = 0; i < errorStr - x500; i++)
618                         *ppszError = CharNextA(*ppszError);
619                 }
620                 else
621                     *ppszError = NULL;
622             }
623             CryptMemFree(x500);
624         }
625         else
626         {
627             SetLastError(ERROR_OUTOFMEMORY);
628             ret = FALSE;
629         }
630     }
631     else
632     {
633         SetLastError(CRYPT_E_INVALID_X500_STRING);
634         if (ppszError)
635             *ppszError = pszX500;
636         ret = FALSE;
637     }
638     return ret;
639 }
640
641 struct KeynameKeeper
642 {
643     WCHAR  buf[10]; /* big enough for L"GivenName" */
644     LPWSTR keyName; /* usually = buf, but may be allocated */
645     DWORD  keyLen;
646 };
647
648 static void CRYPT_InitializeKeynameKeeper(struct KeynameKeeper *keeper)
649 {
650     keeper->keyName = keeper->buf;
651     keeper->keyLen = sizeof(keeper->buf) / sizeof(keeper->buf[0]);
652 }
653
654 static void CRYPT_FreeKeynameKeeper(struct KeynameKeeper *keeper)
655 {
656     if (keeper->keyName != keeper->buf)
657         CryptMemFree(keeper->keyName);
658 }
659
660 struct X500TokenW
661 {
662     LPCWSTR start;
663     LPCWSTR end;
664 };
665
666 static void CRYPT_KeynameKeeperFromTokenW(struct KeynameKeeper *keeper,
667  const struct X500TokenW *key)
668 {
669     DWORD len = key->end - key->start;
670
671     if (len > keeper->keyLen)
672     {
673         if (keeper->keyName == keeper->buf)
674             keeper->keyName = CryptMemAlloc(len * sizeof(WCHAR));
675         else
676             keeper->keyName = CryptMemRealloc(keeper->keyName,
677              len * sizeof(WCHAR));
678         keeper->keyLen = len;
679     }
680     memcpy(keeper->keyName, key->start, (key->end - key->start) *
681      sizeof(WCHAR));
682     keeper->keyName[len] = '\0';
683     TRACE("Keyname is %s\n", debugstr_w(keeper->keyName));
684 }
685
686 static BOOL CRYPT_GetNextKeyW(LPCWSTR str, struct X500TokenW *token,
687  LPCWSTR *ppszError)
688 {
689     BOOL ret = TRUE;
690
691     while (*str && isspaceW(*str))
692         str++;
693     if (*str)
694     {
695         token->start = str;
696         while (*str && *str != '=' && !isspaceW(*str))
697             str++;
698         if (*str && (*str == '=' || isspaceW(*str)))
699             token->end = str;
700         else
701         {
702             TRACE("missing equals char at %s\n", debugstr_w(token->start));
703             if (ppszError)
704                 *ppszError = token->start;
705             SetLastError(CRYPT_E_INVALID_X500_STRING);
706             ret = FALSE;
707         }
708     }
709     else
710         token->start = NULL;
711     return ret;
712 }
713
714 /* Assumes separators are characters in the 0-255 range */
715 static BOOL CRYPT_GetNextValueW(LPCWSTR str, DWORD dwFlags, LPCWSTR separators,
716  struct X500TokenW *token, LPCWSTR *ppszError)
717 {
718     BOOL ret = TRUE;
719
720     TRACE("(%s, %s, %p, %p)\n", debugstr_w(str), debugstr_w(separators), token,
721      ppszError);
722
723     while (*str && isspaceW(*str))
724         str++;
725     if (*str)
726     {
727         token->start = str;
728         if (!(dwFlags & CERT_NAME_STR_NO_QUOTING_FLAG) && *str == '"')
729         {
730             token->end = NULL;
731             str++;
732             while (!token->end && ret)
733             {
734                 while (*str && *str != '"')
735                     str++;
736                 if (*str == '"')
737                 {
738                     if (*(str + 1) != '"')
739                         token->end = str + 1;
740                     else
741                         str += 2;
742                 }
743                 else
744                 {
745                     TRACE("unterminated quote at %s\n", debugstr_w(str));
746                     if (ppszError)
747                         *ppszError = str;
748                     SetLastError(CRYPT_E_INVALID_X500_STRING);
749                     ret = FALSE;
750                 }
751             }
752         }
753         else
754         {
755             WCHAR map[256] = { 0 };
756
757             while (*separators)
758                 map[*separators++] = 1;
759             while (*str && (*str >= 0xff || !map[*str]))
760                 str++;
761             token->end = str;
762         }
763     }
764     else
765     {
766         TRACE("missing value at %s\n", debugstr_w(str));
767         if (ppszError)
768             *ppszError = str;
769         SetLastError(CRYPT_E_INVALID_X500_STRING);
770         ret = FALSE;
771     }
772     return ret;
773 }
774
775 /* Encodes the string represented by value as the string type type into the
776  * CERT_NAME_BLOB output.  If there is an error and ppszError is not NULL,
777  * *ppszError is set to the first failing character.  If there is no error,
778  * output's pbData must be freed with LocalFree.
779  */
780 static BOOL CRYPT_EncodeValueWithType(DWORD dwCertEncodingType,
781  const struct X500TokenW *value, PCERT_NAME_BLOB output, DWORD type,
782  LPCWSTR *ppszError)
783 {
784     CERT_NAME_VALUE nameValue = { type, { 0, NULL } };
785     BOOL ret = TRUE;
786
787     if (value->end > value->start)
788     {
789         nameValue.Value.pbData = CryptMemAlloc((value->end - value->start) *
790          sizeof(WCHAR));
791         if (!nameValue.Value.pbData)
792         {
793             SetLastError(ERROR_OUTOFMEMORY);
794             ret = FALSE;
795         }
796     }
797     if (ret)
798     {
799         if (value->end > value->start)
800         {
801             LONG i;
802             LPWSTR ptr = (LPWSTR)nameValue.Value.pbData;
803
804             for (i = 0; i < value->end - value->start; i++)
805             {
806                 *ptr++ = value->start[i];
807                 if (value->start[i] == '"')
808                     i++;
809             }
810             nameValue.Value.cbData = (LPBYTE)ptr - nameValue.Value.pbData;
811         }
812         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_UNICODE_NAME_VALUE,
813          &nameValue, CRYPT_ENCODE_ALLOC_FLAG, NULL, &output->pbData,
814          &output->cbData);
815         if (!ret && ppszError)
816         {
817             if (type == CERT_RDN_NUMERIC_STRING &&
818              GetLastError() == CRYPT_E_INVALID_NUMERIC_STRING)
819                 *ppszError = value->start + output->cbData;
820             else if (type == CERT_RDN_PRINTABLE_STRING &&
821              GetLastError() == CRYPT_E_INVALID_PRINTABLE_STRING)
822                 *ppszError = value->start + output->cbData;
823             else if (type == CERT_RDN_IA5_STRING &&
824              GetLastError() == CRYPT_E_INVALID_IA5_STRING)
825                 *ppszError = value->start + output->cbData;
826         }
827         CryptMemFree(nameValue.Value.pbData);
828     }
829     return ret;
830 }
831
832 static BOOL CRYPT_EncodeValue(DWORD dwCertEncodingType,
833  const struct X500TokenW *value, PCERT_NAME_BLOB output, const DWORD *types,
834  LPCWSTR *ppszError)
835 {
836     DWORD i;
837     BOOL ret;
838
839     ret = FALSE;
840     for (i = 0; !ret && types[i]; i++)
841         ret = CRYPT_EncodeValueWithType(dwCertEncodingType, value, output,
842          types[i], ppszError);
843     return ret;
844 }
845
846 static BOOL CRYPT_ValueToRDN(DWORD dwCertEncodingType, PCERT_NAME_INFO info,
847  PCCRYPT_OID_INFO keyOID, struct X500TokenW *value, LPCWSTR *ppszError)
848 {
849     BOOL ret = FALSE;
850
851     TRACE("OID %s, value %s\n", debugstr_a(keyOID->pszOID),
852      debugstr_wn(value->start, value->end - value->start));
853
854     if (!info->rgRDN)
855         info->rgRDN = CryptMemAlloc(sizeof(CERT_RDN));
856     else
857         info->rgRDN = CryptMemRealloc(info->rgRDN,
858          (info->cRDN + 1) * sizeof(CERT_RDN));
859     if (info->rgRDN)
860     {
861         /* FIXME: support multiple RDN attrs */
862         info->rgRDN[info->cRDN].rgRDNAttr =
863          CryptMemAlloc(sizeof(CERT_RDN_ATTR));
864         if (info->rgRDN[info->cRDN].rgRDNAttr)
865         {
866             static const DWORD defaultTypes[] = { CERT_RDN_PRINTABLE_STRING,
867              CERT_RDN_BMP_STRING, 0 };
868             const DWORD *types;
869
870             info->rgRDN[info->cRDN].cRDNAttr = 1;
871             info->rgRDN[info->cRDN].rgRDNAttr[0].pszObjId =
872              (LPSTR)keyOID->pszOID;
873             info->rgRDN[info->cRDN].rgRDNAttr[0].dwValueType =
874              CERT_RDN_ENCODED_BLOB;
875             if (keyOID->ExtraInfo.cbData)
876                 types = (const DWORD *)keyOID->ExtraInfo.pbData;
877             else
878                 types = defaultTypes;
879
880             /* Remove surrounding quotes */
881             if (value->start[0] == '"')
882             {
883                 value->start++;
884                 value->end--;
885             }
886             ret = CRYPT_EncodeValue(dwCertEncodingType, value,
887              &info->rgRDN[info->cRDN].rgRDNAttr[0].Value, types, ppszError);
888         }
889         else
890             SetLastError(ERROR_OUTOFMEMORY);
891         info->cRDN++;
892     }
893     else
894         SetLastError(ERROR_OUTOFMEMORY);
895     return ret;
896 }
897
898 BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500,
899  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
900  LPCWSTR *ppszError)
901 {
902     CERT_NAME_INFO info = { 0, NULL };
903     LPCWSTR str;
904     struct KeynameKeeper keeper;
905     DWORD i;
906     BOOL ret = TRUE;
907
908     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
909      debugstr_w(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
910      ppszError);
911
912     CRYPT_InitializeKeynameKeeper(&keeper);
913     str = pszX500;
914     while (str && *str && ret)
915     {
916         struct X500TokenW token;
917
918         ret = CRYPT_GetNextKeyW(str, &token, ppszError);
919         if (ret && token.start)
920         {
921             PCCRYPT_OID_INFO keyOID;
922
923             CRYPT_KeynameKeeperFromTokenW(&keeper, &token);
924             keyOID = CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, keeper.keyName,
925              CRYPT_RDN_ATTR_OID_GROUP_ID);
926             if (!keyOID)
927             {
928                 if (ppszError)
929                     *ppszError = token.start;
930                 SetLastError(CRYPT_E_INVALID_X500_STRING);
931                 ret = FALSE;
932             }
933             else
934             {
935                 str = token.end;
936                 while (isspace(*str))
937                     str++;
938                 if (*str != '=')
939                 {
940                     if (ppszError)
941                         *ppszError = str;
942                     SetLastError(CRYPT_E_INVALID_X500_STRING);
943                     ret = FALSE;
944                 }
945                 else
946                 {
947                     static const WCHAR commaSep[] = { ',',0 };
948                     static const WCHAR semiSep[] = { ';',0 };
949                     static const WCHAR crlfSep[] = { '\r','\n',0 };
950                     static const WCHAR allSeps[] = { ',',';','\r','\n',0 };
951                     LPCWSTR sep;
952
953                     str++;
954                     if (dwStrType & CERT_NAME_STR_COMMA_FLAG)
955                         sep = commaSep;
956                     else if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
957                         sep = semiSep;
958                     else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
959                         sep = crlfSep;
960                     else
961                         sep = allSeps;
962                     ret = CRYPT_GetNextValueW(str, dwStrType, sep, &token,
963                      ppszError);
964                     if (ret)
965                     {
966                         str = token.end;
967                         ret = CRYPT_ValueToRDN(dwCertEncodingType, &info,
968                          keyOID, &token, ppszError);
969                     }
970                 }
971             }
972         }
973     }
974     CRYPT_FreeKeynameKeeper(&keeper);
975     if (ret)
976     {
977         if (ppszError)
978             *ppszError = NULL;
979         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_NAME, &info,
980          0, NULL, pbEncoded, pcbEncoded);
981     }
982     for (i = 0; i < info.cRDN; i++)
983     {
984         DWORD j;
985
986         for (j = 0; j < info.rgRDN[i].cRDNAttr; j++)
987             LocalFree(info.rgRDN[i].rgRDNAttr[j].Value.pbData);
988         CryptMemFree(info.rgRDN[i].rgRDNAttr);
989     }
990     CryptMemFree(info.rgRDN);
991     return ret;
992 }
993
994 DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
995  DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
996 {
997     DWORD ret;
998
999     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType, dwFlags,
1000      pvTypePara, pszNameString, cchNameString);
1001
1002     if (pszNameString)
1003     {
1004         LPWSTR wideName;
1005         DWORD nameLen;
1006
1007         nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
1008          NULL, 0);
1009         wideName = CryptMemAlloc(nameLen * sizeof(WCHAR));
1010         if (wideName)
1011         {
1012             CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
1013              wideName, nameLen);
1014             nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen,
1015              pszNameString, cchNameString, NULL, NULL);
1016             if (nameLen <= cchNameString)
1017                 ret = nameLen;
1018             else
1019             {
1020                 pszNameString[cchNameString - 1] = '\0';
1021                 ret = cchNameString;
1022             }
1023             CryptMemFree(wideName);
1024         }
1025         else
1026         {
1027             *pszNameString = '\0';
1028             ret = 1;
1029         }
1030     }
1031     else
1032         ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
1033          NULL, 0);
1034     return ret;
1035 }
1036
1037 /* Searches cert's extensions for the alternate name extension with OID
1038  * altNameOID, and if found, searches it for the alternate name type entryType.
1039  * If found, returns a pointer to the entry, otherwise returns NULL.
1040  * Regardless of whether an entry of the desired type is found, if the
1041  * alternate name extension is present, sets *info to the decoded alternate
1042  * name extension, which you must free using LocalFree.
1043  * The return value is a pointer within *info, so don't free *info before
1044  * you're done with the return value.
1045  */
1046 static PCERT_ALT_NAME_ENTRY cert_find_alt_name_entry(PCCERT_CONTEXT cert,
1047  LPCSTR altNameOID, DWORD entryType, PCERT_ALT_NAME_INFO *info)
1048 {
1049     PCERT_ALT_NAME_ENTRY entry = NULL;
1050     PCERT_EXTENSION ext = CertFindExtension(altNameOID,
1051      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
1052
1053     if (ext)
1054     {
1055         DWORD bytes = 0;
1056
1057         if (CryptDecodeObjectEx(cert->dwCertEncodingType, X509_ALTERNATE_NAME,
1058          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
1059          info, &bytes))
1060         {
1061             DWORD i;
1062
1063             for (i = 0; !entry && i < (*info)->cAltEntry; i++)
1064                 if ((*info)->rgAltEntry[i].dwAltNameChoice == entryType)
1065                     entry = &(*info)->rgAltEntry[i];
1066         }
1067     }
1068     else
1069         *info = NULL;
1070     return entry;
1071 }
1072
1073 static DWORD cert_get_name_from_rdn_attr(DWORD encodingType,
1074  const CERT_NAME_BLOB *name, LPCSTR oid, LPWSTR pszNameString, DWORD cchNameString)
1075 {
1076     CERT_NAME_INFO *nameInfo;
1077     DWORD bytes = 0, ret = 0;
1078
1079     if (CryptDecodeObjectEx(encodingType, X509_NAME, name->pbData,
1080      name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo, &bytes))
1081     {
1082         PCERT_RDN_ATTR nameAttr = CertFindRDNAttr(oid, nameInfo);
1083
1084         if (nameAttr)
1085             ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value,
1086              pszNameString, cchNameString);
1087         LocalFree(nameInfo);
1088     }
1089     return ret;
1090 }
1091
1092 DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
1093  DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString)
1094 {
1095     DWORD ret = 0;
1096     PCERT_NAME_BLOB name;
1097     LPCSTR altNameOID;
1098
1099     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType,
1100      dwFlags, pvTypePara, pszNameString, cchNameString);
1101
1102     if (dwFlags & CERT_NAME_ISSUER_FLAG)
1103     {
1104         name = &pCertContext->pCertInfo->Issuer;
1105         altNameOID = szOID_ISSUER_ALT_NAME;
1106     }
1107     else
1108     {
1109         name = &pCertContext->pCertInfo->Subject;
1110         altNameOID = szOID_SUBJECT_ALT_NAME;
1111     }
1112
1113     switch (dwType)
1114     {
1115     case CERT_NAME_EMAIL_TYPE:
1116     {
1117         CERT_ALT_NAME_INFO *info;
1118         PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
1119          altNameOID, CERT_ALT_NAME_RFC822_NAME, &info);
1120
1121         if (entry)
1122         {
1123             if (!pszNameString)
1124                 ret = strlenW(entry->u.pwszRfc822Name) + 1;
1125             else if (cchNameString)
1126             {
1127                 ret = min(strlenW(entry->u.pwszRfc822Name), cchNameString - 1);
1128                 memcpy(pszNameString, entry->u.pwszRfc822Name,
1129                  ret * sizeof(WCHAR));
1130                 pszNameString[ret++] = 0;
1131             }
1132         }
1133         if (info)
1134             LocalFree(info);
1135         if (!ret)
1136             ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType,
1137              name, szOID_RSA_emailAddr, pszNameString, cchNameString);
1138         break;
1139     }
1140     case CERT_NAME_RDN_TYPE:
1141         if (name->cbData)
1142             ret = CertNameToStrW(pCertContext->dwCertEncodingType, name,
1143              *(DWORD *)pvTypePara, pszNameString, cchNameString);
1144         else
1145         {
1146             CERT_ALT_NAME_INFO *info;
1147             PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
1148              altNameOID, CERT_ALT_NAME_DIRECTORY_NAME, &info);
1149
1150             if (entry)
1151                 ret = CertNameToStrW(pCertContext->dwCertEncodingType,
1152                  &entry->u.DirectoryName, *(DWORD *)pvTypePara, pszNameString,
1153                  cchNameString);
1154             if (info)
1155                 LocalFree(info);
1156         }
1157         break;
1158     case CERT_NAME_ATTR_TYPE:
1159         ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType,
1160          name, pvTypePara, pszNameString, cchNameString);
1161         if (!ret)
1162         {
1163             CERT_ALT_NAME_INFO *altInfo;
1164             PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
1165              altNameOID, CERT_ALT_NAME_DIRECTORY_NAME, &altInfo);
1166
1167             if (entry)
1168                 ret = cert_name_to_str_with_indent(X509_ASN_ENCODING, 0,
1169                  &entry->u.DirectoryName, 0, pszNameString, cchNameString);
1170             if (altInfo)
1171                 LocalFree(altInfo);
1172         }
1173         break;
1174     case CERT_NAME_SIMPLE_DISPLAY_TYPE:
1175     {
1176         static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME,
1177          szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME,
1178          szOID_RSA_emailAddr };
1179         CERT_NAME_INFO *nameInfo = NULL;
1180         DWORD bytes = 0, i;
1181
1182         if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME,
1183          name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo,
1184          &bytes))
1185         {
1186             PCERT_RDN_ATTR nameAttr = NULL;
1187
1188             for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
1189              sizeof(simpleAttributeOIDs[0]); i++)
1190                 nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], nameInfo);
1191             if (nameAttr)
1192                 ret = CertRDNValueToStrW(nameAttr->dwValueType,
1193                  &nameAttr->Value, pszNameString, cchNameString);
1194             LocalFree(nameInfo);
1195         }
1196         if (!ret)
1197         {
1198             CERT_ALT_NAME_INFO *altInfo;
1199             PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
1200              altNameOID, CERT_ALT_NAME_RFC822_NAME, &altInfo);
1201
1202             if (altInfo)
1203             {
1204                 if (!entry && altInfo->cAltEntry)
1205                     entry = &altInfo->rgAltEntry[0];
1206                 if (entry)
1207                 {
1208                     if (!pszNameString)
1209                         ret = strlenW(entry->u.pwszRfc822Name) + 1;
1210                     else if (cchNameString)
1211                     {
1212                         ret = min(strlenW(entry->u.pwszRfc822Name),
1213                          cchNameString - 1);
1214                         memcpy(pszNameString, entry->u.pwszRfc822Name,
1215                          ret * sizeof(WCHAR));
1216                         pszNameString[ret++] = 0;
1217                     }
1218                 }
1219                 LocalFree(altInfo);
1220             }
1221         }
1222         break;
1223     }
1224     case CERT_NAME_FRIENDLY_DISPLAY_TYPE:
1225     {
1226         DWORD cch = cchNameString;
1227
1228         if (CertGetCertificateContextProperty(pCertContext,
1229          CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch))
1230             ret = cch;
1231         else
1232             ret = CertGetNameStringW(pCertContext,
1233              CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString,
1234              cchNameString);
1235         break;
1236     }
1237     case CERT_NAME_DNS_TYPE:
1238     {
1239         CERT_ALT_NAME_INFO *info;
1240         PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
1241          altNameOID, CERT_ALT_NAME_DNS_NAME, &info);
1242
1243         if (entry)
1244         {
1245             if (!pszNameString)
1246                 ret = strlenW(entry->u.pwszDNSName) + 1;
1247             else if (cchNameString)
1248             {
1249                 ret = min(strlenW(entry->u.pwszDNSName), cchNameString - 1);
1250                 memcpy(pszNameString, entry->u.pwszDNSName, ret * sizeof(WCHAR));
1251                 pszNameString[ret++] = 0;
1252             }
1253         }
1254         if (info)
1255             LocalFree(info);
1256         if (!ret)
1257             ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType,
1258              name, szOID_COMMON_NAME, pszNameString, cchNameString);
1259         break;
1260     }
1261     case CERT_NAME_URL_TYPE:
1262     {
1263         CERT_ALT_NAME_INFO *info;
1264         PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext,
1265          altNameOID, CERT_ALT_NAME_URL, &info);
1266
1267         if (entry)
1268         {
1269             if (!pszNameString)
1270                 ret = strlenW(entry->u.pwszURL) + 1;
1271             else if (cchNameString)
1272             {
1273                 ret = min(strlenW(entry->u.pwszURL), cchNameString - 1);
1274                 memcpy(pszNameString, entry->u.pwszURL, ret * sizeof(WCHAR));
1275                 pszNameString[ret++] = 0;
1276             }
1277         }
1278         if (info)
1279             LocalFree(info);
1280         break;
1281     }
1282     default:
1283         FIXME("unimplemented for type %d\n", dwType);
1284         ret = 0;
1285     }
1286     if (!ret)
1287     {
1288         if (!pszNameString)
1289             ret = 1;
1290         else if (cchNameString)
1291         {
1292             pszNameString[0] = 0;
1293             ret = 1;
1294         }
1295     }
1296     return ret;
1297 }