summaryrefslogtreecommitdiff
path: root/nss/lib/util/nssb64d.c
blob: 375fd508192f39969a4757fce876a166fbb1c994 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Base64 decoding (ascii to binary).
 */

#include "nssb64.h"
#include "nspr.h"
#include "secitem.h"
#include "secerr.h"

/*
 * XXX We want this basic support to go into NSPR (the PL part).
 * Until that can happen, the PL interface is going to be kept entirely
 * internal here -- all static functions and opaque data structures.
 * When someone can get it moved over into NSPR, that should be done:
 *    - giving everything names that are accepted by the NSPR module owners
 *	(though I tried to choose ones that would work without modification)
 *    - exporting the functions (remove static declarations and add
 *	to nssutil.def as necessary)
 *    - put prototypes into appropriate header file (probably replacing
 *	the entire current lib/libc/include/plbase64.h in NSPR)
 *	along with a typedef for the context structure (which should be
 *	kept opaque -- definition in the source file only, but typedef
 *	ala "typedef struct PLBase64FooStr PLBase64Foo;" in header file)
 *    - modify anything else as necessary to conform to NSPR required style
 *	(I looked but found no formatting guide to follow)
 *
 * You will want to move over everything from here down to the comment
 * which says "XXX End of base64 decoding code to be moved into NSPR",
 * into a new file in NSPR.
 */

/*
 **************************************************************
 * XXX Beginning of base64 decoding code to be moved into NSPR.
 */

/*
 * This typedef would belong in the NSPR header file (i.e. plbase64.h).
 */
typedef struct PLBase64DecoderStr PLBase64Decoder;

/*
 * The following implementation of base64 decoding was based on code
 * found in libmime (specifically, in mimeenc.c).  It has been adapted to
 * use PR types and naming as well as to provide other necessary semantics
 * (like buffer-in/buffer-out in addition to "streaming" without undue
 * performance hit of extra copying if you made the buffer versions
 * use the output_fn).  It also incorporates some aspects of the current
 * NSPR base64 decoding code.  As such, you may find similarities to
 * both of those implementations.  I tried to use names that reflected
 * the original code when possible.  For this reason you may find some
 * inconsistencies -- libmime used lots of "in" and "out" whereas the
 * NSPR version uses "src" and "dest"; sometimes I changed one to the other
 * and sometimes I left them when I thought the subroutines were at least
 * self-consistent.
 */

PR_BEGIN_EXTERN_C

/*
 * Opaque object used by the decoder to store state.
 */
struct PLBase64DecoderStr {
    /* Current token (or portion, if token_size < 4) being decoded. */
    unsigned char token[4];
    int token_size;

    /*
     * Where to write the decoded data (used when streaming, not when
     * doing all in-memory (buffer) operations).
     *
     * Note that this definition is chosen to be compatible with PR_Write.
     */
    PRInt32 (*output_fn) (void *output_arg, const unsigned char *buf,
			  PRInt32 size);
    void *output_arg;

    /*
     * Where the decoded output goes -- either temporarily (in the streaming
     * case, staged here before it goes to the output function) or what will
     * be the entire buffered result for users of the buffer version.
     */
    unsigned char *output_buffer;
    PRUint32 output_buflen;	/* the total length of allocated buffer */
    PRUint32 output_length;	/* the length that is currently populated */
};

PR_END_EXTERN_C


/*
 * Table to convert an ascii "code" to its corresponding binary value.
 * For ease of use, the binary values in the table are the actual values
 * PLUS ONE.  This is so that the special value of zero can denote an
 * invalid mapping; that was much easier than trying to fill in the other
 * values with some value other than zero, and to check for it.
 * Just remember to SUBTRACT ONE when using the value retrieved.
 */
