155714Skris/* crypto/evp/bio_b64.c */
255714Skris/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
355714Skris * All rights reserved.
455714Skris *
555714Skris * This package is an SSL implementation written
655714Skris * by Eric Young (eay@cryptsoft.com).
755714Skris * The implementation was written so as to conform with Netscapes SSL.
8296465Sdelphij *
955714Skris * This library is free for commercial and non-commercial use as long as
1055714Skris * the following conditions are aheared to.  The following conditions
1155714Skris * apply to all code found in this distribution, be it the RC4, RSA,
1255714Skris * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
1355714Skris * included with this distribution is covered by the same copyright terms
1455714Skris * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15296465Sdelphij *
1655714Skris * Copyright remains Eric Young's, and as such any Copyright notices in
1755714Skris * the code are not to be removed.
1855714Skris * If this package is used in a product, Eric Young should be given attribution
1955714Skris * as the author of the parts of the library used.
2055714Skris * This can be in the form of a textual message at program startup or
2155714Skris * in documentation (online or textual) provided with the package.
22296465Sdelphij *
2355714Skris * Redistribution and use in source and binary forms, with or without
2455714Skris * modification, are permitted provided that the following conditions
2555714Skris * are met:
2655714Skris * 1. Redistributions of source code must retain the copyright
2755714Skris *    notice, this list of conditions and the following disclaimer.
2855714Skris * 2. Redistributions in binary form must reproduce the above copyright
2955714Skris *    notice, this list of conditions and the following disclaimer in the
3055714Skris *    documentation and/or other materials provided with the distribution.
3155714Skris * 3. All advertising materials mentioning features or use of this software
3255714Skris *    must display the following acknowledgement:
3355714Skris *    "This product includes cryptographic software written by
3455714Skris *     Eric Young (eay@cryptsoft.com)"
3555714Skris *    The word 'cryptographic' can be left out if the rouines from the library
3655714Skris *    being used are not cryptographic related :-).
37296465Sdelphij * 4. If you include any Windows specific code (or a derivative thereof) from
3855714Skris *    the apps directory (application code) you must include an acknowledgement:
3955714Skris *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40296465Sdelphij *
4155714Skris * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
4255714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4455714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5155714Skris * SUCH DAMAGE.
52296465Sdelphij *
5355714Skris * The licence and distribution terms for any publically available version or
5455714Skris * derivative of this code cannot be changed.  i.e. this code cannot simply be
5555714Skris * copied and put under another distribution licence
5655714Skris * [including the GNU Public Licence.]
5755714Skris */
5855714Skris
5955714Skris#include <stdio.h>
6055714Skris#include <errno.h>
6155714Skris#include "cryptlib.h"
6255714Skris#include <openssl/buffer.h>
6355714Skris#include <openssl/evp.h>
6455714Skris
6568651Skrisstatic int b64_write(BIO *h, const char *buf, int num);
6668651Skrisstatic int b64_read(BIO *h, char *buf, int size);
67215697Ssimonstatic int b64_puts(BIO *h, const char *str);
68296465Sdelphij/*
69296465Sdelphij * static int b64_gets(BIO *h, char *str, int size);
70296465Sdelphij */
7168651Skrisstatic long b64_ctrl(BIO *h, int cmd, long arg1, void *arg2);
7255714Skrisstatic int b64_new(BIO *h);
7355714Skrisstatic int b64_free(BIO *data);
74296465Sdelphijstatic long b64_callback_ctrl(BIO *h, int cmd, bio_info_cb *fp);
75296465Sdelphij#define B64_BLOCK_SIZE  1024
76296465Sdelphij#define B64_BLOCK_SIZE2 768
77296465Sdelphij#define B64_NONE        0
78296465Sdelphij#define B64_ENCODE      1
79296465Sdelphij#define B64_DECODE      2
8055714Skris
81296465Sdelphijtypedef struct b64_struct {
82296465Sdelphij    /*
83296465Sdelphij     * BIO *bio; moved to the BIO structure
84296465Sdelphij     */
85296465Sdelphij    int buf_len;
86296465Sdelphij    int buf_off;
87296465Sdelphij    int tmp_len;                /* used to find the start when decoding */
88296465Sdelphij    int tmp_nl;                 /* If true, scan until '\n' */
89296465Sdelphij    int encode;
90296465Sdelphij    int start;                  /* have we started decoding yet? */
91296465Sdelphij    int cont;                   /* <= 0 when finished */
92296465Sdelphij    EVP_ENCODE_CTX base64;
93296465Sdelphij    char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE) + 10];
94296465Sdelphij    char tmp[B64_BLOCK_SIZE];
95296465Sdelphij} BIO_B64_CTX;
9655714Skris
97296465Sdelphijstatic BIO_METHOD methods_b64 = {
98296465Sdelphij    BIO_TYPE_BASE64, "base64 encoding",
99296465Sdelphij    b64_write,
100296465Sdelphij    b64_read,
101296465Sdelphij    b64_puts,
102296465Sdelphij    NULL,                       /* b64_gets, */
103296465Sdelphij    b64_ctrl,
104296465Sdelphij    b64_new,
105296465Sdelphij    b64_free,
106296465Sdelphij    b64_callback_ctrl,
107296465Sdelphij};
10855714Skris
10955714SkrisBIO_METHOD *BIO_f_base64(void)
110296465Sdelphij{
111296465Sdelphij    return (&methods_b64);
112296465Sdelphij}
11355714Skris
11455714Skrisstatic int b64_new(BIO *bi)
115296465Sdelphij{
116296465Sdelphij    BIO_B64_CTX *ctx;
11755714Skris
118296465Sdelphij    ctx = (BIO_B64_CTX *)OPENSSL_malloc(sizeof(BIO_B64_CTX));
119296465Sdelphij    if (ctx == NULL)
120296465Sdelphij        return (0);
12155714Skris
122296465Sdelphij    ctx->buf_len = 0;
123296465Sdelphij    ctx->tmp_len = 0;
124296465Sdelphij    ctx->tmp_nl = 0;
125296465Sdelphij    ctx->buf_off = 0;
126296465Sdelphij    ctx->cont = 1;
127296465Sdelphij    ctx->start = 1;
128296465Sdelphij    ctx->encode = 0;
12955714Skris
130296465Sdelphij    bi->init = 1;
131296465Sdelphij    bi->ptr = (char *)ctx;
132296465Sdelphij    bi->flags = 0;
133296465Sdelphij    bi->num = 0;
134296465Sdelphij    return (1);
135296465Sdelphij}
13655714Skris
13755714Skrisstatic int b64_free(BIO *a)
138296465Sdelphij{
139296465Sdelphij    if (a == NULL)
140296465Sdelphij        return (0);
141296465Sdelphij    OPENSSL_free(a->ptr);
142296465Sdelphij    a->ptr = NULL;
143296465Sdelphij    a->init = 0;
144296465Sdelphij    a->flags = 0;
145296465Sdelphij    return (1);
146296465Sdelphij}
147296465Sdelphij
14855714Skrisstatic int b64_read(BIO *b, char *out, int outl)
149296465Sdelphij{
150296465Sdelphij    int ret = 0, i, ii, j, k, x, n, num, ret_code = 0;
151296465Sdelphij    BIO_B64_CTX *ctx;
152296465Sdelphij    unsigned char *p, *q;
15355714Skris
154296465Sdelphij    if (out == NULL)
155296465Sdelphij        return (0);
156296465Sdelphij    ctx = (BIO_B64_CTX *)b->ptr;
15755714Skris
158296465Sdelphij    if ((ctx == NULL) || (b->next_bio == NULL))
159296465Sdelphij        return (0);
16055714Skris
161296465Sdelphij    BIO_clear_retry_flags(b);
162215697Ssimon
163296465Sdelphij    if (ctx->encode != B64_DECODE) {
164296465Sdelphij        ctx->encode = B64_DECODE;
165296465Sdelphij        ctx->buf_len = 0;
166296465Sdelphij        ctx->buf_off = 0;
167296465Sdelphij        ctx->tmp_len = 0;
168296465Sdelphij        EVP_DecodeInit(&(ctx->base64));
169296465Sdelphij    }
17055714Skris
171296465Sdelphij    /* First check if there are bytes decoded/encoded */
172296465Sdelphij    if (ctx->buf_len > 0) {
173296465Sdelphij        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
174296465Sdelphij        i = ctx->buf_len - ctx->buf_off;
175296465Sdelphij        if (i > outl)
176296465Sdelphij            i = outl;
177296465Sdelphij        OPENSSL_assert(ctx->buf_off + i < (int)sizeof(ctx->buf));
178296465Sdelphij        memcpy(out, &(ctx->buf[ctx->buf_off]), i);
179296465Sdelphij        ret = i;
180296465Sdelphij        out += i;
181296465Sdelphij        outl -= i;
182296465Sdelphij        ctx->buf_off += i;
183296465Sdelphij        if (ctx->buf_len == ctx->buf_off) {
184296465Sdelphij            ctx->buf_len = 0;
185296465Sdelphij            ctx->buf_off = 0;
186296465Sdelphij        }
187296465Sdelphij    }
18855714Skris
189296465Sdelphij    /*
190296465Sdelphij     * At this point, we have room of outl bytes and an empty buffer, so we
191296465Sdelphij     * should read in some more.
192296465Sdelphij     */
19355714Skris
194296465Sdelphij    ret_code = 0;
195296465Sdelphij    while (outl > 0) {
196296465Sdelphij        if (ctx->cont <= 0)
197296465Sdelphij            break;
198120631Snectar
199296465Sdelphij        i = BIO_read(b->next_bio, &(ctx->tmp[ctx->tmp_len]),
200296465Sdelphij                     B64_BLOCK_SIZE - ctx->tmp_len);
20155714Skris
202296465Sdelphij        if (i <= 0) {
203296465Sdelphij            ret_code = i;
20455714Skris
205296465Sdelphij            /* Should we continue next time we are called? */
206296465Sdelphij            if (!BIO_should_retry(b->next_bio)) {
207296465Sdelphij                ctx->cont = i;
208296465Sdelphij                /* If buffer empty break */
209296465Sdelphij                if (ctx->tmp_len == 0)
210296465Sdelphij                    break;
211296465Sdelphij                /* Fall through and process what we have */
212296465Sdelphij                else
213296465Sdelphij                    i = 0;
214296465Sdelphij            }
215296465Sdelphij            /* else we retry and add more data to buffer */
216296465Sdelphij            else
217296465Sdelphij                break;
218296465Sdelphij        }
219296465Sdelphij        i += ctx->tmp_len;
220296465Sdelphij        ctx->tmp_len = i;
22155714Skris
222296465Sdelphij        /*
223296465Sdelphij         * We need to scan, a line at a time until we have a valid line if we
224296465Sdelphij         * are starting.
225296465Sdelphij         */
226296465Sdelphij        if (ctx->start && (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)) {
227296465Sdelphij            /* ctx->start=1; */
228296465Sdelphij            ctx->tmp_len = 0;
229296465Sdelphij        } else if (ctx->start) {
230296465Sdelphij            q = p = (unsigned char *)ctx->tmp;
231296465Sdelphij            num = 0;
232296465Sdelphij            for (j = 0; j < i; j++) {
233296465Sdelphij                if (*(q++) != '\n')
234296465Sdelphij                    continue;
23555714Skris
236296465Sdelphij                /*
237296465Sdelphij                 * due to a previous very long line, we need to keep on
238296465Sdelphij                 * scanning for a '\n' before we even start looking for
239296465Sdelphij                 * base64 encoded stuff.
240296465Sdelphij                 */
241296465Sdelphij                if (ctx->tmp_nl) {
242296465Sdelphij                    p = q;
243296465Sdelphij                    ctx->tmp_nl = 0;
244296465Sdelphij                    continue;
245296465Sdelphij                }
24655714Skris
247296465Sdelphij                k = EVP_DecodeUpdate(&(ctx->base64),
248296465Sdelphij                                     (unsigned char *)ctx->buf,
249296465Sdelphij                                     &num, p, q - p);
250296465Sdelphij                if ((k <= 0) && (num == 0) && (ctx->start))
251296465Sdelphij                    EVP_DecodeInit(&ctx->base64);
252296465Sdelphij                else {
253296465Sdelphij                    if (p != (unsigned char *)
254296465Sdelphij                        &(ctx->tmp[0])) {
255296465Sdelphij                        i -= (p - (unsigned char *)
256296465Sdelphij                              &(ctx->tmp[0]));
257296465Sdelphij                        for (x = 0; x < i; x++)
258296465Sdelphij                            ctx->tmp[x] = p[x];
259296465Sdelphij                    }
260296465Sdelphij                    EVP_DecodeInit(&ctx->base64);
261296465Sdelphij                    ctx->start = 0;
262296465Sdelphij                    break;
263296465Sdelphij                }
264296465Sdelphij                p = q;
265296465Sdelphij            }
26655714Skris
267296465Sdelphij            /* we fell off the end without starting */
268296465Sdelphij            if ((j == i) && (num == 0)) {
269296465Sdelphij                /*
270296465Sdelphij                 * Is this is one long chunk?, if so, keep on reading until a
271296465Sdelphij                 * new line.
272296465Sdelphij                 */
273296465Sdelphij                if (p == (unsigned char *)&(ctx->tmp[0])) {
274296465Sdelphij                    /* Check buffer full */
275296465Sdelphij                    if (i == B64_BLOCK_SIZE) {
276296465Sdelphij                        ctx->tmp_nl = 1;
277296465Sdelphij                        ctx->tmp_len = 0;
278296465Sdelphij                    }
279296465Sdelphij                } else if (p != q) { /* finished on a '\n' */
280296465Sdelphij                    n = q - p;
281296465Sdelphij                    for (ii = 0; ii < n; ii++)
282296465Sdelphij                        ctx->tmp[ii] = p[ii];
283296465Sdelphij                    ctx->tmp_len = n;
284296465Sdelphij                }
285296465Sdelphij                /* else finished on a '\n' */
286296465Sdelphij                continue;
287296465Sdelphij            } else {
288296465Sdelphij                ctx->tmp_len = 0;
289296465Sdelphij            }
290296465Sdelphij        } else if ((i < B64_BLOCK_SIZE) && (ctx->cont > 0)) {
291296465Sdelphij            /*
292296465Sdelphij             * If buffer isn't full and we can retry then restart to read in
293296465Sdelphij             * more data.
294296465Sdelphij             */
295296465Sdelphij            continue;
296296465Sdelphij        }
29755714Skris
298296465Sdelphij        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) {
299296465Sdelphij            int z, jj;
30055714Skris
301215697Ssimon#if 0
302296465Sdelphij            jj = (i >> 2) << 2;
303215697Ssimon#else
304296465Sdelphij            jj = i & ~3;        /* process per 4 */
305215697Ssimon#endif
306296465Sdelphij            z = EVP_DecodeBlock((unsigned char *)ctx->buf,
307296465Sdelphij                                (unsigned char *)ctx->tmp, jj);
308296465Sdelphij            if (jj > 2) {
309296465Sdelphij                if (ctx->tmp[jj - 1] == '=') {
310296465Sdelphij                    z--;
311296465Sdelphij                    if (ctx->tmp[jj - 2] == '=')
312296465Sdelphij                        z--;
313296465Sdelphij                }
314296465Sdelphij            }
315296465Sdelphij            /*
316296465Sdelphij             * z is now number of output bytes and jj is the number consumed
317296465Sdelphij             */
318296465Sdelphij            if (jj != i) {
319296465Sdelphij                memmove(ctx->tmp, &ctx->tmp[jj], i - jj);
320296465Sdelphij                ctx->tmp_len = i - jj;
321296465Sdelphij            }
322296465Sdelphij            ctx->buf_len = 0;
323296465Sdelphij            if (z > 0) {
324296465Sdelphij                ctx->buf_len = z;
325296465Sdelphij            }
326296465Sdelphij            i = z;
327296465Sdelphij        } else {
328296465Sdelphij            i = EVP_DecodeUpdate(&(ctx->base64),
329296465Sdelphij                                 (unsigned char *)ctx->buf, &ctx->buf_len,
330296465Sdelphij                                 (unsigned char *)ctx->tmp, i);
331296465Sdelphij            ctx->tmp_len = 0;
332296465Sdelphij        }
333296465Sdelphij        ctx->buf_off = 0;
334296465Sdelphij        if (i < 0) {
335296465Sdelphij            ret_code = 0;
336296465Sdelphij            ctx->buf_len = 0;
337296465Sdelphij            break;
338296465Sdelphij        }
33955714Skris
340296465Sdelphij        if (ctx->buf_len <= outl)
341296465Sdelphij            i = ctx->buf_len;
342296465Sdelphij        else
343296465Sdelphij            i = outl;
34455714Skris
345296465Sdelphij        memcpy(out, ctx->buf, i);
346296465Sdelphij        ret += i;
347296465Sdelphij        ctx->buf_off = i;
348296465Sdelphij        if (ctx->buf_off == ctx->buf_len) {
349296465Sdelphij            ctx->buf_len = 0;
350296465Sdelphij            ctx->buf_off = 0;
351296465Sdelphij        }
352296465Sdelphij        outl -= i;
353296465Sdelphij        out += i;
354296465Sdelphij    }
355296465Sdelphij    /* BIO_clear_retry_flags(b); */
356296465Sdelphij    BIO_copy_next_retry(b);
357296465Sdelphij    return ((ret == 0) ? ret_code : ret);
358296465Sdelphij}
35955714Skris
36068651Skrisstatic int b64_write(BIO *b, const char *in, int inl)
361296465Sdelphij{
362296465Sdelphij    int ret = 0;
363296465Sdelphij    int n;
364296465Sdelphij    int i;
365296465Sdelphij    BIO_B64_CTX *ctx;
36655714Skris
367296465Sdelphij    ctx = (BIO_B64_CTX *)b->ptr;
368296465Sdelphij    BIO_clear_retry_flags(b);
36955714Skris
370296465Sdelphij    if (ctx->encode != B64_ENCODE) {
371296465Sdelphij        ctx->encode = B64_ENCODE;
372296465Sdelphij        ctx->buf_len = 0;
373296465Sdelphij        ctx->buf_off = 0;
374296465Sdelphij        ctx->tmp_len = 0;
375296465Sdelphij        EVP_EncodeInit(&(ctx->base64));
376296465Sdelphij    }
37755714Skris
378296465Sdelphij    OPENSSL_assert(ctx->buf_off < (int)sizeof(ctx->buf));
379296465Sdelphij    OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
380296465Sdelphij    OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
381296465Sdelphij    n = ctx->buf_len - ctx->buf_off;
382296465Sdelphij    while (n > 0) {
383296465Sdelphij        i = BIO_write(b->next_bio, &(ctx->buf[ctx->buf_off]), n);
384296465Sdelphij        if (i <= 0) {
385296465Sdelphij            BIO_copy_next_retry(b);
386296465Sdelphij            return (i);
387296465Sdelphij        }
388296465Sdelphij        OPENSSL_assert(i <= n);
389296465Sdelphij        ctx->buf_off += i;
390296465Sdelphij        OPENSSL_assert(ctx->buf_off <= (int)sizeof(ctx->buf));
391296465Sdelphij        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
392296465Sdelphij        n -= i;
393296465Sdelphij    }
394296465Sdelphij    /* at this point all pending data has been written */
395296465Sdelphij    ctx->buf_off = 0;
396296465Sdelphij    ctx->buf_len = 0;
39755714Skris
398296465Sdelphij    if ((in == NULL) || (inl <= 0))
399296465Sdelphij        return (0);
40055714Skris
401296465Sdelphij    while (inl > 0) {
402296465Sdelphij        n = (inl > B64_BLOCK_SIZE) ? B64_BLOCK_SIZE : inl;
40355714Skris
404296465Sdelphij        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) {
405296465Sdelphij            if (ctx->tmp_len > 0) {
406296465Sdelphij                OPENSSL_assert(ctx->tmp_len <= 3);
407296465Sdelphij                n = 3 - ctx->tmp_len;
408296465Sdelphij                /*
409296465Sdelphij                 * There's a theoretical possibility for this
410296465Sdelphij                 */
411296465Sdelphij                if (n > inl)
412296465Sdelphij                    n = inl;
413296465Sdelphij                memcpy(&(ctx->tmp[ctx->tmp_len]), in, n);
414296465Sdelphij                ctx->tmp_len += n;
415296465Sdelphij                ret += n;
416296465Sdelphij                if (ctx->tmp_len < 3)
417296465Sdelphij                    break;
418296465Sdelphij                ctx->buf_len =
419296465Sdelphij                    EVP_EncodeBlock((unsigned char *)ctx->buf,
420296465Sdelphij                                    (unsigned char *)ctx->tmp, ctx->tmp_len);
421296465Sdelphij                OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
422296465Sdelphij                OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
423296465Sdelphij                /*
424296465Sdelphij                 * Since we're now done using the temporary buffer, the
425296465Sdelphij                 * length should be 0'd
426296465Sdelphij                 */
427296465Sdelphij                ctx->tmp_len = 0;
428296465Sdelphij            } else {
429296465Sdelphij                if (n < 3) {
430296465Sdelphij                    memcpy(ctx->tmp, in, n);
431296465Sdelphij                    ctx->tmp_len = n;
432296465Sdelphij                    ret += n;
433296465Sdelphij                    break;
434296465Sdelphij                }
435296465Sdelphij                n -= n % 3;
436296465Sdelphij                ctx->buf_len =
437296465Sdelphij                    EVP_EncodeBlock((unsigned char *)ctx->buf,
438296465Sdelphij                                    (const unsigned char *)in, n);
439296465Sdelphij                OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
440296465Sdelphij                OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
441296465Sdelphij                ret += n;
442296465Sdelphij            }
443296465Sdelphij        } else {
444296465Sdelphij            EVP_EncodeUpdate(&(ctx->base64),
445296465Sdelphij                             (unsigned char *)ctx->buf, &ctx->buf_len,
446296465Sdelphij                             (unsigned char *)in, n);
447296465Sdelphij            OPENSSL_assert(ctx->buf_len <= (int)sizeof(ctx->buf));
448296465Sdelphij            OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
449296465Sdelphij            ret += n;
450296465Sdelphij        }
451296465Sdelphij        inl -= n;
452296465Sdelphij        in += n;
45355714Skris
454296465Sdelphij        ctx->buf_off = 0;
455296465Sdelphij        n = ctx->buf_len;
456296465Sdelphij        while (n > 0) {
457296465Sdelphij            i = BIO_write(b->next_bio, &(ctx->buf[ctx->buf_off]), n);
458296465Sdelphij            if (i <= 0) {
459296465Sdelphij                BIO_copy_next_retry(b);
460296465Sdelphij                return ((ret == 0) ? i : ret);
461296465Sdelphij            }
462296465Sdelphij            OPENSSL_assert(i <= n);
463296465Sdelphij            n -= i;
464296465Sdelphij            ctx->buf_off += i;
465296465Sdelphij            OPENSSL_assert(ctx->buf_off <= (int)sizeof(ctx->buf));
466296465Sdelphij            OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
467296465Sdelphij        }
468296465Sdelphij        ctx->buf_len = 0;
469296465Sdelphij        ctx->buf_off = 0;
470296465Sdelphij    }
471296465Sdelphij    return (ret);
472296465Sdelphij}
47355714Skris
47468651Skrisstatic long b64_ctrl(BIO *b, int cmd, long num, void *ptr)
475296465Sdelphij{
476296465Sdelphij    BIO_B64_CTX *ctx;
477296465Sdelphij    long ret = 1;
478296465Sdelphij    int i;
47955714Skris
480296465Sdelphij    ctx = (BIO_B64_CTX *)b->ptr;
48155714Skris
482296465Sdelphij    switch (cmd) {
483296465Sdelphij    case BIO_CTRL_RESET:
484296465Sdelphij        ctx->cont = 1;
485296465Sdelphij        ctx->start = 1;
486296465Sdelphij        ctx->encode = B64_NONE;
487296465Sdelphij        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
488296465Sdelphij        break;
489296465Sdelphij    case BIO_CTRL_EOF:         /* More to read */
490296465Sdelphij        if (ctx->cont <= 0)
491296465Sdelphij            ret = 1;
492296465Sdelphij        else
493296465Sdelphij            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
494296465Sdelphij        break;
495296465Sdelphij    case BIO_CTRL_WPENDING:    /* More to write in buffer */
496296465Sdelphij        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
497296465Sdelphij        ret = ctx->buf_len - ctx->buf_off;
498296465Sdelphij        if ((ret == 0) && (ctx->encode != B64_NONE)
499296465Sdelphij            && (ctx->base64.num != 0))
500296465Sdelphij            ret = 1;
501296465Sdelphij        else if (ret <= 0)
502296465Sdelphij            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
503296465Sdelphij        break;
504296465Sdelphij    case BIO_CTRL_PENDING:     /* More to read in buffer */
505296465Sdelphij        OPENSSL_assert(ctx->buf_len >= ctx->buf_off);
506296465Sdelphij        ret = ctx->buf_len - ctx->buf_off;
507296465Sdelphij        if (ret <= 0)
508296465Sdelphij            ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
509296465Sdelphij        break;
510296465Sdelphij    case BIO_CTRL_FLUSH:
511296465Sdelphij        /* do a final write */
512296465Sdelphij again:
513296465Sdelphij        while (ctx->buf_len != ctx->buf_off) {
514296465Sdelphij            i = b64_write(b, NULL, 0);
515296465Sdelphij            if (i < 0)
516296465Sdelphij                return i;
517296465Sdelphij        }
518296465Sdelphij        if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL) {
519296465Sdelphij            if (ctx->tmp_len != 0) {
520296465Sdelphij                ctx->buf_len = EVP_EncodeBlock((unsigned char *)ctx->buf,
521296465Sdelphij                                               (unsigned char *)ctx->tmp,
522296465Sdelphij                                               ctx->tmp_len);
523296465Sdelphij                ctx->buf_off = 0;
524296465Sdelphij                ctx->tmp_len = 0;
525296465Sdelphij                goto again;
526296465Sdelphij            }
527296465Sdelphij        } else if (ctx->encode != B64_NONE && ctx->base64.num != 0) {
528296465Sdelphij            ctx->buf_off = 0;
529296465Sdelphij            EVP_EncodeFinal(&(ctx->base64),
530296465Sdelphij                            (unsigned char *)ctx->buf, &(ctx->buf_len));
531296465Sdelphij            /* push out the bytes */
532296465Sdelphij            goto again;
533296465Sdelphij        }
534296465Sdelphij        /* Finally flush the underlying BIO */
535296465Sdelphij        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
536296465Sdelphij        break;
53755714Skris
538296465Sdelphij    case BIO_C_DO_STATE_MACHINE:
539296465Sdelphij        BIO_clear_retry_flags(b);
540296465Sdelphij        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
541296465Sdelphij        BIO_copy_next_retry(b);
542296465Sdelphij        break;
54355714Skris
544296465Sdelphij    case BIO_CTRL_DUP:
545296465Sdelphij        break;
546296465Sdelphij    case BIO_CTRL_INFO:
547296465Sdelphij    case BIO_CTRL_GET:
548296465Sdelphij    case BIO_CTRL_SET:
549296465Sdelphij    default:
550296465Sdelphij        ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
551296465Sdelphij        break;
552296465Sdelphij    }
553296465Sdelphij    return (ret);
554296465Sdelphij}
55555714Skris
55668651Skrisstatic long b64_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
557296465Sdelphij{
558296465Sdelphij    long ret = 1;
55959191Skris
560296465Sdelphij    if (b->next_bio == NULL)
561296465Sdelphij        return (0);
562296465Sdelphij    switch (cmd) {
563296465Sdelphij    default:
564296465Sdelphij        ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
565296465Sdelphij        break;
566296465Sdelphij    }
567296465Sdelphij    return (ret);
568296465Sdelphij}
56959191Skris
570215697Ssimonstatic int b64_puts(BIO *b, const char *str)
571296465Sdelphij{
572296465Sdelphij    return b64_write(b, str, strlen(str));
573296465Sdelphij}
574