/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010, 2011
Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
. */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#ifdef HAVE_LIBGEN_H
# include
#endif
#ifdef HAVE_STRINGS_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct _mu_mime_io_buffer
{
unsigned int refcnt;
char *charset;
mu_header_t hdr;
mu_message_t msg;
mu_stream_t stream; /* output file/decoding stream for saving attachment */
mu_stream_t fstream; /* output file stream for saving attachment */
};
int
mu_message_create_attachment (const char *content_type, const char *encoding,
const char *filename, mu_message_t *newmsg)
{
mu_header_t hdr;
mu_body_t body;
mu_stream_t fstream = NULL, tstream = NULL;
char *header = NULL, *name = NULL, *fname = NULL;
int ret;
if (newmsg == NULL)
return MU_ERR_OUT_PTR_NULL;
if (filename == NULL)
return EINVAL;
if ((ret = mu_message_create (newmsg, NULL)) == 0)
{
if (content_type == NULL)
content_type = "text/plain";
if (encoding == NULL)
encoding = "7bit";
if ((fname = strdup (filename)) != NULL)
{
name = strrchr (fname, '/');
if (name)
name++;
else
name = fname;
ret = mu_asprintf (&header,
"Content-Type: %s; name=%s\n"
"Content-Transfer-Encoding: %s\n"
"Content-Disposition: attachment; filename=%s\n\n",
content_type, name, encoding, name);
if (ret)
{
if ((ret = mu_header_create (&hdr, header,
strlen (header))) == 0)
{
mu_message_get_body (*newmsg, &body);
if ((ret = mu_file_stream_create (&fstream, filename,
MU_STREAM_READ)) == 0)
{
if ((ret = mu_filter_create (&tstream, fstream, encoding,
MU_FILTER_ENCODE,
MU_STREAM_READ)) == 0)
{
mu_body_set_stream (body, tstream, *newmsg);
mu_message_set_header (*newmsg, hdr, NULL);
}
}
free (header);
}
}
}
}
if (ret)
{
if (*newmsg)
mu_message_destroy (newmsg, NULL);
if (hdr)
mu_header_destroy (&hdr);
if (fstream)
mu_stream_destroy (&fstream);
if (fname)
free (fname);
}
return ret;
}
int
mu_mime_io_buffer_create (mu_mime_io_buffer_t *pinfo)
{
mu_mime_io_buffer_t info;
if ((info = calloc (1, sizeof (*info))) == NULL)
return ENOMEM;
info->refcnt = 1;
*pinfo = info;
return 0;
}
int
mu_mime_io_buffer_set_charset (mu_mime_io_buffer_t info, const char *charset)
{
char *cp = strdup (charset);
if (!cp)
return ENOMEM;
free (info->charset);
info->charset = cp;
return 0;
}
void
mu_mime_io_buffer_sget_charset (mu_mime_io_buffer_t info, const char **charset)
{
*charset = info->charset;
}
int
mu_mime_io_buffer_aget_charset (mu_mime_io_buffer_t info, const char **charset)
{
*charset = strdup (info->charset);
if (!charset)
return ENOMEM;
return 0;
}
void
mu_mime_io_buffer_destroy (mu_mime_io_buffer_t *pinfo)
{
if (pinfo && *pinfo)
{
mu_mime_io_buffer_t info = *pinfo;
free (info->charset);
free (info);
*pinfo = NULL;
}
}
static void
_attachment_free (struct _mu_mime_io_buffer *info, int free_message)
{
if (free_message)
{
if (info->msg)
mu_message_destroy (&info->msg, NULL);
else if (info->hdr)
mu_header_destroy (&info->hdr);
}
info->msg = NULL;
info->hdr = NULL;
info->stream = NULL;
info->fstream = NULL;
if (--info->refcnt == 0)
{
free (info->charset);
free (info);
}
}
static int
_attachment_setup (mu_mime_io_buffer_t *pinfo, mu_message_t msg,
mu_stream_t *pstream)
{
int ret;
mu_body_t body;
mu_mime_io_buffer_t info;
mu_stream_t stream;
if ((ret = mu_message_get_body (msg, &body)) != 0 ||
(ret = mu_body_get_streamref (body, &stream)) != 0)
return ret;
*pstream = stream;
if (*pinfo)
{
info = *pinfo;
info->refcnt++;
}
else
{
ret = mu_mime_io_buffer_create (&info);
if (ret)
return ret;
}
*pinfo = info;
return 0;
}
int
mu_message_save_attachment (mu_message_t msg, const char *filename,
mu_mime_io_buffer_t info)
{
mu_stream_t istream;
int ret;
mu_header_t hdr;
const char *fname = NULL;
char *partname = NULL;
if (msg == NULL)
return EINVAL;
if ((ret = _attachment_setup (&info, msg, &istream)) != 0)
return ret;
if (ret == 0 && (ret = mu_message_get_header (msg, &hdr)) == 0)
{
if (filename == NULL)
{
ret = mu_message_aget_decoded_attachment_name (msg, info->charset,
&partname, NULL);
if (partname)
fname = partname;
}
else
fname = filename;
if (fname
&& (ret = mu_file_stream_create (&info->fstream, fname,
MU_STREAM_WRITE | MU_STREAM_CREAT)) == 0)
{
const char *content_encoding;
if (mu_header_sget_value (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING,
&content_encoding))
content_encoding = "7bit";
ret = mu_filter_create (&info->stream, istream, content_encoding,
MU_FILTER_DECODE,
MU_STREAM_READ);
}
}
if (info->stream && istream)
ret = mu_stream_copy (info->fstream, info->stream, 0, NULL);
if (ret != EAGAIN && info)
{
mu_stream_close (info->fstream);
mu_stream_destroy (&info->stream);
mu_stream_destroy (&info->fstream);
}
mu_stream_destroy (&istream);
_attachment_free (info, ret); /* FIXME: or 0? */
/* Free fname if we allocated it. */
if (partname)
free (partname);
return ret;
}
int
mu_message_encapsulate (mu_message_t msg, mu_message_t *newmsg,
mu_mime_io_buffer_t info)
{
mu_stream_t istream, ostream;
int ret = 0;
mu_message_t tmsg = NULL;
if (newmsg == NULL)
return MU_ERR_OUT_PTR_NULL;
if (msg == NULL)
{
mu_header_t hdr;
ret = mu_message_create (&tmsg, NULL);
if (ret)
return ret;
msg = tmsg;
#define MSG822_HEADER "Content-Type: message/rfc822\n" \
"Content-Transfer-Encoding: 7bit\n\n"
if ((ret = mu_header_create (&hdr,
MSG822_HEADER,
sizeof (MSG822_HEADER) - 1)) == 0)
ret = mu_message_set_header (msg, hdr, NULL);
#undef MSG822_HEADER
if (ret)
{
mu_message_destroy (&msg, NULL);
return ret;
}
}
if ((ret = _attachment_setup (&info, msg, &ostream)) != 0)
{
mu_message_destroy (&tmsg, NULL);
return ret;
}
info->msg = msg;
if (ret == 0 && (ret = mu_message_get_streamref (msg, &istream)) == 0)
{
mu_stream_seek (istream, 0, MU_SEEK_SET, NULL);
ret = mu_stream_copy (ostream, istream, 0, NULL);
mu_stream_destroy (&istream);
}
if (ret == 0)
*newmsg = info->msg;
mu_stream_destroy (&ostream);
_attachment_free (info, ret && ret != EAGAIN);
return ret;
}
#define MESSAGE_RFC822_STR "message/rfc822"
int
mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg,
mu_mime_io_buffer_t info)
{
int ret = 0;
mu_header_t hdr;
mu_stream_t istream, ostream;
if (msg == NULL)
return EINVAL;
if (newmsg == NULL)
return MU_ERR_OUT_PTR_NULL;
if (info == NULL /* FIXME: not needed? */
&& (ret = mu_message_get_header (msg, &hdr)) == 0)
{
const char *s;
if (!(mu_header_sget_value (hdr, MU_HEADER_CONTENT_TYPE, &s) == 0 &&
mu_c_strncasecmp (s, MESSAGE_RFC822_STR,
sizeof (MESSAGE_RFC822_STR) - 1) == 0))
return EINVAL;
}
if ((ret = _attachment_setup (&info, msg, &istream)) != 0)
return ret;
if (info->msg == NULL)
ret = mu_message_create (&info->msg, NULL);
if (ret == 0)
{
mu_message_get_streamref (info->msg, &ostream);
mu_stream_seek (ostream, 0, MU_SEEK_SET, NULL);
ret = mu_stream_copy (ostream, istream, 0, NULL);
mu_stream_destroy (&ostream);
}
if (ret == 0)
*newmsg = info->msg;
mu_stream_destroy (&istream);
_attachment_free (info, ret && ret != EAGAIN);
return ret;
}