static unsigned char base64_codetovaluep1[256] = {
/*   0: */	  0,	  0,	  0,	  0,	  0,	  0,	  0,	  0,
/*   8: */	  0,	  0,	  0,	  0,	  0,	  0,	  0,	  0,
/*  16: */	  0,	  0,	  0,	  0,	  0,	  0,	  0,	  0,
/*  24: */	  0,	  0,	  0,	  0,	  0,	  0,	  0,	  0,
/*  32: */	  0,	  0,	  0,	  0,	  0,	  0,	  0,	  0,
/*  40: */	  0,	  0,	  0,	 63,	  0,	  0,	  0,	 64,
/*  48: */	 53,	 54,	 55,	 56,	 57,	 58,	 59,	 60,
/*  56: */	 61,	 62,	  0,	  0,	  0,	  0,	  0,	  0,
/*  64: */	  0,	  1,	  2,	  3,	  4,	  5,	  6,	  7,
/*  72: */	  8,	  9,	 10,	 11,	 12,	 13,	 14,	 15,
/*  80: */	 16,	 17,	 18,	 19,	 20,	 21,	 22,	 23,
/*  88: */	 24,	 25,	 26,	  0,	  0,	  0,	  0,	  0,
/*  96: */	  0,	 27,	 28,	 29,	 30,	 31,	 32,	 33,
/* 104: */	 34,	 35,	 36,	 37,	 38,	 39,	 40,	 41,
/* 112: */	 42,	 43,	 44,	 45,	 46,	 47,	 48,	 49,
/* 120: */	 50,	 51,	 52,	  0,	  0,	  0,	  0,	  0,
/* 128: */	  0,	  0,	  0,	  0,	  0,	  0,	  0,	  0
/* and rest are all zero as well */
};

#define B64_PAD	'='


/*
 * Reads 4; writes 3 (known, or expected, to have no trailing padding).
 * Returns bytes written; -1 on error (unexpected character).
 */
static int
pl_base64_decode_4to3 (const unsigned char *in, unsigned char *out)
{
    int j;
    PRUint32 num = 0;
    unsigned char bits;

    for (j = 0; j < 4; j++) {
	bits = base64_codetovaluep1[in[j]];
	if (bits == 0)
	    return -1;
	num = (num << 6) | (bits - 1);
    }

    out[0] = (unsigned char) (num >> 16);
    out[1] = (unsigned char) ((num >> 8) & 0xFF);
    out[2] = (unsigned char) (num & 0xFF);

    return 3;
}

/*
 * Reads 3; writes 2 (caller already confirmed EOF or trailing padding).
 * Returns bytes written; -1 on error (unexpected character).
 */
static int
pl_base64_decode_3to2 (const unsigned char *in, unsigned char *out)
{
    PRUint32 num = 0;
    unsigned char bits1, bits2, bits3;

    bits1 = base64_codetovaluep1[in[0]];
    bits2 = base64_codetovaluep1[in[1]];
    bits3 = base64_codetovaluep1[in[2]];

    if ((bits1 == 0) || (bits2 == 0) || (bits3 == 0))
	return -1;

    num = ((PRUint32)(bits1 - 1)) << 10;
    num |= ((PRUint32)(bits2 - 1)) << 4;
    num |= ((PRUint32)(bits3 - 1)) >> 2;

    out[0] = (unsigned char) (num >> 8);
    out[1] = (unsigned char) (num & 0xFF);

    return 2;
}

/*
 * Reads 2; writes 1 (caller already confirmed EOF or trailing padding).
 * Returns bytes written; -1 on error (unexpected character).
 */
static int
pl_base64_decode_2to1 (const unsigned char *in, unsigned char *out)
{
    PRUint32 num = 0;
    unsigned char bits1, bits2;

    bits1 = base64_codetovaluep1[in[0]];
    bits2 = base64_codetovaluep1[in[1]];

    if ((bits1 == 0) || (bits2 == 0))
	return -1;

    num = ((PRUint32)(bits1 - 1)) << 2;
    num |= ((PRUint32)(bits2 - 1)) >> 4;

    out[0] = (unsigned char) num;

    return 1;
}

/*
 * Reads 4; writes 0-3.  Returns bytes written or -1 on error.
 * (Writes less than 3 only at (presumed) EOF.)
 */
static int
pl_base64_decode_token (const unsigned char *in, unsigned char *out)
{
    if (in[3] != B64_PAD)
	return pl_base64_decode_4to3 (in, out);

    if (in[2] == B64_PAD)
	return pl_base64_decode_2to1 (in, out);

    return pl_base64_decode_3to2 (in, out);
}

