0b51c634ff
* NEW: Support for any mime type and any charset in SmtpClient. git-svn-id: svn://localhost/gambas/trunk@3954 867c0c6c-44f3-4631-809d-bfa615b0a4ec
542 lines
18 KiB
C
542 lines
18 KiB
C
/***************************************************************************
|
|
|
|
parts.c
|
|
|
|
(c) 2000-2011 Benoît Minisini <gambas@users.sourceforge.net>
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA.
|
|
|
|
***************************************************************************/
|
|
/*
|
|
libsmtp is a library to send mail via SMTP
|
|
This is the MIME handling part
|
|
|
|
Copyright 2001 Kevin Read <obsidian@berlios.de>
|
|
|
|
This software is available under the GNU Lesser Public License as described
|
|
in the COPYING file.
|
|
|
|
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 2.1 of the License, or 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, write to the Free
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA
|
|
|
|
Kevin Read <obsidian@berlios.de>
|
|
Thu Aug 16 2001 */
|
|
|
|
/* This will only be included when MIME is enabled */
|
|
|
|
#include "gb_common.h"
|
|
|
|
/* #ifndef __G_LIB_H__ */
|
|
#include <glib.h>
|
|
/* #endif */
|
|
|
|
//#include "../config.h"
|
|
|
|
#include "libsmtp.h"
|
|
#include "libsmtp_mime.h"
|
|
|
|
/* We need some definitions here first. These arrays hold the name tags
|
|
of all MIME settings */
|
|
|
|
char *libsmtp_mime_types[] = {
|
|
"text", "message", "image", "audio", "video", \
|
|
"application", "multipart", "custom!!"};
|
|
|
|
char *libsmtp_mime_subtypes0[] = {
|
|
"plain", "html", "english", "richtext"};
|
|
|
|
char *libsmtp_mime_subtypes1[] = {
|
|
"rfc822", "partial"};
|
|
|
|
char *libsmtp_mime_subtypes2[] = {
|
|
"gif", "jpeg", "png", "tiff", "x-ms-bmp", "x-xbitmap", \
|
|
"x-xpixmap", "x-portable-anymap", "x-portable-bitmap", \
|
|
"x-portable-graymap", "x-portable-pixmap"};
|
|
|
|
char *libsmtp_mime_subtypes3[] = {
|
|
"mpeg", "midi", "x-wav", "x-aiff"};
|
|
|
|
char *libsmtp_mime_subtypes4[] = {
|
|
"mpeg", "x-ms-video", "quicktime", "fli"};
|
|
|
|
char *libsmtp_mime_subtypes5[] = {
|
|
"rtf", "postscript", "pdf", "zip", "x-debian-package", \
|
|
"x-executable", "x-gtar", "x-shellscript", "x-tar", "octet-stream"};
|
|
|
|
char *libsmtp_mime_subtypes6[] = {
|
|
"mixed", "parallel", "digest", "alternative"};
|
|
|
|
char *libsmtp_mime_encodings[] = {
|
|
"7bit", "8bit", "binary", "base64", "quoted-printable"};
|
|
|
|
char *libsmtp_mime_charsets[] = {
|
|
"us-ascii", "iso-8859-1", "iso-8859-2", "iso-8859-3", "iso-8859-15", "utf-8"};
|
|
|
|
bool libsmtp_part_is_type(struct libsmtp_part_struct *part, const char *type)
|
|
{
|
|
if (g_string_is_void(part->Type))
|
|
return FALSE;
|
|
else
|
|
return strcmp(part->Type->str, type) == 0;
|
|
}
|
|
|
|
/* This function creates a new body part, checks for conformance to RFC822
|
|
and RFC 2045 and maybe attaches it to the session. It is taken care in here
|
|
that only multipart and message parts can contain children! Charset is
|
|
ignored unless you set a text or message part */
|
|
|
|
struct libsmtp_part_struct *libsmtp_part_new
|
|
(struct libsmtp_part_struct *libsmtp_int_parent_part, const char *type,
|
|
const char *subtype, int libsmtp_int_encoding, const char *charset,
|
|
char *libsmtp_int_desc, int length, struct libsmtp_session_struct *libsmtp_session)
|
|
{
|
|
struct libsmtp_part_struct *libsmtp_int_part;
|
|
GNode *libsmtp_int_temp_node;
|
|
|
|
/* First we have to check if the session already has a main type */
|
|
|
|
if (libsmtp_session->NumParts)
|
|
{
|
|
/* Yes, there already is a main part. Now lets see if he has passed
|
|
us a non-NULL parent pointer */
|
|
|
|
if (libsmtp_int_parent_part)
|
|
{
|
|
/* Ok, it is non-null. Now the parent part this pointer points to has
|
|
to be some kind of multipart */
|
|
|
|
if (!libsmtp_part_is_type(libsmtp_int_parent_part, "multipart") && !libsmtp_part_is_type(libsmtp_int_parent_part, "message"))
|
|
{
|
|
/* No, it isn't multipart. We can't append new parts to it. */
|
|
libsmtp_session->ErrorCode = LIBSMTP_NOMULTIPART;
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* We already have a parent part but he passed a NULL pointer;
|
|
This won't do!! */
|
|
libsmtp_session->ErrorCode = LIBSMTP_PART_EXISTS;
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This session hasn't got a main type yet. Lets see if he wants to
|
|
define it. */
|
|
if (libsmtp_int_parent_part)
|
|
{
|
|
/* He doesn't want to define the main part!! */
|
|
libsmtp_session->ErrorCode=LIBSMTP_NOPARENT;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Ok. If we got so far the parent argument should be ok. */
|
|
|
|
/* We use calloc here to clear the memory. GLists are initialized when
|
|
they point to NULL, so it must be cleared. */
|
|
|
|
libsmtp_int_part = (struct libsmtp_part_struct *)calloc (1, sizeof(struct libsmtp_part_struct));
|
|
|
|
if (libsmtp_int_part == NULL)
|
|
return NULL;
|
|
|
|
/* The GStrings must be initialized */
|
|
libsmtp_int_part->Description = g_string_new (NULL);
|
|
libsmtp_int_part->Boundary = g_string_new (NULL);
|
|
|
|
libsmtp_int_part->Type = g_string_new(type);
|
|
libsmtp_int_part->Subtype = g_string_new(subtype);
|
|
libsmtp_int_part->Encoding = libsmtp_int_encoding;
|
|
libsmtp_int_part->Description = g_string_new(libsmtp_int_desc);
|
|
libsmtp_int_part->Charset = g_string_new(charset);
|
|
|
|
libsmtp_int_part->length = length;
|
|
|
|
/*if (libsmtp_int_check_part (libsmtp_int_part))
|
|
{
|
|
libsmtp_session->ErrorCode = LIBSMTP_BADARGS;
|
|
return NULL;
|
|
}*/
|
|
|
|
/* We adjust the counters */
|
|
libsmtp_session->NumParts++;
|
|
|
|
if (libsmtp_int_parent_part)
|
|
{
|
|
/* This is a sibling part. We search the N-Tree for the data */
|
|
libsmtp_int_temp_node = g_node_find (libsmtp_session->Parts, \
|
|
G_IN_ORDER, G_TRAVERSE_ALL, libsmtp_int_parent_part);
|
|
g_node_append_data (libsmtp_int_temp_node, libsmtp_int_part);
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_part_new: Me: %s\n", libsmtp_int_part->Description->str);
|
|
#endif
|
|
//libsmtp_int_part=libsmtp_int_temp_node->data;
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_part_new: Parent: %s\n", ((struct libsmtp_part_struct *)libsmtp_int_temp_node->data)->Description->str);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* This is the main part. */
|
|
libsmtp_session->Parts=g_node_new (libsmtp_int_part);
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_part_new: Me: %s\nlibsmtp_part_new: Parent: None\n", libsmtp_int_part->Description->str);
|
|
#endif
|
|
}
|
|
|
|
return libsmtp_int_part;
|
|
}
|
|
|
|
void libsmtp_set_boundary(struct libsmtp_part_struct *part, int index)
|
|
{
|
|
static char pattern[33];
|
|
static char digits[] = "0123456789ABCDEF";
|
|
int i;
|
|
|
|
for(i = 0; i < 32; i++)
|
|
pattern[i] = digits[(random() >> 4) & 0xF];
|
|
|
|
pattern[32] = 0;
|
|
|
|
g_string_sprintf(part->Boundary, "----%s%02d", pattern, index);
|
|
}
|
|
|
|
/* We use this function internally to set the session to the next part to
|
|
send. This function relies on the caller to check that all arguments
|
|
are ok */
|
|
int libsmtp_int_nextpart (struct libsmtp_session_struct *libsmtp_session)
|
|
{
|
|
GNode *libsmtp_temp_now;
|
|
struct libsmtp_part_struct *libsmtp_temp_part;
|
|
GString *libsmtp_temp_gstring = NULL;
|
|
int libsmtp_int_travel = 0;
|
|
|
|
libsmtp_temp_gstring = g_string_new (NULL);
|
|
|
|
/* Are we in a part already? */
|
|
if (!libsmtp_session->PartNowNode)
|
|
{
|
|
/* No, we start with the main part */
|
|
libsmtp_session->PartNowNode = libsmtp_session->Parts;
|
|
libsmtp_session->PartNow = libsmtp_session->PartNowNode->data;
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Starting with main part %s\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
|
|
/* If this is a Multipart part, we must send the standard MIME blurb */
|
|
if (libsmtp_part_is_type(libsmtp_session->PartNow, "multipart"))
|
|
{
|
|
g_string_assign (libsmtp_temp_gstring, \
|
|
"This is a MIME multipart message.\r\n");
|
|
|
|
if (libsmtp_int_send (libsmtp_temp_gstring, libsmtp_session, 2))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Starting with non-main part %s, travelling\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
libsmtp_int_travel=1;
|
|
}
|
|
|
|
/* This is the scanning loop. It scans for the next non-Multipart part */
|
|
for(;;)
|
|
{
|
|
libsmtp_temp_part = (struct libsmtp_part_struct *)libsmtp_session->PartNow;
|
|
|
|
/* Should we travel over this one anyway? */
|
|
|
|
if (libsmtp_int_travel)
|
|
{
|
|
/* Yes, so we'll have to take the next sibling. O Brother were art
|
|
thou? */
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Travelled over %s\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
|
|
libsmtp_int_travel=0;
|
|
if (libsmtp_session->PartNowNode->next)
|
|
{
|
|
/* Yeah, so we take it. */
|
|
libsmtp_session->PartNowNode=libsmtp_session->PartNowNode->next;
|
|
libsmtp_session->PartNow=libsmtp_session->PartNowNode->data;
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Now at sibling %s\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
|
|
/* Jump the gun, Mr. Ed */
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: %s has no more siblings\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
|
|
/* No, so we need the parent */
|
|
if (libsmtp_session->PartNowNode->parent)
|
|
{
|
|
/* Ok, here it is */
|
|
libsmtp_session->PartNowNode = libsmtp_session->PartNowNode->parent;
|
|
libsmtp_temp_part = libsmtp_session->PartNow = libsmtp_session->PartNowNode->data;
|
|
|
|
/* We must send the boundary */
|
|
|
|
g_string_sprintf (libsmtp_temp_gstring, "\r\n--%s--\r\n", \
|
|
libsmtp_temp_part->Boundary->str);
|
|
|
|
if (libsmtp_int_send (libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Now at parent %s\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
/* We will travel over this one, of course. It is already finished */
|
|
libsmtp_int_travel=1;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
/* No more parents here. We are finished. */
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Finished at %s\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
return LIBSMTP_PARTSFINISHED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Ok, we don't need to travel. Is this a multipart part? */
|
|
if (libsmtp_part_is_type(libsmtp_temp_part, "multipart"))
|
|
{
|
|
/* Yes, is the boundary string set? */
|
|
if (libsmtp_temp_part->Boundary->len == 0)
|
|
{
|
|
/* No, we really should set that */
|
|
libsmtp_set_boundary(libsmtp_temp_part, g_node_depth (libsmtp_session->PartNowNode));
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Part %s is Multipart, Setting boundary to '%s'\n", libsmtp_temp_part->Description->str, libsmtp_temp_part->Boundary->str);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
{
|
|
/* No, this is our man! */
|
|
libsmtp_session->PartNow = libsmtp_session->PartNowNode->data;
|
|
|
|
/* Maybe we have to send out a boundary and next part data... */
|
|
if (g_node_depth (libsmtp_session->PartNowNode) > 1)
|
|
{
|
|
libsmtp_temp_now=libsmtp_session->PartNowNode->parent;
|
|
libsmtp_temp_part=libsmtp_temp_now->data;
|
|
|
|
g_string_sprintf (libsmtp_temp_gstring, "\r\n\r\n--%s\r\n", \
|
|
libsmtp_temp_part->Boundary->str);
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_mime_headers: %s", libsmtp_temp_gstring->str);
|
|
#endif
|
|
|
|
if (libsmtp_int_send (libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
|
|
libsmtp_temp_part=libsmtp_session->PartNow;
|
|
|
|
/* We should check for valied MIME settings first */
|
|
/*if ((libsmtp_temp_int=libsmtp_int_check_part (libsmtp_temp_part)))
|
|
{
|
|
libsmtp_session->ErrorCode=libsmtp_temp_int;
|
|
return libsmtp_temp_int;
|
|
}*/
|
|
|
|
/* Then we look up the names of the MIME settings of the main body part
|
|
and send them as headers */
|
|
|
|
g_string_sprintf(libsmtp_temp_gstring, "Content-Type: %s/%s", libsmtp_temp_part->Type->str, libsmtp_temp_part->Subtype->str);
|
|
|
|
if (!g_string_is_void(libsmtp_temp_part->Description))
|
|
{
|
|
g_string_append(libsmtp_temp_gstring, "; name=\"");
|
|
g_string_append(libsmtp_temp_gstring, libsmtp_temp_part->Description->str);
|
|
g_string_append(libsmtp_temp_gstring, "\"");
|
|
}
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_mime_headers: %s. Type: %d/%d\n", libsmtp_temp_gstring->str, \
|
|
libsmtp_temp_part->Type, libsmtp_temp_part->Subtype);
|
|
#endif
|
|
|
|
if (libsmtp_int_send (libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
|
|
/* Multiparts have a boundary */
|
|
|
|
if (libsmtp_part_is_type(libsmtp_temp_part, "multipart"))
|
|
{
|
|
g_string_sprintf (libsmtp_temp_gstring, "; boundary=\"%s\"", libsmtp_temp_part->Boundary->str);
|
|
|
|
if (libsmtp_int_send(libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
}
|
|
|
|
/* Text and message parts will have a charset setting */
|
|
|
|
if (!g_string_is_void(libsmtp_temp_part->Charset))
|
|
{
|
|
g_string_sprintf (libsmtp_temp_gstring, "; charset=\"%s\"", libsmtp_temp_part->Charset->str);
|
|
|
|
if (libsmtp_int_send(libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_mime_headers: %s", libsmtp_temp_gstring->str);
|
|
#endif
|
|
}
|
|
|
|
if (libsmtp_temp_part->length > 0)
|
|
{
|
|
g_string_sprintf (libsmtp_temp_gstring, "\r\nContent-Length: %d", libsmtp_temp_part->length);
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: %s\n", libsmtp_temp_gstring);
|
|
#endif
|
|
|
|
if (libsmtp_int_send (libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
}
|
|
|
|
/* We need a transfer encoding, too */
|
|
|
|
if (!libsmtp_part_is_type(libsmtp_temp_part, "multipart"))
|
|
{
|
|
g_string_sprintf (libsmtp_temp_gstring, "\r\nContent-Transfer-Encoding: %s\r\n", \
|
|
libsmtp_int_lookup_mime_encoding (libsmtp_temp_part));
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_mime_headers: %s\n", libsmtp_temp_gstring);
|
|
#endif
|
|
|
|
if (libsmtp_int_send (libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
}
|
|
|
|
/* Adds a blank line */
|
|
g_string_assign (libsmtp_temp_gstring, "\r\n");
|
|
|
|
if (libsmtp_int_send(libsmtp_temp_gstring, libsmtp_session, 1))
|
|
return LIBSMTP_ERRORSENDFATAL;
|
|
}
|
|
}
|
|
|
|
if (libsmtp_part_is_type(libsmtp_temp_part, "multipart"))
|
|
{
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Part %s is Multipart, so we jump to the first child\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
|
|
/* Set PartNowNode to the first child */
|
|
libsmtp_session->PartNowNode=libsmtp_session->PartNowNode->children;
|
|
libsmtp_session->PartNow=libsmtp_session->PartNowNode->data;
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: Jumped to %s\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
|
|
/* and continue to the next loop */
|
|
continue;
|
|
}
|
|
|
|
#ifdef LIBSMTP_DEBUG
|
|
printf ("libsmtp_int_nextpart: I chose %s\n", libsmtp_session->PartNow->Description->str);
|
|
#endif
|
|
return LIBSMTP_NOERR;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* This function returns the current part. It should be used directly after
|
|
sending the headers to find out which part will be transmitted first. It
|
|
can be used at any time to find out what part is currently being sent, of
|
|
course. */
|
|
|
|
struct libsmtp_part_struct *libsmtp_part_query(struct libsmtp_session_struct *libsmtp_session)
|
|
{
|
|
/* Are we in data stage? */
|
|
if ((libsmtp_session->Stage < LIBSMTP_HEADERS_STAGE) ||
|
|
(libsmtp_session->Stage > LIBSMTP_BODY_STAGE))
|
|
{
|
|
libsmtp_session->ErrorCode = LIBSMTP_BADSTAGE;
|
|
return NULL;
|
|
}
|
|
|
|
/* Check to see if we already are in a part */
|
|
if (!libsmtp_session->PartNow)
|
|
{
|
|
/* We are not at the moment working on one part. Lets see if any parts
|
|
are defined at all! */
|
|
if (!libsmtp_session->Parts)
|
|
{
|
|
/* nope. bad mistake! */
|
|
libsmtp_session->ErrorCode=LIBSMTP_NOPARENT;
|
|
return NULL;
|
|
}
|
|
|
|
/* So we try to lookup the first part that might contain data */
|
|
if (libsmtp_int_nextpart (libsmtp_session))
|
|
return NULL;
|
|
}
|
|
|
|
/* Ok, return the current part */
|
|
return libsmtp_session->PartNow;
|
|
}
|
|
|
|
|
|
/* These functions lookup the name of types, subtypes and encodings for a
|
|
part. They only perform glancing checking of parameters, so you should
|
|
check the mime settings beforehand with libsmtp_int_check_parts */
|
|
|
|
const char *libsmtp_int_lookup_mime_encoding (struct libsmtp_part_struct *libsmtp_int_part)
|
|
{
|
|
if ((libsmtp_int_part->Encoding >= 0) && (libsmtp_int_part->Encoding <= LIBSMTP_MAX_ENC))
|
|
return libsmtp_mime_encodings[libsmtp_int_part->Encoding];
|
|
else
|
|
return NULL;
|
|
}
|
|
|