urlmon/tests: Fix timeout for tests on win9x.
[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 #include "windef.h"
20 #include "winbase.h"
21 #include "winnls.h"
22 #include "winuser.h"
23 #include "wincrypt.h"
24 #include "wine/debug.h"
25 #include "wine/unicode.h"
26
27 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
28
29 DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
30  LPSTR psz, DWORD csz)
31 {
32     DWORD ret = 0;
33
34     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz);
35
36     switch (dwValueType)
37     {
38     case CERT_RDN_ANY_TYPE:
39         break;
40     case CERT_RDN_NUMERIC_STRING:
41     case CERT_RDN_PRINTABLE_STRING:
42     case CERT_RDN_TELETEX_STRING:
43     case CERT_RDN_VIDEOTEX_STRING:
44     case CERT_RDN_IA5_STRING:
45     case CERT_RDN_GRAPHIC_STRING:
46     case CERT_RDN_VISIBLE_STRING:
47     case CERT_RDN_GENERAL_STRING:
48         if (!psz || !csz)
49             ret = pValue->cbData;
50         else
51         {
52             DWORD chars = min(pValue->cbData, csz - 1);
53
54             if (chars)
55             {
56                 memcpy(psz, pValue->pbData, chars);
57                 ret += chars;
58                 csz -= chars;
59             }
60         }
61         break;
62     case CERT_RDN_UTF8_STRING:
63         if (!psz || !csz)
64             ret = WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)pValue->pbData,
65              pValue->cbData / sizeof(WCHAR) + 1, NULL, 0, NULL, NULL);
66         else
67         {
68             ret = WideCharToMultiByte(CP_UTF8, 0, (LPWSTR)pValue->pbData,
69              pValue->cbData / sizeof(WCHAR) + 1, psz, csz - 1, NULL, NULL);
70             csz -= ret;
71         }
72         break;
73     default:
74         FIXME("string type %d unimplemented\n", dwValueType);
75     }
76     if (psz && csz)
77     {
78         *(psz + ret) = '\0';
79         csz--;
80         ret++;
81     }
82     else
83         ret++;
84     TRACE("returning %d (%s)\n", ret, debugstr_a(psz));
85     return ret;
86 }
87
88 DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue,
89  LPWSTR psz, DWORD csz)
90 {
91     DWORD ret = 0;
92
93     TRACE("(%d, %p, %p, %d)\n", dwValueType, pValue, psz, csz);
94
95     switch (dwValueType)
96     {
97     case CERT_RDN_ANY_TYPE:
98         break;
99     case CERT_RDN_NUMERIC_STRING:
100     case CERT_RDN_PRINTABLE_STRING:
101     case CERT_RDN_TELETEX_STRING:
102     case CERT_RDN_VIDEOTEX_STRING:
103     case CERT_RDN_IA5_STRING:
104     case CERT_RDN_GRAPHIC_STRING:
105     case CERT_RDN_VISIBLE_STRING:
106     case CERT_RDN_GENERAL_STRING:
107         if (!psz || !csz)
108             ret = pValue->cbData;
109         else
110         {
111             DWORD chars = min(pValue->cbData, csz - 1);
112
113             if (chars)
114             {
115                 DWORD i;
116
117                 for (i = 0; i < chars; i++)
118                     psz[i] = pValue->pbData[i];
119                 ret += chars;
120                 csz -= chars;
121             }
122         }
123         break;
124     case CERT_RDN_UTF8_STRING:
125         if (!psz || !csz)
126             ret = pValue->cbData / sizeof(WCHAR);
127         else
128         {
129             DWORD chars = min(pValue->cbData / sizeof(WCHAR), csz - 1);
130
131             if (chars)
132             {
133                 DWORD i;
134
135                 for (i = 0; i < chars; i++)
136                     psz[i] = *((LPWSTR)pValue->pbData + i);
137                 ret += chars;
138                 csz -= chars;
139             }
140         }
141         break;
142     default:
143         FIXME("string type %d unimplemented\n", dwValueType);
144     }
145     if (psz && csz)
146     {
147         *(psz + ret) = '\0';
148         csz--;
149         ret++;
150     }
151     else
152         ret++;
153     TRACE("returning %d (%s)\n", ret, debugstr_w(psz));
154     return ret;
155 }
156
157 /* Adds the prefix prefix to the string pointed to by psz, followed by the
158  * character '='.  Copies no more than csz characters.  Returns the number of
159  * characters copied.  If psz is NULL, returns the number of characters that
160  * would be copied.
161  */
162 static DWORD CRYPT_AddPrefixA(LPCSTR prefix, LPSTR psz, DWORD csz)
163 {
164     DWORD chars;
165
166     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz);
167
168     if (psz)
169     {
170         chars = min(lstrlenA(prefix), csz);
171         memcpy(psz, prefix, chars);
172         csz -= chars;
173         *(psz + chars) = '=';
174         chars++;
175         csz--;
176     }
177     else
178         chars = lstrlenA(prefix) + 1;
179     return chars;
180 }
181
182 DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
183  DWORD dwStrType, LPSTR psz, DWORD csz)
184 {
185     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
186      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
187     static const char commaSep[] = ", ";
188     static const char semiSep[] = "; ";
189     static const char crlfSep[] = "\r\n";
190     static const char plusSep[] = " + ";
191     static const char spaceSep[] = " ";
192     DWORD ret = 0, bytes = 0;
193     BOOL bRet;
194     CERT_NAME_INFO *info;
195
196     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
197      psz, csz);
198     if (dwStrType & unsupportedFlags)
199         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
200
201     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
202      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
203     if (bRet)
204     {
205         DWORD i, j, sepLen, rdnSepLen;
206         LPCSTR sep, rdnSep;
207
208         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
209             sep = semiSep;
210         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
211             sep = crlfSep;
212         else
213             sep = commaSep;
214         sepLen = strlen(sep);
215         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
216             rdnSep = spaceSep;
217         else
218             rdnSep = plusSep;
219         rdnSepLen = strlen(rdnSep);
220         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
221         {
222             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++)
223             {
224                 DWORD chars;
225                 char prefixBuf[10]; /* big enough for GivenName */
226                 LPCSTR prefix = NULL;
227
228                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
229                     prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId;
230                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
231                 {
232                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
233                      CRYPT_OID_INFO_OID_KEY,
234                      info->rgRDN[i].rgRDNAttr[j].pszObjId,
235                      CRYPT_RDN_ATTR_OID_GROUP_ID);
236
237                     if (oidInfo)
238                     {
239                         WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1,
240                          prefixBuf, sizeof(prefixBuf), NULL, NULL);
241                         prefix = prefixBuf;
242                     }
243                     else
244                         prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId;
245                 }
246                 if (prefix)
247                 {
248                     /* - 1 is needed to account for the NULL terminator. */
249                     chars = CRYPT_AddPrefixA(prefix,
250                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
251                     ret += chars;
252                     csz -= chars;
253                 }
254                 /* FIXME: handle quoting */
255                 chars = CertRDNValueToStrA(
256                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
257                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
258                  psz ? csz - ret : 0);
259                 if (chars)
260                     ret += chars - 1;
261                 if (j < info->rgRDN[i].cRDNAttr - 1)
262                 {
263                     if (psz && ret < csz - rdnSepLen - 1)
264                         memcpy(psz + ret, rdnSep, rdnSepLen);
265                     ret += rdnSepLen;
266                 }
267             }
268             if (i < info->cRDN - 1)
269             {
270                 if (psz && ret < csz - sepLen - 1)
271                     memcpy(psz + ret, sep, sepLen);
272                 ret += sepLen;
273             }
274         }
275         LocalFree(info);
276     }
277     if (psz && csz)
278     {
279         *(psz + ret) = '\0';
280         csz--;
281         ret++;
282     }
283     else
284         ret++;
285     TRACE("Returning %s\n", debugstr_a(psz));
286     return ret;
287 }
288
289 /* Adds the prefix prefix to the wide-character string pointed to by psz,
290  * followed by the character '='.  Copies no more than csz characters.  Returns
291  * the number of characters copied.  If psz is NULL, returns the number of
292  * characters that would be copied.
293  * Assumes the characters in prefix are ASCII (not multibyte characters.)
294  */
295 static DWORD CRYPT_AddPrefixAToW(LPCSTR prefix, LPWSTR psz, DWORD csz)
296 {
297     DWORD chars;
298
299     TRACE("(%s, %p, %d)\n", debugstr_a(prefix), psz, csz);
300
301     if (psz)
302     {
303         DWORD i;
304
305         chars = min(lstrlenA(prefix), csz);
306         for (i = 0; i < chars; i++)
307             *(psz + i) = prefix[i];
308         csz -= chars;
309         *(psz + chars) = '=';
310         chars++;
311         csz--;
312     }
313     else
314         chars = lstrlenA(prefix) + 1;
315     return chars;
316 }
317
318 /* Adds the prefix prefix to the string pointed to by psz, followed by the
319  * character '='.  Copies no more than csz characters.  Returns the number of
320  * characters copied.  If psz is NULL, returns the number of characters that
321  * would be copied.
322  */
323 static DWORD CRYPT_AddPrefixW(LPCWSTR prefix, LPWSTR psz, DWORD csz)
324 {
325     DWORD chars;
326
327     TRACE("(%s, %p, %d)\n", debugstr_w(prefix), psz, csz);
328
329     if (psz)
330     {
331         chars = min(lstrlenW(prefix), csz);
332         memcpy(psz, prefix, chars * sizeof(WCHAR));
333         csz -= chars;
334         *(psz + chars) = '=';
335         chars++;
336         csz--;
337     }
338     else
339         chars = lstrlenW(prefix) + 1;
340     return chars;
341 }
342
343 DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
344  DWORD dwStrType, LPWSTR psz, DWORD csz)
345 {
346     static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG |
347      CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
348     static const WCHAR commaSep[] = { ',',' ',0 };
349     static const WCHAR semiSep[] = { ';',' ',0 };
350     static const WCHAR crlfSep[] = { '\r','\n',0 };
351     static const WCHAR plusSep[] = { ' ','+',' ',0 };
352     static const WCHAR spaceSep[] = { ' ',0 };
353     DWORD ret = 0, bytes = 0;
354     BOOL bRet;
355     CERT_NAME_INFO *info;
356
357     TRACE("(%d, %p, %08x, %p, %d)\n", dwCertEncodingType, pName, dwStrType,
358      psz, csz);
359     if (dwStrType & unsupportedFlags)
360         FIXME("unsupported flags: %08x\n", dwStrType & unsupportedFlags);
361
362     bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData,
363      pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes);
364     if (bRet)
365     {
366         DWORD i, j, sepLen, rdnSepLen;
367         LPCWSTR sep, rdnSep;
368
369         if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
370             sep = semiSep;
371         else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
372             sep = crlfSep;
373         else
374             sep = commaSep;
375         sepLen = lstrlenW(sep);
376         if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG)
377             rdnSep = spaceSep;
378         else
379             rdnSep = plusSep;
380         rdnSepLen = lstrlenW(rdnSep);
381         for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++)
382         {
383             for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++)
384             {
385                 DWORD chars;
386                 LPCSTR prefixA = NULL;
387                 LPCWSTR prefixW = NULL;
388
389                 if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR)
390                     prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId;
391                 else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR)
392                 {
393                     PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo(
394                      CRYPT_OID_INFO_OID_KEY,
395                      info->rgRDN[i].rgRDNAttr[j].pszObjId,
396                      CRYPT_RDN_ATTR_OID_GROUP_ID);
397
398                     if (oidInfo)
399                         prefixW = oidInfo->pwszName;
400                     else
401                         prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId;
402                 }
403                 if (prefixW)
404                 {
405                     /* - 1 is needed to account for the NULL terminator. */
406                     chars = CRYPT_AddPrefixW(prefixW,
407                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
408                     ret += chars;
409                     csz -= chars;
410                 }
411                 else if (prefixA)
412                 {
413                     /* - 1 is needed to account for the NULL terminator. */
414                     chars = CRYPT_AddPrefixAToW(prefixA,
415                      psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0);
416                     ret += chars;
417                     csz -= chars;
418                 }
419                 /* FIXME: handle quoting */
420                 chars = CertRDNValueToStrW(
421                  info->rgRDN[i].rgRDNAttr[j].dwValueType, 
422                  &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL,
423                  psz ? csz - ret : 0);
424                 if (chars)
425                     ret += chars - 1;
426                 if (j < info->rgRDN[i].cRDNAttr - 1)
427                 {
428                     if (psz && ret < csz - rdnSepLen - 1)
429                         memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR));
430                     ret += rdnSepLen;
431                 }
432             }
433             if (i < info->cRDN - 1)
434             {
435                 if (psz && ret < csz - sepLen - 1)
436                     memcpy(psz + ret, sep, sepLen * sizeof(WCHAR));
437                 ret += sepLen;
438             }
439         }
440         LocalFree(info);
441     }
442     if (psz && csz)
443     {
444         *(psz + ret) = '\0';
445         csz--;
446         ret++;
447     }
448     else
449         ret++;
450     TRACE("Returning %s\n", debugstr_w(psz));
451     return ret;
452 }
453
454 BOOL WINAPI CertStrToNameA(DWORD dwCertEncodingType, LPCSTR pszX500,
455  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
456  LPCSTR *ppszError)
457 {
458     BOOL ret;
459     int len;
460
461     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
462      debugstr_a(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
463      ppszError);
464
465     len = MultiByteToWideChar(CP_ACP, 0, pszX500, -1, NULL, 0);
466     if (len)
467     {
468         LPWSTR x500, errorStr;
469
470         if ((x500 = CryptMemAlloc(len * sizeof(WCHAR))))
471         {
472             MultiByteToWideChar(CP_ACP, 0, pszX500, -1, x500, len);
473             ret = CertStrToNameW(dwCertEncodingType, x500, dwStrType,
474              pvReserved, pbEncoded, pcbEncoded,
475              ppszError ? (LPCWSTR *)&errorStr : NULL);
476             if (ppszError)
477             {
478                 if (!ret)
479                 {
480                     DWORD i;
481
482                     *ppszError = pszX500;
483                     for (i = 0; i < errorStr - x500; i++)
484                         *ppszError = CharNextA(*ppszError);
485                 }
486                 else
487                     *ppszError = NULL;
488             }
489             CryptMemFree(x500);
490         }
491         else
492         {
493             SetLastError(ERROR_OUTOFMEMORY);
494             ret = FALSE;
495         }
496     }
497     else
498     {
499         SetLastError(CRYPT_E_INVALID_X500_STRING);
500         if (ppszError)
501             *ppszError = pszX500;
502         ret = FALSE;
503     }
504     return ret;
505 }
506
507 struct KeynameKeeper
508 {
509     WCHAR  buf[10]; /* big enough for L"GivenName" */
510     LPWSTR keyName; /* usually = buf, but may be allocated */
511     DWORD  keyLen;
512 };
513
514 static void CRYPT_InitializeKeynameKeeper(struct KeynameKeeper *keeper)
515 {
516     keeper->keyName = keeper->buf;
517     keeper->keyLen = sizeof(keeper->buf) / sizeof(keeper->buf[0]);
518 }
519
520 static void CRYPT_FreeKeynameKeeper(struct KeynameKeeper *keeper)
521 {
522     if (keeper->keyName != keeper->buf)
523         CryptMemFree(keeper->keyName);
524 }
525
526 struct X500TokenW
527 {
528     LPCWSTR start;
529     LPCWSTR end;
530 };
531
532 static void CRYPT_KeynameKeeperFromTokenW(struct KeynameKeeper *keeper,
533  const struct X500TokenW *key)
534 {
535     DWORD len = key->end - key->start;
536
537     if (len > keeper->keyLen)
538     {
539         if (keeper->keyName == keeper->buf)
540             keeper->keyName = CryptMemAlloc(len * sizeof(WCHAR));
541         else
542             keeper->keyName = CryptMemRealloc(keeper->keyName,
543              len * sizeof(WCHAR));
544         keeper->keyLen = len;
545     }
546     memcpy(keeper->keyName, key->start, (key->end - key->start) *
547      sizeof(WCHAR));
548     keeper->keyName[len] = '\0';
549     TRACE("Keyname is %s\n", debugstr_w(keeper->keyName));
550 }
551
552 static BOOL CRYPT_GetNextKeyW(LPCWSTR str, struct X500TokenW *token,
553  LPCWSTR *ppszError)
554 {
555     BOOL ret = TRUE;
556
557     while (*str && isspaceW(*str))
558         str++;
559     if (*str)
560     {
561         token->start = str;
562         while (*str && *str != '=' && !isspaceW(*str))
563             str++;
564         if (*str && (*str == '=' || isspaceW(*str)))
565             token->end = str;
566         else
567         {
568             TRACE("missing equals char at %s\n", debugstr_w(token->start));
569             if (ppszError)
570                 *ppszError = token->start;
571             SetLastError(CRYPT_E_INVALID_X500_STRING);
572             ret = FALSE;
573         }
574     }
575     else
576         token->start = NULL;
577     return ret;
578 }
579
580 /* Assumes separators are characters in the 0-255 range */
581 static BOOL CRYPT_GetNextValueW(LPCWSTR str, DWORD dwFlags, LPCWSTR separators,
582  struct X500TokenW *token, LPCWSTR *ppszError)
583 {
584     BOOL ret = TRUE;
585
586     TRACE("(%s, %s, %p, %p)\n", debugstr_w(str), debugstr_w(separators), token,
587      ppszError);
588
589     while (*str && isspaceW(*str))
590         str++;
591     if (*str)
592     {
593         token->start = str;
594         if (!(dwFlags & CERT_NAME_STR_NO_QUOTING_FLAG) && *str == '"')
595         {
596             token->end = NULL;
597             str++;
598             while (!token->end && ret)
599             {
600                 while (*str && *str != '"')
601                     str++;
602                 if (*str == '"')
603                 {
604                     if (*(str + 1) != '"')
605                         token->end = str + 1;
606                     else
607                         str += 2;
608                 }
609                 else
610                 {
611                     TRACE("unterminated quote at %s\n", debugstr_w(str));
612                     if (ppszError)
613                         *ppszError = str;
614                     SetLastError(CRYPT_E_INVALID_X500_STRING);
615                     ret = FALSE;
616                 }
617             }
618         }
619         else
620         {
621             WCHAR map[256] = { 0 };
622
623             while (*separators)
624                 map[*separators++] = 1;
625             while (*str && (*str >= 0xff || !map[*str]))
626                 str++;
627             token->end = str;
628         }
629     }
630     else
631     {
632         TRACE("missing value at %s\n", debugstr_w(str));
633         if (ppszError)
634             *ppszError = str;
635         SetLastError(CRYPT_E_INVALID_X500_STRING);
636         ret = FALSE;
637     }
638     return ret;
639 }
640
641 /* Encodes the string represented by value as the string type type into the
642  * CERT_NAME_BLOB output.  If there is an error and ppszError is not NULL,
643  * *ppszError is set to the first failing character.  If there is no error,
644  * output's pbData must be freed with LocalFree.
645  */
646 static BOOL CRYPT_EncodeValueWithType(DWORD dwCertEncodingType,
647  const struct X500TokenW *value, PCERT_NAME_BLOB output, DWORD type,
648  LPCWSTR *ppszError)
649 {
650     CERT_NAME_VALUE nameValue = { type, { 0, NULL } };
651     BOOL ret = TRUE;
652
653     if (value->end > value->start)
654     {
655         nameValue.Value.pbData = CryptMemAlloc((value->end - value->start) *
656          sizeof(WCHAR));
657         if (!nameValue.Value.pbData)
658         {
659             SetLastError(ERROR_OUTOFMEMORY);
660             ret = FALSE;
661         }
662     }
663     if (ret)
664     {
665         if (value->end > value->start)
666         {
667             DWORD i;
668             LPWSTR ptr = (LPWSTR)nameValue.Value.pbData;
669
670             for (i = 0; i < value->end - value->start; i++)
671             {
672                 *ptr++ = value->start[i];
673                 if (value->start[i] == '"')
674                     i++;
675             }
676             nameValue.Value.cbData = (LPBYTE)ptr - nameValue.Value.pbData;
677         }
678         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_UNICODE_NAME_VALUE,
679          &nameValue, CRYPT_ENCODE_ALLOC_FLAG, NULL, &output->pbData,
680          &output->cbData);
681         if (!ret && ppszError)
682         {
683             if (type == CERT_RDN_NUMERIC_STRING &&
684              GetLastError() == CRYPT_E_INVALID_NUMERIC_STRING)
685                 *ppszError = value->start + output->cbData;
686             else if (type == CERT_RDN_PRINTABLE_STRING &&
687              GetLastError() == CRYPT_E_INVALID_PRINTABLE_STRING)
688                 *ppszError = value->start + output->cbData;
689             else if (type == CERT_RDN_IA5_STRING &&
690              GetLastError() == CRYPT_E_INVALID_IA5_STRING)
691                 *ppszError = value->start + output->cbData;
692         }
693         CryptMemFree(nameValue.Value.pbData);
694     }
695     return ret;
696 }
697
698 static BOOL CRYPT_EncodeValue(DWORD dwCertEncodingType,
699  const struct X500TokenW *value, PCERT_NAME_BLOB output, const DWORD *types,
700  LPCWSTR *ppszError)
701 {
702     DWORD i;
703     BOOL ret;
704
705     ret = FALSE;
706     for (i = 0; !ret && types[i]; i++)
707         ret = CRYPT_EncodeValueWithType(dwCertEncodingType, value, output,
708          types[i], ppszError);
709     return ret;
710 }
711
712 static BOOL CRYPT_ValueToRDN(DWORD dwCertEncodingType, PCERT_NAME_INFO info,
713  PCCRYPT_OID_INFO keyOID, struct X500TokenW *value, LPCWSTR *ppszError)
714 {
715     BOOL ret = FALSE;
716
717     TRACE("OID %s, value %s\n", debugstr_a(keyOID->pszOID),
718      debugstr_wn(value->start, value->end - value->start));
719
720     if (!info->rgRDN)
721         info->rgRDN = CryptMemAlloc(sizeof(CERT_RDN));
722     else
723         info->rgRDN = CryptMemRealloc(info->rgRDN,
724          (info->cRDN + 1) * sizeof(CERT_RDN));
725     if (info->rgRDN)
726     {
727         /* FIXME: support multiple RDN attrs */
728         info->rgRDN[info->cRDN].rgRDNAttr =
729          CryptMemAlloc(sizeof(CERT_RDN_ATTR));
730         if (info->rgRDN[info->cRDN].rgRDNAttr)
731         {
732             static const DWORD defaultTypes[] = { CERT_RDN_PRINTABLE_STRING,
733              CERT_RDN_BMP_STRING, 0 };
734             const DWORD *types;
735
736             info->rgRDN[info->cRDN].cRDNAttr = 1;
737             info->rgRDN[info->cRDN].rgRDNAttr[0].pszObjId =
738              (LPSTR)keyOID->pszOID;
739             info->rgRDN[info->cRDN].rgRDNAttr[0].dwValueType =
740              CERT_RDN_ENCODED_BLOB;
741             if (keyOID->ExtraInfo.cbData)
742                 types = (const DWORD *)keyOID->ExtraInfo.pbData;
743             else
744                 types = defaultTypes;
745
746             /* Remove surrounding quotes */
747             if (value->start[0] == '"')
748             {
749                 value->start++;
750                 value->end--;
751             }
752             ret = CRYPT_EncodeValue(dwCertEncodingType, value,
753              &info->rgRDN[info->cRDN].rgRDNAttr[0].Value, types, ppszError);
754         }
755         else
756             SetLastError(ERROR_OUTOFMEMORY);
757         info->cRDN++;
758     }
759     else
760         SetLastError(ERROR_OUTOFMEMORY);
761     return ret;
762 }
763
764 BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500,
765  DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
766  LPCWSTR *ppszError)
767 {
768     CERT_NAME_INFO info = { 0, NULL };
769     LPCWSTR str;
770     struct KeynameKeeper keeper;
771     DWORD i;
772     BOOL ret = TRUE;
773
774     TRACE("(%08x, %s, %08x, %p, %p, %p, %p)\n", dwCertEncodingType,
775      debugstr_w(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
776      ppszError);
777
778     CRYPT_InitializeKeynameKeeper(&keeper);
779     str = pszX500;
780     while (str && *str && ret)
781     {
782         struct X500TokenW token;
783
784         ret = CRYPT_GetNextKeyW(str, &token, ppszError);
785         if (ret && token.start)
786         {
787             PCCRYPT_OID_INFO keyOID;
788
789             CRYPT_KeynameKeeperFromTokenW(&keeper, &token);
790             keyOID = CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, keeper.keyName,
791              CRYPT_RDN_ATTR_OID_GROUP_ID);
792             if (!keyOID)
793             {
794                 if (ppszError)
795                     *ppszError = token.start;
796                 SetLastError(CRYPT_E_INVALID_X500_STRING);
797                 ret = FALSE;
798             }
799             else
800             {
801                 str = token.end;
802                 while (isspace(*str))
803                     str++;
804                 if (*str != '=')
805                 {
806                     if (ppszError)
807                         *ppszError = str;
808                     SetLastError(CRYPT_E_INVALID_X500_STRING);
809                     ret = FALSE;
810                 }
811                 else
812                 {
813                     static const WCHAR commaSep[] = { ',',0 };
814                     static const WCHAR semiSep[] = { ';',0 };
815                     static const WCHAR crlfSep[] = { '\r','\n',0 };
816                     static const WCHAR allSeps[] = { ',',';','\r','\n',0 };
817                     LPCWSTR sep;
818
819                     str++;
820                     if (dwStrType & CERT_NAME_STR_COMMA_FLAG)
821                         sep = commaSep;
822                     else if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG)
823                         sep = semiSep;
824                     else if (dwStrType & CERT_NAME_STR_CRLF_FLAG)
825                         sep = crlfSep;
826                     else
827                         sep = allSeps;
828                     ret = CRYPT_GetNextValueW(str, dwStrType, sep, &token,
829                      ppszError);
830                     if (ret)
831                     {
832                         str = token.end;
833                         ret = CRYPT_ValueToRDN(dwCertEncodingType, &info,
834                          keyOID, &token, ppszError);
835                     }
836                 }
837             }
838         }
839     }
840     CRYPT_FreeKeynameKeeper(&keeper);
841     if (ret)
842     {
843         if (ppszError)
844             *ppszError = NULL;
845         ret = CryptEncodeObjectEx(dwCertEncodingType, X509_NAME, &info,
846          0, NULL, pbEncoded, pcbEncoded);
847     }
848     for (i = 0; i < info.cRDN; i++)
849     {
850         DWORD j;
851
852         for (j = 0; j < info.rgRDN[i].cRDNAttr; j++)
853             LocalFree(info.rgRDN[i].rgRDNAttr[j].Value.pbData);
854         CryptMemFree(info.rgRDN[i].rgRDNAttr);
855     }
856     CryptMemFree(info.rgRDN);
857     return ret;
858 }
859
860 DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
861  DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
862 {
863     DWORD ret;
864
865     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType, dwFlags,
866      pvTypePara, pszNameString, cchNameString);
867
868     if (pszNameString)
869     {
870         LPWSTR wideName;
871         DWORD nameLen;
872
873         nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
874          NULL, 0);
875         wideName = CryptMemAlloc(nameLen * sizeof(WCHAR));
876         if (wideName)
877         {
878             CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
879              wideName, nameLen);
880             nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen,
881              pszNameString, cchNameString, NULL, NULL);
882             if (nameLen <= cchNameString)
883                 ret = nameLen;
884             else
885             {
886                 pszNameString[cchNameString - 1] = '\0';
887                 ret = cchNameString;
888             }
889             CryptMemFree(wideName);
890         }
891         else
892         {
893             *pszNameString = '\0';
894             ret = 1;
895         }
896     }
897     else
898         ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara,
899          NULL, 0);
900     return ret;
901 }
902
903 DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType,
904  DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString)
905 {
906     DWORD ret;
907     PCERT_NAME_BLOB name;
908     LPCSTR altNameOID;
909
910     TRACE("(%p, %d, %08x, %p, %p, %d)\n", pCertContext, dwType,
911      dwFlags, pvTypePara, pszNameString, cchNameString);
912
913     if (dwFlags & CERT_NAME_ISSUER_FLAG)
914     {
915         name = &pCertContext->pCertInfo->Issuer;
916         altNameOID = szOID_ISSUER_ALT_NAME;
917     }
918     else
919     {
920         name = &pCertContext->pCertInfo->Subject;
921         altNameOID = szOID_SUBJECT_ALT_NAME;
922     }
923
924     switch (dwType)
925     {
926     case CERT_NAME_SIMPLE_DISPLAY_TYPE:
927     {
928         static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME,
929          szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME,
930          szOID_RSA_emailAddr };
931         CERT_NAME_INFO *info = NULL;
932         PCERT_RDN_ATTR nameAttr = NULL;
933         DWORD bytes = 0, i;
934
935         if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME,
936          name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info,
937          &bytes))
938         {
939             for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
940              sizeof(simpleAttributeOIDs[0]); i++)
941                 nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
942         }
943         if (!nameAttr)
944         {
945             PCERT_EXTENSION ext = CertFindExtension(altNameOID,
946              pCertContext->pCertInfo->cExtension,
947              pCertContext->pCertInfo->rgExtension);
948
949             if (ext)
950             {
951                 for (i = 0; !nameAttr && i < sizeof(simpleAttributeOIDs) /
952                  sizeof(simpleAttributeOIDs[0]); i++)
953                     nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], info);
954                 if (!nameAttr)
955                 {
956                     /* FIXME: gotta then look for a rfc822Name choice in ext.
957                      * Failing that, look for the first attribute.
958                      */
959                     FIXME("CERT_NAME_SIMPLE_DISPLAY_TYPE: stub\n");
960                 }
961             }
962         }
963         if (nameAttr)
964             ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value,
965                                      pszNameString, cchNameString);
966         else
967             ret = 0;
968         if (info)
969             LocalFree(info);
970         break;
971     }
972     case CERT_NAME_FRIENDLY_DISPLAY_TYPE:
973     {
974         DWORD cch = cchNameString;
975
976         if (CertGetCertificateContextProperty(pCertContext,
977          CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch))
978             ret = cch;
979         else
980             ret = CertGetNameStringW(pCertContext,
981              CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString,
982              cchNameString);
983         break;
984     }
985     default:
986         FIXME("unimplemented for type %d\n", dwType);
987         ret = 0;
988     }
989     return ret;
990 }