static PRStatus
pl_base64_decode_buffer (PLBase64Decoder *data, const unsigned char *in,
			 PRUint32 length)
{
    unsigned char *out = data->output_buffer;
    unsigned char *token = data->token;
    int i, n = 0;

    i = data->token_size;
    data->token_size = 0;

    while (length > 0) {
	while (i < 4 && length > 0) {
	    /*
	     * XXX Note that the following simply ignores any unexpected
	     * characters.  This is exactly what the original code in
	     * libmime did, and I am leaving it.  We certainly want to skip
	     * over whitespace (we must); this does much more than that.
	     * I am not confident changing it, and I don't want to slow
	     * the processing down doing more complicated checking, but
	     * someone else might have different ideas in the future.
	     */
	    if (base64_codetovaluep1[*in] > 0 || *in == B64_PAD)
		token[i++] = *in;
	    in++;
	    length--;
	}

	if (i < 4) {
	    /* Didn't get enough for a complete token. */
	    data->token_size = i;
	    break;
	}
	i = 0;

	PR_ASSERT((out - data->output_buffer + 3) <= data->output_buflen);

	/*
	 * Assume we are not at the end; the following function only works
	 * for an internal token (no trailing padding characters) but is
	 * faster that way.  If it hits an invalid character (padding) it
	 * will return an error; we break out of the loop and try again
	 * calling the routine that will handle a final token.
	 * Note that we intentionally do it this way rather than explicitly
	 * add a check for padding here (because that would just slow down
	 * the normal case) nor do we rely on checking whether we have more
	 * input to process (because that would also slow it down but also
	 * because we want to allow trailing garbage, especially white space
	 * and cannot tell that without read-ahead, also a slow proposition).
	 * Whew.  Understand?
	 */
	n = pl_base64_decode_4to3 (token, out);
	if (n < 0)
	    break;

	/* Advance "out" by the number of bytes just written to it. */
	out += n;
	n = 0;
    }

    /*
     * See big comment above, before call to pl_base64_decode_4to3.
     * Here we check if we error'd out of loop, and allow for the case
     * that we are processing the last interesting token.  If the routine
     * which should handle padding characters also fails, then we just
     * have bad input and give up.
     */
    if (n < 0) {
	n = pl_base64_decode_token (token, out);
	if (n < 0)
	    return PR_FAILURE;

	out += n;
    }

    /*
     * As explained above, we can get here with more input remaining, but
     * it should be all characters we do not care about (i.e. would be
     * ignored when transferring from "in" to "token" in loop above,
     * except here we choose to ignore extraneous pad characters, too).
     * Swallow it, performing that check.  If we find more characters that
     * we would expect to decode, something is wrong.
     */
    while (length > 0) {
	if (base64_codetovaluep1[*in] > 0)
	    return PR_FAILURE;
	in++;
	length--;
    }

    /* Record the length of decoded data we have left in output_buffer. */
    data->output_length = (PRUint32) (out - data->output_buffer);
    return PR_SUCCESS;
}

/*
 * Flush any remaining buffered characters.  Given well-formed input,
 * this will have nothing to do.  If the input was missing the padding
 * characters at the end, though, there could be 1-3 characters left
 * behind -- we will tolerate that by adding the padding for them.
 */
static PRStatus
pl_base64_decode_flush (PLBase64Decoder *data)
{
    int count;

    /*
     * If no remaining characters, or all are padding (also not well-formed
     * input, but again, be tolerant), then nothing more to do.  (And, that
     * is considered successful.)
     */
    if (data->token_size == 0 || data->token[0] == B64_PAD)
	return PR_SUCCESS;

    /*
     * Assume we have all the interesting input except for some expected
     * padding characters.  Add them and decode the resulting token.
     */
    while (data->token_size < 4)
	data->token[data->token_size++] = B64_PAD;

    data->token_size = 0;	/* so a subsequent flush call is a no-op */

    count = pl_base64_decode_token (data->token,
				    data->output_buffer + data->output_length);
    if (count < 0)
	return PR_FAILURE;

    /*
     * If there is an output function, call it with this last bit of data.
     * Otherwise we are doing all buffered output, and the decoded bytes
     * are now there, we just need to reflect that in the length.
     */
    if (data->output_fn != NULL) {
	PRInt32 output_result;

	PR_ASSERT(data->output_length == 0);
	output_result = data->output_fn (data->output_arg,
					 data->output_buffer,
					 (PRInt32) count);
	if (output_result < 0)
	    return  PR_FAILURE;
    } else {
	data->output_length += count;
    }

    return PR_SUCCESS;
}


/*
 * The maximum space needed to hold the output of the decoder given
 * input data of length "size".
 */
static PRUint32
PL_Base64MaxDecodedLength (PRUint32 size)
{
    return ((size * 3) / 4);
}


/*
 * A distinct internal creation function for the buffer version to use.
 * (It does not want to specify an output_fn, and we want the normal
 * Create function to require that.)  If more common initialization
 * of the decoding context needs to be done, it should be done *here*.
 */
