/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2003 Free Software Foundation, Inc. GNU Mailutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Mailutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Mailutils; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #if HAVE_CONFIG_H # include #endif #ifdef WITH_GSASL #include #include #include #include #include #include #include #include struct _gsasl_stream { Gsasl_session_ctx *sess_ctx; /* Context */ int last_err; /* Last Gsasl error code */ stream_t stream; /* Underlying stream */ size_t offset; /* Current offset */ char *buffer; /* Line buffer */ size_t size; /* Allocated size */ size_t level; /* Current filling level */ }; static void _gsasl_destroy (stream_t stream) { struct _gsasl_stream *s = stream_get_owner (stream); stream_destroy (&s->stream, stream_get_owner (s->stream)); free (s->buffer); s->buffer = NULL; } #define buffer_drop(s) s->level = 0 int buffer_grow (struct _gsasl_stream *s, char *ptr, size_t size) { if (!s->buffer) { s->buffer = malloc (size); s->size = size; s->level = 0; } else if (s->size - s->level < size) { size_t newsize = s->size + size; s->buffer = realloc (s->buffer, newsize); if (s->buffer) s->size = newsize; } if (!s->buffer) return ENOMEM; memcpy (s->buffer + s->level, ptr, size); s->level += size; return 0; } static int _gsasl_readline (stream_t stream, char *optr, size_t osize, off_t offset, size_t *nbytes) { struct _gsasl_stream *s = stream_get_owner (stream); int rc; size_t len, sz; char *bufp; if (s->level) { len = s->level > osize ? osize : s->level; memcpy (optr, s->buffer, len); if (s->level > len) { memmove (s->buffer, s->buffer + len, s->level - len); s->level -= len; } if (nbytes) *nbytes = len; return 0; } do { char buf[80]; size_t sz; rc = stream_readline (s->stream, buf, sizeof (buf), s->offset, &sz); if (rc) return rc; s->offset += sz; rc = buffer_grow (s, buf, sz); if (rc) return rc; rc = gsasl_decode (s->sess_ctx, s->buffer, s->level, NULL, &len); } while (rc == GSASL_NEEDS_MORE); if (rc != GSASL_OK) { s->last_err = rc; return EIO; } bufp = malloc (len + 1); if (!bufp) return ENOMEM; rc = gsasl_decode (s->sess_ctx, s->buffer, s->level, bufp, &len); if (rc != GSASL_OK) { s->last_err = rc; return EIO; } bufp[len++] = '\0'; sz = len > osize ? osize : len; if (len > osize) { memcpy (optr, bufp, osize); buffer_drop (s); buffer_grow (s, bufp + osize, len - osize); len = osize; } else memcpy (optr, bufp, len); if (nbytes) *nbytes = len; free (bufp); return 0; } static int _gsasl_write (stream_t stream, const char *iptr, size_t isize, off_t offset, size_t *nbytes) { int rc; struct _gsasl_stream *s = stream_get_owner (stream); rc = buffer_grow (s, iptr, isize); if (rc) return rc; if (s->level >= 2 && s->buffer[s->level - 2] == '\r' && s->buffer[s->level - 1] == '\n') { size_t len, wrsize; char *buf = NULL; gsasl_encode (s->sess_ctx, s->buffer, s->level, NULL, &len); buf = malloc (len); if (!buf) return ENOMEM; gsasl_encode (s->sess_ctx, s->buffer, s->level, buf, &len); rc = stream_write (s->stream, buf, len, s->offset, &wrsize); free (buf); if (rc) return rc; s->offset += wrsize; if (nbytes) *nbytes = wrsize; s->level = 0; } return 0; } static int _gsasl_flush (stream_t stream) { struct _gsasl_stream *s = stream_get_owner (stream); stream_flush (s->stream); return 0; } static int _gsasl_close (stream_t stream) { struct _gsasl_stream *s = stream_get_owner (stream); stream_close (s->stream); if (s->sess_ctx) gsasl_server_finish (s->sess_ctx); return 0; } static int _gsasl_open (stream_t stream) { struct _gsasl_stream *s = stream_get_owner (stream); return 0; } int _gsasl_strerror (stream_t stream, const char **pstr) { struct _gsasl_stream *s = stream_get_owner (stream); *pstr = gsasl_strerror (s->last_err); return 0; } int gsasl_stream_create (stream_t *stream, stream_t ins, Gsasl_session_ctx *ctx, int flags) { struct _gsasl_stream *s; if (stream == NULL) return EINVAL; if ((flags & ~(MU_STREAM_READ|MU_STREAM_WRITE)) || (flags & (MU_STREAM_READ|MU_STREAM_WRITE)) == (MU_STREAM_READ|MU_STREAM_WRITE)) return EINVAL; s = calloc (1, sizeof (*s)); if (s == NULL) return ENOMEM; s->stream = ins; s->sess_ctx = ctx; stream_set_open (*stream, _gsasl_open, s); stream_set_close (*stream, _gsasl_close, s); stream_set_flush (*stream, _gsasl_flush, s); stream_set_destroy (*stream, _gsasl_destroy, s); stream_set_strerror (*stream, _gsasl_strerror, s); if (flags & MU_STREAM_READ) stream_set_readline (*stream, _gsasl_readline, s); else stream_set_write (*stream, _gsasl_write, s); return 0; } #endif