1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 /*
5  * pkix_pl_string.c
6  *
7  * String Object Functions
8  *
9  */
10 
11 #include "pkix_pl_string.h"
12 
13 /* --Private-String-Functions------------------------------------- */
14 
15 /*
16  * FUNCTION: pkix_pl_String_Comparator
17  * (see comments for PKIX_PL_ComparatorCallback in pkix_pl_system.h)
18  *
19  * NOTE:
20  *  This function is a utility function called by pkix_pl_String_Equals().
21  *  It is not officially registered as a comparator.
22  */
23 static PKIX_Error *
pkix_pl_String_Comparator(PKIX_PL_String * firstString,PKIX_PL_String * secondString,PKIX_Int32 * pResult,void * plContext)24 pkix_pl_String_Comparator(
25         PKIX_PL_String *firstString,
26         PKIX_PL_String *secondString,
27         PKIX_Int32 *pResult,
28         void *plContext)
29 {
30         PKIX_UInt32 i;
31         PKIX_Int32 result;
32         unsigned char *p1 = NULL;
33         unsigned char *p2 = NULL;
34 
35         PKIX_ENTER(STRING, "pkix_pl_String_Comparator");
36         PKIX_NULLCHECK_THREE(firstString, secondString, pResult);
37 
38         result = 0;
39 
40         p1 = (unsigned char*) firstString->utf16String;
41         p2 = (unsigned char*) secondString->utf16String;
42 
43         /* Compare characters until you find a difference */
44         for (i = 0; ((i < firstString->utf16Length) &&
45                     (i < secondString->utf16Length) &&
46                     result == 0); i++, p1++, p2++) {
47                 if (*p1 < *p2){
48                         result = -1;
49                 } else if (*p1 > *p2){
50                         result = 1;
51                 }
52         }
53 
54         /* If two arrays are identical so far, the longer one is greater */
55         if (result == 0) {
56                 if (firstString->utf16Length < secondString->utf16Length) {
57                         result = -1;
58                 } else if (firstString->utf16Length >
59                             secondString->utf16Length) {
60                         result = 1;
61                 }
62         }
63 
64         *pResult = result;
65 
66         PKIX_RETURN(STRING);
67 }
68 
69 /*
70  * FUNCTION: pkix_pl_String_Destroy
71  * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
72  */
73 static PKIX_Error *
pkix_pl_String_Destroy(PKIX_PL_Object * object,void * plContext)74 pkix_pl_String_Destroy(
75         PKIX_PL_Object *object,
76         void *plContext)
77 {
78         PKIX_PL_String *string = NULL;
79 
80         PKIX_ENTER(STRING, "pkix_pl_String_Destroy");
81         PKIX_NULLCHECK_ONE(object);
82 
83         PKIX_CHECK(pkix_CheckType(object, PKIX_STRING_TYPE, plContext),
84                     PKIX_ARGUMENTNOTSTRING);
85 
86         string = (PKIX_PL_String*)object;
87 
88         /* XXX For debugging Destroy EscASCII String  */
89         if (string->escAsciiString != NULL) {
90                 PKIX_FREE(string->escAsciiString);
91                 string->escAsciiString = NULL;
92                 string->escAsciiLength = 0;
93         }
94 
95         /* Destroy UTF16 String */
96         if (string->utf16String != NULL) {
97                 PKIX_FREE(string->utf16String);
98                 string->utf16String = NULL;
99                 string->utf16Length = 0;
100         }
101 
102 cleanup:
103 
104         PKIX_RETURN(STRING);
105 }
106 
107 /*
108  * FUNCTION: pkix_pl_String_ToString
109  * (see comments for PKIX_PL_ToStringCallback in pkix_pl_system.h)
110  */
111 static PKIX_Error *
pkix_pl_String_ToString(PKIX_PL_Object * object,PKIX_PL_String ** pString,void * plContext)112 pkix_pl_String_ToString(
113         PKIX_PL_Object *object,
114         PKIX_PL_String **pString,
115         void *plContext)
116 {
117         PKIX_PL_String *string = NULL;
118         char *ascii = NULL;
119         PKIX_UInt32 length;
120 
121         PKIX_ENTER(STRING, "pkix_pl_String_ToString");
122         PKIX_NULLCHECK_TWO(object, pString);
123 
124         PKIX_CHECK(pkix_CheckType(object, PKIX_STRING_TYPE, plContext),
125                     PKIX_ARGUMENTNOTSTRING);
126 
127         string = (PKIX_PL_String*)object;
128 
129         PKIX_CHECK(PKIX_PL_String_GetEncoded
130                 (string, PKIX_ESCASCII, (void **)&ascii, &length, plContext),
131                 PKIX_STRINGGETENCODEDFAILED);
132 
133         PKIX_CHECK(PKIX_PL_String_Create
134                     (PKIX_ESCASCII, ascii, 0, pString, plContext),
135                     PKIX_STRINGCREATEFAILED);
136 
137         goto cleanup;
138 
139 cleanup:
140 
141         PKIX_FREE(ascii);
142 
143         PKIX_RETURN(STRING);
144 }
145 
146 /*
147  * FUNCTION: pkix_pl_String_Equals
148  * (see comments for PKIX_PL_EqualsCallback in pkix_pl_system.h)
149  */
150 static PKIX_Error *
pkix_pl_String_Equals(PKIX_PL_Object * firstObject,PKIX_PL_Object * secondObject,PKIX_Boolean * pResult,void * plContext)151 pkix_pl_String_Equals(
152         PKIX_PL_Object *firstObject,
153         PKIX_PL_Object *secondObject,
154         PKIX_Boolean *pResult,
155         void *plContext)
156 {
157         PKIX_UInt32 secondType;
158         PKIX_Int32 cmpResult = 0;
159 
160         PKIX_ENTER(STRING, "pkix_pl_String_Equals");
161         PKIX_NULLCHECK_THREE(firstObject, secondObject, pResult);
162 
163         /* Sanity check: Test that "firstObject" is a Strings */
164         PKIX_CHECK(pkix_CheckType(firstObject, PKIX_STRING_TYPE, plContext),
165                 PKIX_FIRSTOBJECTNOTSTRING);
166 
167         /* "SecondObject" doesn't have to be a string */
168         PKIX_CHECK(PKIX_PL_Object_GetType
169                 (secondObject, &secondType, plContext),
170                 PKIX_COULDNOTGETTYPEOFSECONDARGUMENT);
171 
172         /* If types differ, then we will return false */
173         *pResult = PKIX_FALSE;
174 
175         if (secondType != PKIX_STRING_TYPE) goto cleanup;
176 
177         /* It's safe to cast here */
178         PKIX_CHECK(pkix_pl_String_Comparator
179                 ((PKIX_PL_String*)firstObject,
180                 (PKIX_PL_String*)secondObject,
181                 &cmpResult,
182                 plContext),
183                 PKIX_STRINGCOMPARATORFAILED);
184 
185         /* Strings are equal iff Comparator Result is 0 */
186         *pResult = (cmpResult == 0);
187 
188 cleanup:
189 
190         PKIX_RETURN(STRING);
191 }
192 
193 /*
194  * FUNCTION: pkix_pl_String_Hashcode
195  * (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h)
196  */
197 static PKIX_Error *
pkix_pl_String_Hashcode(PKIX_PL_Object * object,PKIX_UInt32 * pHashcode,void * plContext)198 pkix_pl_String_Hashcode(
199         PKIX_PL_Object *object,
200         PKIX_UInt32 *pHashcode,
201         void *plContext)
202 {
203         PKIX_PL_String *string = NULL;
204 
205         PKIX_ENTER(STRING, "pkix_pl_String_Hashcode");
206         PKIX_NULLCHECK_TWO(object, pHashcode);
207 
208         PKIX_CHECK(pkix_CheckType(object, PKIX_STRING_TYPE, plContext),
209                 PKIX_OBJECTNOTSTRING);
210 
211         string = (PKIX_PL_String*)object;
212 
213         PKIX_CHECK(pkix_hash
214                 ((const unsigned char *)string->utf16String,
215                 string->utf16Length,
216                 pHashcode,
217                 plContext),
218                 PKIX_HASHFAILED);
219 
220 cleanup:
221 
222         PKIX_RETURN(STRING);
223 }
224 
225 /*
226  * FUNCTION: pkix_pl_String_RegisterSelf
227  * DESCRIPTION:
228  *  Registers PKIX_STRING_TYPE and its related functions with systemClasses[]
229  * THREAD SAFETY:
230  *  Not Thread Safe - for performance and complexity reasons
231  *
232  *  Since this function is only called by PKIX_PL_Initialize, which should
233  *  only be called once, it is acceptable that this function is not
234  *  thread-safe.
235  */
236 PKIX_Error *
pkix_pl_String_RegisterSelf(void * plContext)237 pkix_pl_String_RegisterSelf(
238         void *plContext)
239 {
240         extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
241         pkix_ClassTable_Entry entry;
242 
243         PKIX_ENTER(STRING, "pkix_pl_String_RegisterSelf");
244 
245         entry.description = "String";
246         entry.objCounter = 0;
247         entry.typeObjectSize = sizeof(PKIX_PL_String);
248         entry.destructor = pkix_pl_String_Destroy;
249         entry.equalsFunction = pkix_pl_String_Equals;
250         entry.hashcodeFunction = pkix_pl_String_Hashcode;
251         entry.toStringFunction = pkix_pl_String_ToString;
252         entry.comparator = NULL;
253         entry.duplicateFunction = pkix_duplicateImmutable;
254 
255         systemClasses[PKIX_STRING_TYPE] = entry;
256 
257         PKIX_RETURN(STRING);
258 }
259 
260 
261 /* --Public-String-Functions----------------------------------------- */
262 
263 /*
264  * FUNCTION: PKIX_PL_String_Create (see comments in pkix_pl_system.h)
265  */
266 PKIX_Error *
PKIX_PL_String_Create(PKIX_UInt32 fmtIndicator,const void * stringRep,PKIX_UInt32 stringLen,PKIX_PL_String ** pString,void * plContext)267 PKIX_PL_String_Create(
268         PKIX_UInt32 fmtIndicator,
269         const void *stringRep,
270         PKIX_UInt32 stringLen,
271         PKIX_PL_String **pString,
272         void *plContext)
273 {
274         PKIX_PL_String *string = NULL;
275         unsigned char *utf16Char = NULL;
276         PKIX_UInt32 i;
277 
278         PKIX_ENTER(STRING, "PKIX_PL_String_Create");
279         PKIX_NULLCHECK_TWO(pString, stringRep);
280 
281         PKIX_CHECK(PKIX_PL_Object_Alloc
282                     (PKIX_STRING_TYPE,
283                     sizeof (PKIX_PL_String),
284                     (PKIX_PL_Object **)&string,
285                     plContext),
286                     PKIX_COULDNOTALLOCATENEWSTRINGOBJECT);
287 
288         string->utf16String = NULL;
289         string->utf16Length = 0;
290 
291         /* XXX For Debugging */
292         string->escAsciiString = NULL;
293         string->escAsciiLength = 0;
294 
295         switch (fmtIndicator) {
296         case PKIX_ESCASCII: case PKIX_ESCASCII_DEBUG:
297                 PKIX_STRING_DEBUG("\tCalling PL_strlen).\n");
298                 string->escAsciiLength = PL_strlen(stringRep);
299 
300                 /* XXX Cache for Debugging */
301                 PKIX_CHECK(PKIX_PL_Malloc
302                             ((string->escAsciiLength)+1,
303                             (void **)&string->escAsciiString,
304                             plContext),
305                             PKIX_MALLOCFAILED);
306 
307                 (void) PORT_Memcpy
308                         (string->escAsciiString,
309                         (void *)((char *)stringRep),
310                         (string->escAsciiLength)+1);
311 
312                 /* Convert the EscASCII string to UTF16 */
313                 PKIX_CHECK(pkix_EscASCII_to_UTF16
314                             (string->escAsciiString,
315                             string->escAsciiLength,
316                             (fmtIndicator == PKIX_ESCASCII_DEBUG),
317                             &string->utf16String,
318                             &string->utf16Length,
319                             plContext),
320                             PKIX_ESCASCIITOUTF16FAILED);
321                 break;
322         case PKIX_UTF8:
323                 /* Convert the UTF8 string to UTF16 */
324                 PKIX_CHECK(pkix_UTF8_to_UTF16
325                             (stringRep,
326                             stringLen,
327                             &string->utf16String,
328                             &string->utf16Length,
329                             plContext),
330                             PKIX_UTF8TOUTF16FAILED);
331                 break;
332         case PKIX_UTF16:
333                 /* UTF16 Strings must be even in length */
334                 if (stringLen%2 == 1) {
335                         PKIX_DECREF(string);
336                         PKIX_ERROR(PKIX_UTF16ALIGNMENTERROR);
337                 }
338 
339                 utf16Char = (unsigned char *)stringRep;
340 
341                 /* Make sure this is a valid UTF-16 String */
342                 for (i = 0; \
343                     (i < stringLen) && (pkixErrorResult == NULL); \
344                     i += 2) {
345                         /* Check that surrogate pairs are valid */
346                         if ((utf16Char[i] >= 0xD8)&&
347                             (utf16Char[i] <= 0xDB)) {
348                                 if ((i+2) >= stringLen) {
349                                   PKIX_ERROR(PKIX_UTF16HIGHZONEALIGNMENTERROR);
350                                   /* Second pair should be DC00-DFFF */
351                                 } else if (!((utf16Char[i+2] >= 0xDC)&&
352                                       (utf16Char[i+2] <= 0xDF))) {
353                                   PKIX_ERROR(PKIX_UTF16LOWZONEERROR);
354                                 } else {
355                                   /*  Surrogate quartet is valid. */
356                                   i += 2;
357                                 }
358                         }
359                 }
360 
361                 /* Create UTF16 String */
362                 string->utf16Length = stringLen;
363 
364                 /* Alloc space for string */
365                 PKIX_CHECK(PKIX_PL_Malloc
366                             (stringLen, &string->utf16String, plContext),
367                             PKIX_MALLOCFAILED);
368 
369                 PKIX_STRING_DEBUG("\tCalling PORT_Memcpy).\n");
370                 (void) PORT_Memcpy
371                         (string->utf16String, stringRep, stringLen);
372                 break;
373 
374         default:
375                 PKIX_ERROR(PKIX_UNKNOWNFORMAT);
376         }
377 
378         *pString = string;
379 
380 cleanup:
381 
382         if (PKIX_ERROR_RECEIVED){
383                 PKIX_DECREF(string);
384         }
385 
386         PKIX_RETURN(STRING);
387 }
388 
389 /*
390  * FUNCTION: PKIX_PL_Sprintf (see comments in pkix_pl_system.h)
391  */
392 PKIX_Error *
PKIX_PL_Sprintf(PKIX_PL_String ** pOut,void * plContext,const PKIX_PL_String * fmt,...)393 PKIX_PL_Sprintf(
394         PKIX_PL_String **pOut,
395         void *plContext,
396         const PKIX_PL_String *fmt,
397         ...)
398 {
399         PKIX_PL_String *tempString = NULL;
400         PKIX_UInt32 tempUInt = 0;
401         void *pArg = NULL;
402         char *asciiText =  NULL;
403         char *asciiFormat = NULL;
404         char *convertedAsciiFormat = NULL;
405         char *convertedAsciiFormatBase = NULL;
406         va_list args;
407         PKIX_UInt32 length, i, j, dummyLen;
408 
409         PKIX_ENTER(STRING, "PKIX_PL_Sprintf");
410         PKIX_NULLCHECK_TWO(pOut, fmt);
411 
412         PKIX_CHECK(PKIX_PL_String_GetEncoded
413                     ((PKIX_PL_String *)fmt,
414                     PKIX_ESCASCII,
415                     (void **)&asciiFormat,
416                     &length,
417                     plContext),
418                     PKIX_STRINGGETENCODEDFAILED);
419 
420         PKIX_STRING_DEBUG("\tCalling PR_Malloc).\n");
421         convertedAsciiFormat = PR_Malloc(length + 1);
422         if (convertedAsciiFormat == NULL)
423                 PKIX_ERROR_ALLOC_ERROR();
424 
425         convertedAsciiFormatBase = convertedAsciiFormat;
426 
427         PKIX_STRING_DEBUG("\tCalling va_start).\n");
428         va_start(args, fmt);
429 
430         i = 0;
431         j = 0;
432         while (i < length) {
433                 if ((asciiFormat[i] == '%')&&((i+1) < length)) {
434                         switch (asciiFormat[i+1]) {
435                         case 's':
436                                 convertedAsciiFormat[j++] = asciiFormat[i++];
437                                 convertedAsciiFormat[j++] = asciiFormat[i++];
438                                 convertedAsciiFormat[j] = '\0';
439 
440                                 tempString = va_arg(args, PKIX_PL_String *);
441                                 if (tempString != NULL) {
442                                         PKIX_CHECK_NO_GOTO(
443                                                 PKIX_PL_String_GetEncoded
444                                                 ((PKIX_PL_String*)
445                                                 tempString,
446                                                 PKIX_ESCASCII,
447                                                 &pArg,
448                                                 &dummyLen,
449                                                 plContext),
450                                                 PKIX_STRINGGETENCODEDFAILED);
451                                         /* need to cleanup var args before
452                                          * we ditch out to cleanup. */
453                                         if (pkixErrorResult) {
454                                             va_end(args);
455                                             goto cleanup;
456                                         }
457                                 } else {
458                                         /* there may be a NULL in var_args */
459                                         pArg = NULL;
460                                 }
461                                 if (asciiText != NULL) {
462                                     asciiText = PR_sprintf_append(asciiText,
463                                           (const char *)convertedAsciiFormat,
464                                           pArg);
465                                 } else {
466                                     asciiText = PR_smprintf
467                                         ((const char *)convertedAsciiFormat,
468                                         pArg);
469                                 }
470                                 if (pArg != NULL) {
471                                         PKIX_PL_Free(pArg, plContext);
472                                         pArg = NULL;
473                                 }
474                                 convertedAsciiFormat += j;
475                                 j = 0;
476                                 break;
477                          case 'd':
478                          case 'i':
479                          case 'o':
480                          case 'u':
481                          case 'x':
482                          case 'X':
483                                 convertedAsciiFormat[j++] = asciiFormat[i++];
484                                 convertedAsciiFormat[j++] = asciiFormat[i++];
485                                 convertedAsciiFormat[j] = '\0';
486 
487                                 tempUInt = va_arg(args, PKIX_UInt32);
488                                 if (asciiText != NULL) {
489                                     asciiText = PR_sprintf_append(asciiText,
490                                           (const char *)convertedAsciiFormat,
491                                           tempUInt);
492                                 } else {
493                                     asciiText = PR_smprintf
494                                         ((const char *)convertedAsciiFormat,
495                                         tempUInt);
496                                 }
497                                 convertedAsciiFormat += j;
498                                 j = 0;
499                                 break;
500                         default:
501                                 convertedAsciiFormat[j++] = asciiFormat[i++];
502                                 convertedAsciiFormat[j++] = asciiFormat[i++];
503                                 break;
504                         }
505                 } else {
506                         convertedAsciiFormat[j++] = asciiFormat[i++];
507                 }
508         }
509 
510         /* for constant string value at end of fmt */
511         if (j > 0) {
512                 convertedAsciiFormat[j] = '\0';
513                 if (asciiText != NULL) {
514                     asciiText = PR_sprintf_append(asciiText,
515                                     (const char *)convertedAsciiFormat);
516                 } else {
517                     asciiText = PR_smprintf((const char *)convertedAsciiFormat);
518                 }
519         }
520 
521         va_end(args);
522 
523         /* Copy temporary char * into a string object */
524         PKIX_CHECK(PKIX_PL_String_Create
525                 (PKIX_ESCASCII, (void *)asciiText, 0, pOut, plContext),
526                 PKIX_STRINGCREATEFAILED);
527 
528 cleanup:
529 
530         PKIX_FREE(asciiFormat);
531 
532         if (convertedAsciiFormatBase){
533                 PR_Free(convertedAsciiFormatBase);
534         }
535 
536         if (asciiText){
537                 PKIX_STRING_DEBUG("\tCalling PR_smprintf_free).\n");
538                 PR_smprintf_free(asciiText);
539         }
540 
541         PKIX_RETURN(STRING);
542 }
543 
544 /*
545  * FUNCTION: PKIX_PL_GetString (see comments in pkix_pl_system.h)
546  */
547 PKIX_Error *
PKIX_PL_GetString(PKIX_UInt32 stringID,char * defaultString,PKIX_PL_String ** pString,void * plContext)548 PKIX_PL_GetString(
549         /* ARGSUSED */ PKIX_UInt32 stringID,
550         char *defaultString,
551         PKIX_PL_String **pString,
552         void *plContext)
553 {
554         PKIX_ENTER(STRING, "PKIX_PL_GetString");
555         PKIX_NULLCHECK_TWO(pString, defaultString);
556 
557         /* XXX Optimization - use stringID for caching */
558         PKIX_CHECK(PKIX_PL_String_Create
559                     (PKIX_ESCASCII, defaultString, 0, pString, plContext),
560                     PKIX_STRINGCREATEFAILED);
561 
562 cleanup:
563 
564         PKIX_RETURN(STRING);
565 }
566 
567 /*
568  * FUNCTION: PKIX_PL_String_GetEncoded (see comments in pkix_pl_system.h)
569  */
570 PKIX_Error *
PKIX_PL_String_GetEncoded(PKIX_PL_String * string,PKIX_UInt32 fmtIndicator,void ** pStringRep,PKIX_UInt32 * pLength,void * plContext)571 PKIX_PL_String_GetEncoded(
572         PKIX_PL_String *string,
573         PKIX_UInt32 fmtIndicator,
574         void **pStringRep,
575         PKIX_UInt32 *pLength,
576         void *plContext)
577 {
578         PKIX_ENTER(STRING, "PKIX_PL_String_GetEncoded");
579         PKIX_NULLCHECK_THREE(string, pStringRep, pLength);
580 
581         switch (fmtIndicator) {
582         case PKIX_ESCASCII: case PKIX_ESCASCII_DEBUG:
583                 PKIX_CHECK(pkix_UTF16_to_EscASCII
584                             (string->utf16String,
585                             string->utf16Length,
586                             (fmtIndicator == PKIX_ESCASCII_DEBUG),
587                             (char **)pStringRep,
588                             pLength,
589                             plContext),
590                             PKIX_UTF16TOESCASCIIFAILED);
591                 break;
592         case PKIX_UTF8:
593                 PKIX_CHECK(pkix_UTF16_to_UTF8
594                             (string->utf16String,
595                             string->utf16Length,
596                             PKIX_FALSE,
597                             pStringRep,
598                             pLength,
599                             plContext),
600                             PKIX_UTF16TOUTF8FAILED);
601                 break;
602         case PKIX_UTF8_NULL_TERM:
603                 PKIX_CHECK(pkix_UTF16_to_UTF8
604                             (string->utf16String,
605                             string->utf16Length,
606                             PKIX_TRUE,
607                             pStringRep,
608                             pLength,
609                             plContext),
610                             PKIX_UTF16TOUTF8FAILED);
611                 break;
612         case PKIX_UTF16:
613                 *pLength = string->utf16Length;
614 
615                 PKIX_CHECK(PKIX_PL_Malloc(*pLength, pStringRep, plContext),
616                             PKIX_MALLOCFAILED);
617 
618                 PKIX_STRING_DEBUG("\tCalling PORT_Memcpy).\n");
619                 (void) PORT_Memcpy(*pStringRep, string->utf16String, *pLength);
620                 break;
621         default:
622                 PKIX_ERROR(PKIX_UNKNOWNFORMAT);
623         }
624 
625 cleanup:
626 
627         PKIX_RETURN(STRING);
628 }
629