static PLBase64Decoder *
pl_base64_create_decoder (void)
{
    return PR_NEWZAP(PLBase64Decoder);
}

/*
 * Function to start a base64 decoding context.
 * An "output_fn" is required; the "output_arg" parameter to that is optional.
 */
static PLBase64Decoder *
PL_CreateBase64Decoder (PRInt32 (*output_fn) (void *, const unsigned char *,
					      PRInt32),
			void *output_arg)
{
    PLBase64Decoder *data;

    if (output_fn == NULL) {
	PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
	return NULL;
    }

    data = pl_base64_create_decoder ();
    if (data != NULL) {
	data->output_fn = output_fn;
	data->output_arg = output_arg;
    }
    return data;
}


/*
 * Push data through the decoder, causing the output_fn (provided to Create)
 * to be called with the decoded data.
 */
static PRStatus
PL_UpdateBase64Decoder (PLBase64Decoder *data, const char *buffer,
			PRUint32 size)
{
    PRUint32 need_length;
    PRStatus status;

    /* XXX Should we do argument checking only in debug build? */
    if (data == NULL || buffer == NULL || size == 0) {
	PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
	return PR_FAILURE;
    }

    /*
     * How much space could this update need for decoding?
     */
    need_length = PL_Base64MaxDecodedLength (size + data->token_size);

    /*
     * Make sure we have at least that much.  If not, (re-)allocate.
     */
    if (need_length > data->output_buflen) {
	unsigned char *output_buffer = data->output_buffer;

	if (output_buffer != NULL)
	    output_buffer = (unsigned char *) PR_Realloc(output_buffer,
							 need_length);
	else
	    output_buffer = (unsigned char *) PR_Malloc(need_length);

	if (output_buffer == NULL)
	    return PR_FAILURE;

	data->output_buffer = output_buffer;
	data->output_buflen = need_length;
    }

    /* There should not have been any leftover output data in the buffer. */
    PR_ASSERT(data->output_length == 0);
    data->output_length = 0;

    status = pl_base64_decode_buffer (data, (const unsigned char *) buffer,
				      size);

    /* Now that we have some decoded data, write it. */
    if (status == PR_SUCCESS && data->output_length > 0) {
	PRInt32 output_result;

	PR_ASSERT(data->output_fn != NULL);
	output_result = data->output_fn (data->output_arg,
					 data->output_buffer,
					 (PRInt32) data->output_length);
	if (output_result < 0)
	    status = PR_FAILURE;
    }

    data->output_length = 0;
    return status;
}


/*
 * When you're done decoding, call this to free the data.  If "abort_p"
 * is false, then calling this may cause the output_fn to be called
 * one last time (as the last buffered data is flushed out).
 */
static PRStatus
PL_DestroyBase64Decoder (PLBase64Decoder *data, PRBool abort_p)
{
    PRStatus status = PR_SUCCESS;

    /* XXX Should we do argument checking only in debug build? */
    if (data == NULL) {
	PR_SetError (PR_INVALID_ARGUMENT_ERROR, 0);
	return PR_FAILURE;
    }

    /* Flush out the last few buffered characters. */
    if (!abort_p)
	status = pl_base64_decode_flush (data);

    if (data->output_buffer != NULL)
	PR_Free(data->output_buffer);
    PR_Free(data);

    return status;
}


/*
 * Perform base64 decoding from an input buffer to an output buffer.
 * The output buffer can be provided (as "dest"); you can also pass in
 * a NULL and this function will allocate a buffer large enough for you,
 * and return it.  If you do provide the output buffer, you must also
 * provide the maximum length of that buffer (as "maxdestlen").
 * The actual decoded length of output will be returned to you in
 * "output_destlen".
 *
 * Return value is NULL on error, the output buffer (allocated or provided)
 * otherwise.
 */
