#include #include #include #include "base64-internal.h" typedef struct priv PRIV; struct priv { union { int (*rcb)(void *, void *, int); int (*wcb)(void *, const void *, int); } ; void *cbcookie; int (*rcf)(PRIV *, void *, int); int (*wcf)(PRIV *, const void *, int); void (*ccf)(PRIV *); unsigned char isave[4]; int nisave; unsigned char osave[4]; int nosave; unsigned int flags; #define PF_ERR 0x00000001 #define PF_EOF 0x00000002 } ; static int e_r_cf(PRIV *p, void *buf, int len) { unsigned char dbuf[8192]; unsigned char *bp; int left; int dbfill; int want; int nr; int maxwant; int ix; if (p->flags & PF_ERR) return(-1); if (len < 0) return(-1); if (len < 1) return(0); bp = buf; left = len; if (p->nosave > 0) { if (left < p->nosave) { bcopy(&p->osave[0],bp,left); bcopy(&p->osave[left],&p->osave[0],p->nosave-left); p->nosave -= left; return(left); } else { bcopy(&p->osave[0],bp,p->nosave); left -= p->nosave; bp += p->nosave; p->nosave = 0; if (left < 1) return(len); } } if (p->flags & PF_EOF) return(len-left); if (p->nisave > 0) { bcopy(&p->isave[0],&dbuf[0],p->nisave); dbfill = p->nisave; } else { dbfill = 0; } while (left > 0) { maxwant = ((left + 3) / 4) * 3; want = sizeof(dbuf); if (want > maxwant) want = maxwant; want -= dbfill; if (want < 1) abort(); /* can't happen: dbfill is 0..2, want before subtract is >=3 */ nr = (*p->rcb)(p->cbcookie,&dbuf[dbfill],want); if (nr < 0) { p->flags |= PF_ERR; return(-1); } if (nr == 0) { p->flags |= PF_EOF; if (dbfill > 2) abort(); /* can't happen: dbfill is 0..2 */ if (dbfill) { if (left >= 4) { base64__encode_trail(&dbuf[0],dbfill,bp); left -= 4; } else { base64__encode_trail(&dbuf[0],dbfill,&p->osave[0]); if (left > 0) { bcopy(&p->osave[0],bp,left); bcopy(&p->osave[left],&p->osave[0],4-left); p->nosave = 4 - left; left = 0; } } } return(len-left); } dbfill += nr; ix = 0; while ((dbfill-ix >= 3) && (left >= 4)) { base64__encode(&dbuf[ix],bp); bp += 4; ix += 3; left -= 4; } if (left < 1) { if (dbfill-ix >= 3) abort(); /* can't happen: maxwant cap prevents it */ p->nisave = dbfill - ix; if (p->nisave > 0) bcopy(&dbuf[ix],&p->isave[0],p->nisave); return(len); } if (ix && (ix < dbfill)) bcopy(&dbuf[ix],&dbuf[0],dbfill-ix); dbfill -= ix; if (dbfill > 3) abort(); /* can't happen: maxwant cap prevents it */ if (dbfill == 3) { base64__encode(&dbuf[0],&p->osave[0]); dbfill = 0; if (left >= 4) abort(); /* can't happen: while would have looped */ if (left < 1) abort(); /* above block would have tripped */ bcopy(&p->osave[0],bp,left); p->nosave = 4 - left; bcopy(&p->osave[left],&p->osave[0],p->nosave); return(len); } } return(len); } static int d_r_cf(PRIV *p, void *buf, int len) { unsigned char dbuf[8192]; unsigned char *bp; int left; int dbfill; int want; int nr; int maxwant; int ix; if (p->flags & PF_ERR) return(-1); if (len < 0) return(-1); if (len < 1) return(0); bp = buf; left = len; if (p->nosave > 0) { if (left < p->nosave) { bcopy(&p->osave[0],bp,left); bcopy(&p->osave[left],&p->osave[0],p->nosave-left); p->nosave -= left; return(left); } else { bcopy(&p->osave[0],bp,p->nosave); left -= p->nosave; bp += p->nosave; p->nosave = 0; if (left < 1) return(len); } } if (p->flags & PF_EOF) return(len-left); if (p->nisave > 0) { bcopy(&p->isave[0],&dbuf[0],p->nisave); dbfill = p->nisave; } else { dbfill = 0; } while (left > 0) { maxwant = ((left + 2) / 3) * 4; want = sizeof(dbuf); if (want > maxwant) want = maxwant; want -= dbfill; if (want < 1) abort(); /* can't happen: dbfill is 0..3, want before subtract is >=4 */ nr = (*p->rcb)(p->cbcookie,&dbuf[dbfill],want); if (nr < 0) { p->flags |= PF_ERR; return(-1); } if (nr == 0) { p->flags |= PF_EOF; if (dbfill > 3) abort(); /* can't happen: dbfill is 0..3 */ if (dbfill > 0) { p->flags |= PF_ERR; return(-1); } return(len-left); } dbfill += nr; ix = 0; while ((dbfill-ix >= 4) && (left >= 3)) { switch (base64__decode(&dbuf[ix],bp)) { default: abort(); /* can't happen: base64__decode violated interface */ break; case B64D_NORMAL: break; case B64D_FINAL_1: p->flags |= PF_EOF; return(len-(left-1)); break; case B64D_FINAL_2: p->flags |= PF_EOF; return(len-(left-2)); break; case B64D_ERROR: p->flags |= PF_ERR; return(-1); break; } bp += 3; ix += 4; left -= 3; } if (left < 1) { if (dbfill-ix >= 4) abort(); /* can't happen: maxwant cap prevents it */ p->nisave = dbfill - ix; if (p->nisave > 0) bcopy(&dbuf[ix],&p->isave[0],p->nisave); return(len); } if (ix && (ix < dbfill)) bcopy(&dbuf[ix],&dbuf[0],dbfill-ix); dbfill -= ix; if (dbfill > 4) abort(); /* can't happen: maxwant cap prevents it */ if (dbfill == 4) { switch (base64__decode(&dbuf[ix],&p->osave[0])) { default: abort(); /* can't happen: base64__decode violated interface */ break; case B64D_NORMAL: p->nosave = 3; break; case B64D_FINAL_1: p->flags |= PF_EOF; p->nosave = 1; break; case B64D_FINAL_2: p->flags |= PF_EOF; p->nosave = 2; break; case B64D_ERROR: p->flags |= PF_ERR; return(-1); break; } dbfill = 0; if (left >= 3) abort(); /* can't happen: while would have looped */ if (left < 1) abort(); /* above block would have tripped */ if (left >= p->nosave) { bcopy(&p->osave[0],bp,p->nosave); left -= p->nosave; p->nosave = 0; return(len-left); } else { bcopy(&p->osave[0],bp,left); p->nosave -= left; bcopy(&p->osave[left],&p->osave[0],p->nosave); return(len); } } } return(len); } static int e_w_cf(PRIV *p, const void *data, int len) { const unsigned char *ibp; int ileft; char obuf[8190]; int oleft; char *obp; int n; if (p->flags & PF_ERR) return(-1); if (len < 1) return(-1); obp = &obuf[0]; oleft = sizeof(obuf); ibp = data; ileft = len; if (p->nisave+ileft < 3) { bcopy(ibp,&p->isave[p->nisave],ileft); p->nisave += ileft; return(len); } if (p->nisave) { n = 3 - p->nisave; if (ileft < n) abort(); /* can't happen: test above would have fired */ bcopy(ibp,&p->isave[p->nisave],n); ibp += n; ileft -= n; base64__encode(&p->isave[0],obp); p->nisave = 0; obp += 4; oleft -= 4; } while (ileft >= 3) { if (oleft < 4) { n = (*p->wcb)(p->cbcookie,&obuf[0],obp-&obuf[0]); if (n < 0) { p->flags |= PF_ERR; return(-1); } obp = &obuf[0]; oleft = sizeof(obuf); } base64__encode(ibp,obp); ibp += 3; ileft -= 3; obp += 4; oleft -= 4; } if (obp > &obuf[0]) { n = (*p->wcb)(p->cbcookie,&obuf[0],obp-&obuf[0]); if (n < 0) { p->flags |= PF_ERR; return(-1); } } if (ileft > 0) bcopy(ibp,&p->isave[0],ileft); p->nisave = ileft; return(len); } static void e_c_cf(PRIV *p) { char o[4]; if (p->flags & PF_ERR) return; if (p->nisave) { base64__encode_trail(&p->isave[0],p->nisave,&o[0]); (*p->wcb)(p->cbcookie,&o[0],4); } } static int d_w_cf(PRIV *p, const void *data, int len) { const unsigned char *ibp; int ileft; char obuf[8190]; int oleft; char *obp; int n; if (p->flags & (PF_ERR|PF_EOF)) return(-1); if (len < 1) return(-1); obp = &obuf[0]; oleft = sizeof(obuf); ibp = data; ileft = len; if (p->nisave+ileft < 4) { bcopy(ibp,&p->isave[p->nisave],ileft); p->nisave += ileft; return(len); } if (p->nisave) { n = 4 - p->nisave; if (ileft < n) abort(); /* can't happen: test above would have fired */ bcopy(ibp,&p->isave[p->nisave],n); ibp += n; ileft -= n; switch (base64__decode(&p->isave[0],obp)) { default: abort(); /* can't happen: base64__decode violated interface */ break; case B64D_NORMAL: n = 3; break; case B64D_FINAL_1: p->flags |= PF_EOF; n = 1; break; case B64D_FINAL_2: p->flags |= PF_EOF; n = 2; break; case B64D_ERROR: p->flags |= PF_ERR; return(-1); break; } p->nisave = 0; obp += n; oleft -= n; } while (ileft >= 4) { if (oleft < 4) { n = (*p->wcb)(p->cbcookie,&obuf[0],obp-&obuf[0]); if (n < 0) { p->flags |= PF_ERR; return(-1); } obp = &obuf[0]; oleft = sizeof(obuf); } if (p->flags & PF_EOF) { p->flags |= PF_ERR; return(-1); } switch (base64__decode(ibp,obp)) { default: abort(); /* can't happen: base64__decode violated interface */ break; case B64D_NORMAL: n = 3; break; case B64D_FINAL_1: p->flags |= PF_EOF; n = 1; break; case B64D_FINAL_2: p->flags |= PF_EOF; n = 2; break; case B64D_ERROR: p->flags |= PF_ERR; return(-1); break; } ibp += 4; ileft -= 4; obp += n; oleft -= n; } if (obp > &obuf[0]) { n = (*p->wcb)(p->cbcookie,&obuf[0],obp-&obuf[0]); if (n < 0) { p->flags |= PF_ERR; return(-1); } } if (ileft > 0) { if (p->flags & PF_EOF) { p->flags |= PF_ERR; return(-1); } bcopy(ibp,&p->isave[0],ileft); } p->nisave = ileft; return(len); } static void d_c_cf(PRIV *p) { char o[4]; if (p->flags & (PF_ERR|PF_EOF)) return; if (p->nisave) { base64__encode_trail(&p->isave[0],p->nisave,&o[0]); (*p->wcb)(p->cbcookie,&o[0],4); } } static int err_r_cf( PRIV *p __attribute__((__unused__)), void *buf __attribute__((__unused__)), int len __attribute__((__unused__)) ) { return(-1); } static int err_w_cf( PRIV *p __attribute__((__unused__)), const void *buf __attribute__((__unused__)), int len __attribute__((__unused__)) ) { return(-1); } static void nil_c_cf(PRIV *p __attribute__((__unused__))) { } static PRIV *newpriv(void) { PRIV *p; p = malloc(sizeof(PRIV)); p->nisave = 0; p->nosave = 0; p->flags = 0; return(p); } void *base64_e_r_cb(void *cookie, int (*cb)(void *, void *, int)) { PRIV *p; p = newpriv(); p->rcb = cb; p->cbcookie = cookie; p->rcf = &e_r_cf; p->wcf = &err_w_cf; p->ccf = &nil_c_cf; return(p); } void *base64_e_w_cb(void *cookie, int (*cb)(void *, const void *, int)) { PRIV *p; p = newpriv(); p->wcb = cb; p->cbcookie = cookie; p->rcf = &err_r_cf; p->wcf = &e_w_cf; p->ccf = &e_c_cf; return(p); } void *base64_d_r_cb(void *cookie, int (*cb)(void *, void *, int)) { PRIV *p; p = newpriv(); p->rcb = cb; p->cbcookie = cookie; p->rcf = &d_r_cf; p->wcf = &err_w_cf; p->ccf = &nil_c_cf; return(p); } void *base64_d_w_cb(void *cookie, int (*cb)(void *, const void *, int)) { PRIV *p; p = newpriv(); p->wcb = cb; p->cbcookie = cookie; p->rcf = &err_r_cf; p->wcf = &d_w_cf; p->ccf = &d_c_cf; return(p); } int base64_r(void *pv, void *buf, int len) { return((*((PRIV *)pv)->rcf)(pv,buf,len)); } int base64_w(void *pv, const void *buf, int len) { return((*((PRIV *)pv)->wcf)(pv,buf,len)); } void base64_close(void *pv) { (*((PRIV *)pv)->ccf)(pv); free(pv); }