libpurple/protocols/sametime/im_mime.c

Fri, 29 Mar 2019 20:12:10 -0400

author
Elliott Sales de Andrade <qulogic@pidgin.im>
date
Fri, 29 Mar 2019 20:12:10 -0400
changeset 39524
b49e731857a2
parent 39523
415b0705e48b
child 39526
4f678f514b69
permissions
-rw-r--r--

sametime: Use GLib random generator.

/*
 * purple - Sametime Protocol Plugin
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * 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 of the License, 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  02111-1301  USA
 *
 */

#include "internal.h"

#include <glib.h>

/* purple includes */
#include "image-store.h"
#include "mime.h"

/* plugin includes */
#include "sametime.h"
#include "im_mime.h"


/** generate "cid:908@20582notesbuddy" from "<908@20582notesbuddy>" */
static char *
make_cid(const char *cid)
{
	gsize n;
	char *c, *d;

	g_return_val_if_fail(cid != NULL, NULL);

	n = strlen(cid);
	g_return_val_if_fail(n > 2, NULL);

	c = g_strndup(cid+1, n-2);
	d = g_strdup_printf("cid:%s", c);

	g_free(c);
	return d;
}


gchar *
im_mime_parse(const char *data)
{
	GHashTable *img_by_cid;

	GString *str;

	PurpleMimeDocument *doc;
	GList *parts;

	img_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
			g_object_unref);

	/* don't want the contained string to ever be NULL */
	str = g_string_new("");

	doc = purple_mime_document_parse(data);

	/* handle all the MIME parts */
	parts = purple_mime_document_get_parts(doc);
	for (; parts; parts = parts->next) {
		PurpleMimePart *part = parts->data;
		const char *type;

		type = purple_mime_part_get_field(part, "content-type");
		purple_debug_info("sametime", "MIME part Content-Type: %s\n",
				type);

		if (!type) {
			; /* feh */

		} else if (purple_str_has_prefix(type, "image")) {
			/* put images into the image store */

			guchar *d_dat;
			gsize d_len;
			char *cid;
			PurpleImage *image;

			/* obtain and unencode the data */
			purple_mime_part_get_data_decoded(part, &d_dat, &d_len);

			/* look up the content id */
			cid = (char *)purple_mime_part_get_field(part,
					"Content-ID");
			cid = make_cid(cid);

			/* add image to the purple image store */
			image = purple_image_new_from_data(d_dat, d_len);
			purple_image_set_friendly_filename(image, cid);

			/* map the cid to the image store identifier */
			g_hash_table_insert(img_by_cid, cid, image);

		} else if (purple_str_has_prefix(type, "text")) {

			/* concatenate all the text parts together */
			guchar *data;
			gsize len;

			purple_mime_part_get_data_decoded(part, &data, &len);
			g_string_append(str, (const char *)data);
			g_free(data);
		}
	}

	purple_mime_document_free(doc);

	/* @todo should put this in its own function */
	{ /* replace each IMG tag's SRC attribute with an ID attribute. This
	     actually modifies the contents of str */
		GData *attribs;
		char *start, *end;
		char *tmp = str->str;

		while (*tmp &&
			purple_markup_find_tag("img", tmp,
				(const char **) &start, (const char **) &end,
				&attribs)) {

			char *alt, *align, *border, *src;
			int img = 0;

			alt = g_datalist_get_data(&attribs, "alt");
			align = g_datalist_get_data(&attribs, "align");
			border = g_datalist_get_data(&attribs, "border");
			src = g_datalist_get_data(&attribs, "src");

			if (src) {
				img = GPOINTER_TO_INT(g_hash_table_lookup(img_by_cid, src));
			}

			if (img) {
				GString *atstr;
				gsize len = (end - start);
				gsize mov;

				atstr = g_string_new("");
				if (alt) {
					g_string_append_printf(atstr,
							" alt=\"%s\"", alt);
				}
				if (align) {
					g_string_append_printf(atstr,
							" align=\"%s\"", align);
				}
				if (border) {
					g_string_append_printf(atstr,
							" border=\"%s\"", border);
				}

				mov = g_snprintf(start, len,
						"<img src=\"" PURPLE_IMAGE_STORE_PROTOCOL
						"%u\"%s", img, atstr->str);
				while (mov < len) start[mov++] = ' ';

				g_string_free(atstr, TRUE);
			}

			g_datalist_clear(&attribs);
			tmp = end + 1;
		}
	}

	/* clean up the cid table */
	g_hash_table_destroy(img_by_cid);

	return g_string_free(str, FALSE);
}