static unsigned char *
PL_Base64DecodeBuffer (const char *src, PRUint32 srclen, unsigned char *dest,
		       PRUint32 maxdestlen, PRUint32 *output_destlen)
{
    PRUint32 need_length;
    unsigned char *output_buffer = NULL;
    PLBase64Decoder *data = NULL;
    PRStatus status;

    PR_ASSERT(srclen > 0);
    if (srclen == 0) {
	PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
	return NULL;
    }

    /*
     * How much space could we possibly need for decoding this input?
     */
    need_length = PL_Base64MaxDecodedLength (srclen);

    /*
     * Make sure we have at least that much, if output buffer provided.
     * If no output buffer provided, then we allocate that much.
     */
    if (dest != NULL) {
	PR_ASSERT(maxdestlen >= need_length);
	if (maxdestlen < need_length) {
	    PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
	    goto loser;
	}
	output_buffer = dest;
    } else {
	output_buffer = (unsigned char *) PR_Malloc(need_length);
	if (output_buffer == NULL)
	    goto loser;
	maxdestlen = need_length;
    }

    data = pl_base64_create_decoder();
    if (data == NULL)
	goto loser;

    data->output_buflen = maxdestlen;
    data->output_buffer = output_buffer;

    status = pl_base64_decode_buffer (data, (const unsigned char *) src,
				      srclen);

    /*
     * We do not wait for Destroy to flush, because Destroy will also
     * get rid of our decoder context, which we need to look at first!
     */
    if (status == PR_SUCCESS)
	status = pl_base64_decode_flush (data);

    /* Must clear this or Destroy will free it. */
    data->output_buffer = NULL;

    if (status == PR_SUCCESS) {
	*output_destlen = data->output_length;
	status = PL_DestroyBase64Decoder (data, PR_FALSE);
	data = NULL;
	if (status == PR_FAILURE)
	    goto loser;
	return output_buffer;
    }

loser:
    if (dest == NULL && output_buffer != NULL)
	PR_Free(output_buffer);
    if (data != NULL)
	(void) PL_DestroyBase64Decoder (data, PR_TRUE);
    return NULL;
}


/*
 * XXX End of base64 decoding code to be moved into NSPR.
 ********************************************************
 */

/*
 * This is the beginning of the NSS cover functions.  These will
 * provide the interface we want to expose as NSS-ish.  For example,
 * they will operate on our Items, do any special handling or checking
 * we want to do, etc.
 */


PR_BEGIN_EXTERN_C

/*
 * A boring cover structure for now.  Perhaps someday it will include
 * some more interesting fields.
 */
struct NSSBase64DecoderStr {
    PLBase64Decoder *pl_data;
};

PR_END_EXTERN_C


/*
 * Function to start a base64 decoding context.
 */
NSSBase64Decoder *
NSSBase64Decoder_Create (PRInt32 (*output_fn) (void *, const unsigned char *,
					       PRInt32),
			 void *output_arg)
{
    PLBase64Decoder *pl_data;
    NSSBase64Decoder *nss_data;

    nss_data = PORT_ZNew(NSSBase64Decoder);
    if (nss_data == NULL)
	return NULL;

    pl_data = PL_CreateBase64Decoder (output_fn, output_arg);
    if (pl_data == NULL) {
	PORT_Free(nss_data);
	return NULL;
    }

    nss_data->pl_data = pl_data;
    return nss_data;
}


/*
 * Push data through the decoder, causing the output_fn (provided to Create)
 * to be called with the decoded data.
 */
SECStatus
NSSBase64Decoder_Update (NSSBase64Decoder *data, const char *buffer,
			 PRUint32 size)
{
    PRStatus pr_status;

    /* XXX Should we do argument checking only in debug build? */
    if (data == NULL) {
	PORT_SetError (SEC_ERROR_INVALID_ARGS);
	return SECFailure;
    }

    pr_status = PL_UpdateBase64Decoder (data->pl_data, buffer, size);
    if (pr_status == PR_FAILURE)
	return SECFailure;

    return SECSuccess;
}


/*
 * When you're done decoding, call this to free the data.  If "abort_p"
 * is false, then calling this may cause the output_fn to be called
 * one last time (as the last buffered data is flushed out).
 */
SECStatus
NSSBase64Decoder_Destroy (NSSBase64Decoder *data, PRBool abort_p)
{
    PRStatus pr_status;

    /* XXX Should we do argument checking only in debug build? */
    if (data == NULL) {
	PORT_SetError (SEC_ERROR_INVALID_ARGS);
	return SECFailure;
    }

    pr_status = PL_DestroyBase64Decoder (data->pl_data, abort_p);

    PORT_Free(data);

    if (pr_status == PR_FAILURE)
	return SECFailure;

    return SECSuccess;
}


/*
 * Perform base64 decoding from an ascii string "inStr" to an Item.
 * The length of the input must be provided as "inLen".  The Item
 * may be provided (as "outItemOpt"); you can also pass in a NULL
 * and the Item will be allocated for you.
 *
 * In any case, the data within the Item will be allocated for you.
 * All allocation will happen out of the passed-in "arenaOpt", if non-NULL.
 * If "arenaOpt" is NULL, standard allocation (heap) will be used and
 * you will want to free the result via SECITEM_FreeItem.
 *
 * Return value is NULL on error, the Item (allocated or provided) otherwise.
 */
SECItem *
NSSBase64_DecodeBuffer (PLArenaPool *arenaOpt, SECItem *outItemOpt,
			const char *inStr, unsigned int inLen)
{
    SECItem *out_item = NULL;
    PRUint32 max_out_len = 0;
    PRUint32 out_len;
    void *mark = NULL;
    unsigned char *dummy;

    if ((outItemOpt != NULL && outItemOpt->data != NULL) || inLen == 0) {
	PORT_SetError (SEC_ERROR_INVALID_ARGS);
	return NULL;
    }

    if (arenaOpt != NULL)
	mark = PORT_ArenaMark (arenaOpt);

    max_out_len = PL_Base64MaxDecodedLength (inLen);
    out_item = SECITEM_AllocItem (arenaOpt, outItemOpt, max_out_len);
    if (out_item == NULL) {
	if (arenaOpt != NULL)
	    PORT_ArenaRelease (arenaOpt, mark);
	return NULL;
    }

    dummy = PL_Base64DecodeBuffer (inStr, inLen, out_item->data,
				   max_out_len, &out_len);
    if (dummy == NULL) {
	if (arenaOpt != NULL) {
	    PORT_ArenaRelease (arenaOpt, mark);
	    if (outItemOpt != NULL) {
		outItemOpt->data = NULL;
		outItemOpt->len = 0;
	    }
	} else {
	    SECITEM_FreeItem (out_item,
			      (outItemOpt == NULL) ? PR_TRUE : PR_FALSE);
	}
	return NULL;
    }

    if (arenaOpt != NULL)
	PORT_ArenaUnmark (arenaOpt, mark);
    out_item->len = out_len;
    return out_item;
}


/*
 * XXX Everything below is deprecated.  If you add new stuff, put it
 * *above*, not below.
 */

/*
 * XXX The following "ATOB" functions are provided for backward compatibility
 * with current code.  They should be considered strongly deprecated.
 * When we can convert all our code over to using the new NSSBase64Decoder_
 * functions defined above, we should get rid of these altogether.  (Remove
 * protoypes from base64.h as well -- actually, remove that file completely).
 * If someone thinks either of these functions provides such a very useful
 * interface (though, as shown, the same functionality can already be
 * obtained by calling NSSBase64_DecodeBuffer directly), fine -- but then
 * that API should be provided with a nice new NSSFoo name and using
 * appropriate types, etc.
 */

#include "base64.h"

/*
** Return an PORT_Alloc'd string which is the base64 decoded version
** of the input string; set *lenp to the length of the returned data.
*/
unsigned char *
ATOB_AsciiToData(const char *string, unsigned int *lenp)
{
    SECItem binary_item, *dummy;

    binary_item.data = NULL;
    binary_item.len = 0;

    dummy = NSSBase64_DecodeBuffer (NULL, &binary_item, string,
				    (PRUint32) PORT_Strlen(string));
    if (dummy == NULL)
	return NULL;

    PORT_Assert(dummy == &binary_item);

    *lenp = dummy->len;
    return dummy->data;
}
 
/*
** Convert from ascii to binary encoding of an item.
*/
SECStatus
ATOB_ConvertAsciiToItem(SECItem *binary_item, const char *ascii)
{
    SECItem *dummy;

    if (binary_item == NULL) {
	PORT_SetError (SEC_ERROR_INVALID_ARGS);
	return SECFailure;
    }

    /*
     * XXX Would prefer to assert here if data is non-null (actually,
     * don't need to, just let NSSBase64_DecodeBuffer do it), so as to
     * to catch unintended memory leaks, but callers are not clean in
     * this respect so we need to explicitly clear here to avoid the
     * assert in NSSBase64_DecodeBuffer.
     */
    binary_item->data = NULL;
    binary_item->len = 0;

    dummy = NSSBase64_DecodeBuffer (NULL, binary_item, ascii,
				    (PRUint32) PORT_Strlen(ascii));

    if (dummy == NULL)
	return SECFailure;

    return SECSuccess;
}