/** generates a random-ish content id string */
static char *
im_mime_content_id(void)
{
	return g_strdup_printf("%03x@%05xmeanwhile",
			g_random_int() & 0xfff, g_random_int() & 0xfffff);
}


/** generates a multipart/related content type with a random-ish
    boundary value */
static char *
im_mime_content_type(void)
{
	return g_strdup_printf(
			"multipart/related; boundary=related_MW%03x_%04x",
			g_random_int() & 0xfff, g_random_int() & 0xffff);
}

/** determine content type from contents */
static gchar *
im_mime_img_content_type(PurpleImage *img)
{
	const gchar *mimetype;

	mimetype = purple_image_get_mimetype(img);
	if (!mimetype) {
		mimetype = "image";
	}

	return g_strdup_printf("%s; name=\"%s\"", mimetype,
			purple_image_get_friendly_filename(img));
}


static char *
im_mime_img_content_disp(PurpleImage *img)
{
	return g_strdup_printf("attachment; filename=\"%s\"",
		purple_image_get_friendly_filename(img));
}


gchar *
im_mime_generate(const char *message)
{
	GString *str;
	PurpleMimeDocument *doc;
	PurpleMimePart *part;

	GData *attr;
	char *tmp, *start, *end;

	str = g_string_new(NULL);

	doc = purple_mime_document_new();

	purple_mime_document_set_field(doc, "Mime-Version", "1.0");
	purple_mime_document_set_field(doc, "Content-Disposition", "inline");

	tmp = im_mime_content_type();
	purple_mime_document_set_field(doc, "Content-Type", tmp);
	g_free(tmp);

	tmp = (char *)message;
	while (*tmp &&
		purple_markup_find_tag("img", tmp, (const char **) &start,
			(const char **) &end, &attr)) {
		gchar *uri;
		PurpleImage *img = NULL;

		gsize len = (start - tmp);

		/* append the in-between-tags text */
		if (len) {
			g_string_append_len(str, tmp, len);
		}

		uri = g_datalist_get_data(&attr, "src");
		if (uri) {
			img = purple_image_store_get_from_uri(uri);
		}

		if (img) {
			char *cid;
			gpointer data;
			gsize size;

			part = purple_mime_part_new(doc);

			data = im_mime_img_content_disp(img);
			purple_mime_part_set_field(part, "Content-Disposition",
					data);
			g_free(data);

			data = im_mime_img_content_type(img);
			purple_mime_part_set_field(part, "Content-Type", data);
			g_free(data);

			cid = im_mime_content_id();
			data = g_strdup_printf("<%s>", cid);
			purple_mime_part_set_field(part, "Content-ID", data);
			g_free(data);

			purple_mime_part_set_field(part,
					"Content-transfer-encoding", "base64");

			/* obtain and base64 encode the image data, and put it
			   in the mime part */
			size = purple_image_get_data_size(img);
			data = g_base64_encode(purple_image_get_data(img), size);
			purple_mime_part_set_data(part, data);
			g_free(data);

			/* append the modified tag */
			g_string_append_printf(str, "<img src=\"cid:%s\">", cid);
			g_free(cid);

		} else {
			/* append the literal image tag, since we couldn't find
			   a relative PurpleImage object */
			gsize len = (end - start) + 1;
			g_string_append_len(str, start, len);
		}

		g_datalist_clear(&attr);
		tmp = end + 1;
	}

	/* append left-overs */
	g_string_append(str, tmp);

	/* add the text/html part */
	part = purple_mime_part_new(doc);
	purple_mime_part_set_field(part, "Content-Disposition", "inline");

	tmp = purple_utf8_ncr_encode(str->str);
	purple_mime_part_set_field(part, "Content-Type", "text/html");
	purple_mime_part_set_field(part, "Content-Transfer-Encoding", "7bit");
	purple_mime_part_set_data(part, tmp);
	g_free(tmp);

	g_string_free(str, TRUE);

	str = g_string_new(NULL);
	purple_mime_document_write(doc, str);
	return g_string_free(str, FALSE);
}

mercurial