From 6d81a82a76208cc31a74c248921d23348fb8be40 Mon Sep 17 00:00:00 2001
From: randomdan <randomdan@localhost>
Date: Tue, 3 Jan 2012 23:10:47 +0000
Subject: Allowing specification of output encoding

---
 project2/cgi/cgiAppEngine.cpp            | 22 ++++++-----
 project2/cgi/cgiAppEngine.h              |  3 +-
 project2/cgi/cgiEnvironment.cpp          |  2 +
 project2/cgi/cgiEnvironment.h            |  1 +
 project2/cgi/cgiStageCustomError.cpp     |  2 +-
 project2/cgi/cgiStageCustomNotFound.cpp  |  2 +-
 project2/cgi/cgiStageDefaultError.cpp    |  2 +-
 project2/cgi/cgiStageDefaultNotFound.cpp |  2 +-
 project2/cgi/cgiStageFail.cpp            |  2 +-
 project2/cgi/cgiStagePresent.cpp         |  7 ++--
 project2/cgi/cgiStageRedirect.cpp        |  2 +-
 project2/cgi/cgiStageRequest.cpp         |  2 +-
 project2/common/scripts.cpp              | 13 +++++++
 project2/common/scripts.h                |  1 +
 project2/common/transform.h              |  2 +-
 project2/json/couchSession.cpp           |  4 +-
 project2/json/json.h                     | 16 ++++----
 project2/json/presenter.cpp              |  6 +--
 project2/json/serialize.cpp              | 46 ++++++++++++----------
 project2/json/transformStream.cpp        | 15 +++++--
 project2/mail/sendmailTask.cpp           | 67 +++++++++++++-------------------
 project2/mail/sendmailTask.h             |  2 +
 project2/xml/transformHtml.cpp           |  6 +--
 project2/xml/transformHtml.h             |  2 +-
 project2/xml/transformText.cpp           | 18 +++++----
 project2/xml/transformText.h             |  4 +-
 project2/xml/xmlPresenter.cpp            |  4 +-
 project2/xml/xmlPresenter.h              |  2 +-
 28 files changed, 141 insertions(+), 116 deletions(-)

diff --git a/project2/cgi/cgiAppEngine.cpp b/project2/cgi/cgiAppEngine.cpp
index 8093ef3..b92095c 100644
--- a/project2/cgi/cgiAppEngine.cpp
+++ b/project2/cgi/cgiAppEngine.cpp
@@ -45,20 +45,22 @@ CgiApplicationEngine::env() const
 
 class CgiResult : public TransformChainLink {
 	public:
-		CgiResult(CgiApplicationEngine::HttpHeaderPtr & h, std::ostream & s) :
+		CgiResult(CgiApplicationEngine::HttpHeaderPtr & h, std::ostream & s, const std::string & e) :
 			header(h),
-			stream(s) {
+			stream(s),
+			encoding(e) {
 		}
 		CgiApplicationEngine::HttpHeaderPtr header;
 		std::ostream & stream;
+		const std::string encoding;
 };
 
 class WriteToCgiResult : public TransformImpl<WritableContent, CgiResult> {
 	public:
 		void transform(const WritableContent * wc, CgiResult * cr) const {
-			cr->header->addHeader("Content-Type", wc->getContentType());
+			cr->header->addHeader("Content-Type", Glib::ustring::compose("%1; charset=%2", wc->getContentType(), cr->encoding));
 			cr->header->render(cr->stream);
-			wc->writeTo(cr->stream);
+			wc->writeTo(cr->stream, cr->encoding);
 		}
 };
 DECLARE_TRANSFORM(WriteToCgiResult);
@@ -115,9 +117,9 @@ CgiApplicationEngine::process() const
 	} while (currentStage.get<0>());
 	endTime = boost::date_time::microsec_clock<boost::posix_time::ptime>::universal_time();
 	ResponseStagePtr rs = currentStage.get<1>();
-	if (currentStage.get<3>()) {
-		addAppData(currentStage.get<3>().get(), rs->outputOptions);
-		addEnvData(currentStage.get<3>().get(), rs->outputOptions);
+	if (const MultiRowSetPresenter * p = currentStage.get<3>().get()) {
+		addAppData(p, rs->outputOptions);
+		addEnvData(p, rs->outputOptions);
 	}
 	HttpHeaderPtr header = rs->getHeader();
 	if (!sessionEmpty || !cursession->Empty()) {
@@ -125,9 +127,9 @@ CgiApplicationEngine::process() const
 		header->setCookie(cgicc::HTTPCookie(SESSIONID, cursession->ID.str(), "Session ID",
 					_env->getServerName().substr(_env->getServerName().find(".")), env()->sessionTimeOut, "/", false));
 	}
-	if (currentStage.get<2>()) {
-		TransformSourcePtr ts = currentStage.get<2>();
-		addFinalTransformTarget(ts, new CgiResult(header, IO));
+	if (TransformSourcePtr ts = currentStage.get<2>()) {
+		addFinalTransformTarget(ts, new CgiResult(header, IO,
+					rs && rs->root ? rs->root->value("encoding", _env->outputEncoding) : VariableType(_env->outputEncoding)));
 		std::fstream * ddd = NULL;
 		if (!_env->dumpdatadoc.empty()) {
 			ddd = new std::fstream(_env->dumpdatadoc.c_str(), std::fstream::trunc | std::fstream::out);
diff --git a/project2/cgi/cgiAppEngine.h b/project2/cgi/cgiAppEngine.h
index 80dad6e..a7648d1 100644
--- a/project2/cgi/cgiAppEngine.h
+++ b/project2/cgi/cgiAppEngine.h
@@ -67,10 +67,11 @@ class CgiApplicationEngine : public ApplicationEngine, public TransformChainLink
 		/// Base class for a stage that can be a response to the client
 		class ResponseStage : public Stage {
 			public:
-				ResponseStage(const CgiEnvironment * e);
+				ResponseStage(const CgiEnvironment * e, ScriptNodePtr root);
 				virtual HttpHeaderPtr getHeader() const = 0;
 
 				OutputOptionsPtr outputOptions;
+				ScriptNodePtr root;
 		};
 
 		/// Stage implementation used to bootstrap the iteration process based on the CGI environment
diff --git a/project2/cgi/cgiEnvironment.cpp b/project2/cgi/cgiEnvironment.cpp
index 7b2b254..b1565ad 100644
--- a/project2/cgi/cgiEnvironment.cpp
+++ b/project2/cgi/cgiEnvironment.cpp
@@ -55,6 +55,8 @@ CgiEnvironment::CgiEnvironment(cgicc::Cgicc * c) :
 		 "The module with which to implement session management")
 		("cgi.hostRegex", hpi,
 		 "Regular expression used to define a hostname -> platform association")
+		("cgi.outputEncoding", Options::value(&outputEncoding, "utf-8"),
+		 "The encoding to use when outputing to the web server")
 		;
 }
 
diff --git a/project2/cgi/cgiEnvironment.h b/project2/cgi/cgiEnvironment.h
index 28e47d3..25ea90f 100644
--- a/project2/cgi/cgiEnvironment.h
+++ b/project2/cgi/cgiEnvironment.h
@@ -53,6 +53,7 @@ class CgiEnvironment : public Environment, public cgicc::CgiEnvironment {
 		std::string onErrorPresent;
 		std::string	defaultPresenter;
 		std::string	sessionModule;
+		std::string	outputEncoding;
 };
 
 #endif
diff --git a/project2/cgi/cgiStageCustomError.cpp b/project2/cgi/cgiStageCustomError.cpp
index eb66ff0..957b572 100644
--- a/project2/cgi/cgiStageCustomError.cpp
+++ b/project2/cgi/cgiStageCustomError.cpp
@@ -5,7 +5,7 @@
 #include "logger.h"
 
 CgiApplicationEngine::CustomErrorStage::CustomErrorStage(const CgiEnvironment * env, const std::exception & ex, ScriptReaderPtr s) :
-	CgiApplicationEngine::ResponseStage(env),
+	CgiApplicationEngine::ResponseStage(env, s->root()),
 	::CommonObjects(s->root()),
 	::CheckHost(s->root()),
 	CgiApplicationEngine::DefaultErrorStage(env, ex),
diff --git a/project2/cgi/cgiStageCustomNotFound.cpp b/project2/cgi/cgiStageCustomNotFound.cpp
index e6520dd..3ce8012 100644
--- a/project2/cgi/cgiStageCustomNotFound.cpp
+++ b/project2/cgi/cgiStageCustomNotFound.cpp
@@ -5,7 +5,7 @@
 #include "logger.h"
 
 CgiApplicationEngine::CustomNotFoundStage::CustomNotFoundStage(const CgiEnvironment * env, const ScriptNotFound & notfound, ScriptReaderPtr s) :
-	CgiApplicationEngine::ResponseStage(env),
+	CgiApplicationEngine::ResponseStage(env, s->root()),
 	::CommonObjects(s->root()),
 	::CheckHost(s->root()),
 	CgiApplicationEngine::DefaultNotFoundStage(env, notfound),
diff --git a/project2/cgi/cgiStageDefaultError.cpp b/project2/cgi/cgiStageDefaultError.cpp
index e464981..d2abeef 100644
--- a/project2/cgi/cgiStageDefaultError.cpp
+++ b/project2/cgi/cgiStageDefaultError.cpp
@@ -8,7 +8,7 @@
 static const Glib::ustring DefaultErrorStageResp("error");
 
 CgiApplicationEngine::DefaultErrorStage::DefaultErrorStage(const CgiEnvironment * env, const std::exception & ex) :
-	CgiApplicationEngine::ResponseStage(env),
+	CgiApplicationEngine::ResponseStage(env, NULL),
 	buf(__cxxabiv1::__cxa_demangle(typeid(ex).name(), NULL, NULL, NULL)),
 	what(ex.what()),
 	pres(new XmlPresenter(DefaultErrorStageResp, e->errorTransformStyle, e->errorContentType))
diff --git a/project2/cgi/cgiStageDefaultNotFound.cpp b/project2/cgi/cgiStageDefaultNotFound.cpp
index 2cf0475..b971dc3 100644
--- a/project2/cgi/cgiStageDefaultNotFound.cpp
+++ b/project2/cgi/cgiStageDefaultNotFound.cpp
@@ -7,7 +7,7 @@
 static const Glib::ustring DefaultNotFoundStageResp("notfound");
 
 CgiApplicationEngine::DefaultNotFoundStage::DefaultNotFoundStage(const CgiEnvironment * env, const ScriptNotFound & notfound) :
-	CgiApplicationEngine::ResponseStage(env),
+	CgiApplicationEngine::ResponseStage(env, NULL),
 	nf(notfound),
 	pres(new XmlPresenter(DefaultNotFoundStageResp, e->errorTransformStyle, e->errorContentType))
 {
diff --git a/project2/cgi/cgiStageFail.cpp b/project2/cgi/cgiStageFail.cpp
index 62de295..64bc15a 100644
--- a/project2/cgi/cgiStageFail.cpp
+++ b/project2/cgi/cgiStageFail.cpp
@@ -9,7 +9,7 @@ namespace CgiApplicationExtras {
 	class FailStage : public CgiApplicationEngine::ResponseStage {
 		public:
 			FailStage(const CgiEnvironment * env, int c, const std::string & m) :
-				ResponseStage(env),
+				ResponseStage(env, NULL),
 				code(c),
 				message(m) {
 			}
diff --git a/project2/cgi/cgiStagePresent.cpp b/project2/cgi/cgiStagePresent.cpp
index 1330cdc..2ca6147 100644
--- a/project2/cgi/cgiStagePresent.cpp
+++ b/project2/cgi/cgiStagePresent.cpp
@@ -6,7 +6,7 @@
 #include <boost/bind.hpp>
 
 CgiApplicationEngine::PresentStage::PresentStage(const CgiEnvironment * e, ScriptReaderPtr s) :
-	CgiApplicationEngine::ResponseStage(e),
+	CgiApplicationEngine::ResponseStage(e, s->root()),
 	CommonObjects(s->root()),
 	CheckHost(s->root()),
 	ViewHost(s->root()),
@@ -41,8 +41,9 @@ CgiApplicationEngine::PresentStage::getPresenter() const
 	return presenter;
 }
 
-CgiApplicationEngine::ResponseStage::ResponseStage(const CgiEnvironment * e) :
-	CgiApplicationEngine::Stage(e)
+CgiApplicationEngine::ResponseStage::ResponseStage(const CgiEnvironment * e, ScriptNodePtr r) :
+	CgiApplicationEngine::Stage(e),
+	root(r)
 {
 }
 
diff --git a/project2/cgi/cgiStageRedirect.cpp b/project2/cgi/cgiStageRedirect.cpp
index 8c918f1..5459e08 100644
--- a/project2/cgi/cgiStageRedirect.cpp
+++ b/project2/cgi/cgiStageRedirect.cpp
@@ -8,7 +8,7 @@ namespace CgiApplicationExtras {
 	class RedirectStage : public CgiApplicationEngine::ResponseStage {
 		public:
 			RedirectStage(const CgiEnvironment * env, const std::string & u) :
-				ResponseStage(env),
+				ResponseStage(env, NULL),
 				url(u) {
 			}
 
diff --git a/project2/cgi/cgiStageRequest.cpp b/project2/cgi/cgiStageRequest.cpp
index d505f3c..136627f 100644
--- a/project2/cgi/cgiStageRequest.cpp
+++ b/project2/cgi/cgiStageRequest.cpp
@@ -9,7 +9,7 @@ CgiApplicationEngine::RequestStage::RequestStage(const CgiEnvironment * e, Scrip
 	SourceObject(s->root()),
 	CommonObjects(s->root()),
 	::CheckHost(s->root()),
-	CgiApplicationEngine::ResponseStage(e),
+	CgiApplicationEngine::ResponseStage(e, s->root()),
 	::TaskHost(s->root()),
 	present(s->root()->value("present","").as<std::string>())
 {
diff --git a/project2/common/scripts.cpp b/project2/common/scripts.cpp
index 4d15cfe..65a1ed0 100644
--- a/project2/common/scripts.cpp
+++ b/project2/common/scripts.cpp
@@ -27,3 +27,16 @@ ScriptNode::value(const Glib::ustring & n, const VariableType & def) const
 		return def;
 	}
 }
+
+bool
+ScriptNode::applyValue(const Glib::ustring & n, VariableType & target) const
+{
+	try {
+		target = value(n);
+		return true;
+	}
+	catch (const ValueNotFound &) {
+		return false;
+	}
+}
+
diff --git a/project2/common/scripts.h b/project2/common/scripts.h
index becfda2..3469d4a 100644
--- a/project2/common/scripts.h
+++ b/project2/common/scripts.h
@@ -38,6 +38,7 @@ class ScriptNode : public IntrusivePtrBase {
 		virtual ScriptNodes childrenIn(const Glib::ustring & sub) const = 0;
 		
 		virtual bool valueExists(const Glib::ustring & name) const = 0;
+		bool applyValue(const Glib::ustring & name, VariableType & target) const;
 		virtual VariableImpl * variable(const boost::optional<Glib::ustring> & defaultSource = boost::optional<Glib::ustring>()) const = 0;
 		virtual VariableImpl * variable(const Glib::ustring & name) const = 0;
 		VariableImpl * variable(const Glib::ustring & name, const VariableType & def) const;
diff --git a/project2/common/transform.h b/project2/common/transform.h
index 175bf42..4e80476 100644
--- a/project2/common/transform.h
+++ b/project2/common/transform.h
@@ -89,7 +89,7 @@ class TransformImpl : public Transform {
 class WritableContent {
 	public:
 		virtual Glib::ustring getContentType() const = 0;
-		virtual void writeTo(std::ostream &) const = 0;
+		virtual void writeTo(std::ostream &, const std::string & encoding) const = 0;
 };
 
 #endif
diff --git a/project2/json/couchSession.cpp b/project2/json/couchSession.cpp
index a328ad5..f810486 100644
--- a/project2/json/couchSession.cpp
+++ b/project2/json/couchSession.cpp
@@ -42,7 +42,7 @@ class CouchSessionContainer : public SessionContainer {
 			json::Object obj;
 			s->ForeachValue(boost::bind(&CouchSessionContainer::addToObject, &obj, _1, _2));
 			obj[ExpiryKey] = json::ValuePtr(new json::Value((json::Number)s->ExpiryTime()));
-			Glib::ustring out = json::serializeObject(obj);
+			Glib::ustring out = json::serializeObject(obj, "utf-8");
 			c->setopt(CURLOPT_INFILESIZE_LARGE, (curl_off_t)out.size());
 			unsigned int off = 0;
 			BOOST_FOREACH(const std::string & b, baseUrls) {
@@ -147,7 +147,7 @@ class CustomCouchSessionLoader : public SessionContainerLoaderImpl<CouchSessionC
 			mapBuf.appendf("function(doc) { var exp = doc['%s']; if (exp < %u) { emit(exp, doc._rev); } }",
 					CouchSessionContainer::ExpiryKey.c_str(), (unsigned int)time(NULL));
 			map["map"] = json::ValuePtr(new json::Value(mapBuf.str()));
-			Glib::ustring mapStr(json::serializeObject(map));
+			Glib::ustring mapStr(json::serializeObject(map, "utf-8"));
 			// Create the CURL handle
 			CurlPtr c = new Curl();
 			c->setopt(CURLOPT_FAILONERROR, 1);
diff --git a/project2/json/json.h b/project2/json/json.h
index 7339ce4..23eeac2 100644
--- a/project2/json/json.h
+++ b/project2/json/json.h
@@ -29,14 +29,14 @@ namespace json {
 	Value parseValue(Glib::ustring::const_iterator & s);
 	Array parseArray(Glib::ustring::const_iterator &);
 
-	void serializeObject(const Object &, std::ostream & s);
-	void serializeValue(const Value &, std::ostream & s);
-	void serializeArray(const Array &, std::ostream & s);
-	void serializeString(const String &, std::ostream & s);
-	void serializeNumber(const Number &, std::ostream & s);
-	void serializeBoolean(const Boolean &, std::ostream & s);
-	void serializeNull(const Null &, std::ostream & s);
-	Glib::ustring serializeObject(const Object &);
+	void serializeObject(const Object &, std::ostream & s, const std::string & encoding);
+	void serializeValue(const Value &, std::ostream & s, const std::string & encoding);
+	void serializeArray(const Array &, std::ostream & s, const std::string & encoding);
+	void serializeString(const String &, std::ostream & s, const std::string & encoding);
+	void serializeNumber(const Number &, std::ostream & s, const std::string & encoding);
+	void serializeBoolean(const Boolean &, std::ostream & s, const std::string & encoding);
+	void serializeNull(const Null &, std::ostream & s, const std::string & encoding);
+	Glib::ustring serializeObject(const Object &, const std::string & encoding);
 }
 
 #endif
diff --git a/project2/json/presenter.cpp b/project2/json/presenter.cpp
index aaa72bf..6500ea1 100644
--- a/project2/json/presenter.cpp
+++ b/project2/json/presenter.cpp
@@ -9,7 +9,7 @@ class JsonPresenter : public MultiRowSetPresenter, public ContentPresenter, publ
 	public:
 		JsonPresenter(ScriptNodePtr s) :
 			TransformSource(s),
-			ContentPresenter("application/json; charset=UTF-8"),
+			ContentPresenter("application/json"),
 			SourceOf<json::Object>(s),
 			SourceOf<WritableContent>(s) {
 			curRowSet.push(&object);
@@ -53,8 +53,8 @@ class JsonPresenter : public MultiRowSetPresenter, public ContentPresenter, publ
 		Glib::ustring getContentType() const {
 			return contentType;
 		}
-		void writeTo(std::ostream & o) const {
-			serializeObject(object, o);
+		void writeTo(std::ostream & o, const std::string & encoding) const {
+			serializeObject(object, o, encoding);
 		}
 
 	private:
diff --git a/project2/json/serialize.cpp b/project2/json/serialize.cpp
index ff2d778..a1ba1f6 100644
--- a/project2/json/serialize.cpp
+++ b/project2/json/serialize.cpp
@@ -2,35 +2,39 @@
 #include "json.h"
 #include <stdio.h>
 #include <boost/foreach.hpp>
+#include <glibmm/convert.h>
 
 namespace json {
 	class JsonSerialize : public boost::static_visitor<> {
 		public:
-			JsonSerialize(std::ostream & out) : o(out) {
+			JsonSerialize(std::ostream & out, const std::string & encoding) :
+				o(out),
+				e(encoding) {
 			}
 			void operator()(const String & s) const {
-				serializeString(s, o);
+				serializeString(s, o, e);
 			}
 			void operator()(const Number & s) const {
-				serializeNumber(s, o);
+				serializeNumber(s, o, e);
 			}
 			void operator()(const Array & s) const {
-				serializeArray(s, o);
+				serializeArray(s, o, e);
 			}
 			void operator()(const Object & s) const {
-				serializeObject(s, o);
+				serializeObject(s, o, e);
 			}
 			void operator()(const Null & s) const {
-				serializeNull(s, o);
+				serializeNull(s, o, e);
 			}
 			void operator()(const Boolean & s) const {
-				serializeBoolean(s, o);
+				serializeBoolean(s, o, e);
 			}
 		private:
 			std::ostream & o;
+			std::string e;
 	};
 
-	void serializeObject(const Object & o, std::ostream & s) {
+	void serializeObject(const Object & o, std::ostream & s, const std::string & enc) {
 		s << std::boolalpha;
 		s << std::fixed;
 		s << '{';
@@ -38,29 +42,30 @@ namespace json {
 			if (&v != &*o.begin()) {
 				s << ',';
 			}
-			serializeString(v.first, s);
+			serializeString(v.first, s, enc);
 			s << ':';
-			serializeValue(*v.second, s);
+			serializeValue(*v.second, s, enc);
 		}
 		s << '}';
 	}
 
-	void serializeValue(const Value & v, std::ostream & s) {
-		boost::apply_visitor(JsonSerialize(s), v);
+	void serializeValue(const Value & v, std::ostream & s, const std::string & enc) {
+		boost::apply_visitor(JsonSerialize(s, enc), v);
 	}
 
-	void serializeArray(const Array & a, std::ostream & s) {
+	void serializeArray(const Array & a, std::ostream & s, const std::string & enc) {
 		s << '[';
 		BOOST_FOREACH(const Array::value_type & v, a) {
 			if (&v != &*a.begin()) {
 				s << ',';
 			}
-			serializeValue(*v, s);
+			serializeValue(*v, s, enc);
 		}
 		s << ']';
 	}
 
-	void serializeString(const String & str, std::ostream & s) {
+	void serializeString(const String & str, std::ostream & o, const std::string & encoding) {
+		std::stringstream s;
 		s << '"';
 		BOOST_FOREACH(gunichar c, str) {
 			if (c < 32) {
@@ -105,23 +110,24 @@ namespace json {
 			}
 		}
 		s << '"';
+		o << Glib::convert(s.str(), encoding, "utf-8");
 	}
 
-	void serializeNumber(const Number & n, std::ostream & s) {
+	void serializeNumber(const Number & n, std::ostream & s, const std::string & ) {
 		s << n;
 	}
 
-	void serializeBoolean(const Boolean & b, std::ostream & s) {
+	void serializeBoolean(const Boolean & b, std::ostream & s, const std::string & ) {
 		s << b;
 	}
 
-	void serializeNull(const Null &, std::ostream & s) {
+	void serializeNull(const Null &, std::ostream & s, const std::string & ) {
 		s << "null";
 	}
 
-	Glib::ustring serializeObject(const Object & o) {
+	Glib::ustring serializeObject(const Object & o, const std::string & enc) {
 		std::stringstream out;
-		serializeObject(o, out);
+		serializeObject(o, out, enc);
 		return out.str();
 	}
 }
diff --git a/project2/json/transformStream.cpp b/project2/json/transformStream.cpp
index 25f5d17..fae71b0 100644
--- a/project2/json/transformStream.cpp
+++ b/project2/json/transformStream.cpp
@@ -4,13 +4,22 @@
 #include "json.h"
 #include "ostreamWrapper.h"
 
-class TransformJsonToHttpStream : public TransformImpl<json::Object, ostreamWrapper> {
+class TransformJsonToStream : public TransformImpl<json::Object, ostreamWrapper> {
 	public:
+		TransformJsonToStream() :
+			encoding("utf-8") {
+		}
 		void transform(const json::Object * obj, ostreamWrapper * o) const
 		{
-			json::serializeObject(*obj, o->strm);
+			json::serializeObject(*obj, o->strm, encoding);
+		}
+		void configure(ScriptNodePtr s)
+		{
+			s->applyValue("encoding", encoding);
 		}
+	private:
+		VariableType encoding;
 };
-DECLARE_TRANSFORM(TransformJsonToHttpStream);
+DECLARE_TRANSFORM(TransformJsonToStream);
 
 
diff --git a/project2/mail/sendmailTask.cpp b/project2/mail/sendmailTask.cpp
index 2fc77fa..d3ee53a 100644
--- a/project2/mail/sendmailTask.cpp
+++ b/project2/mail/sendmailTask.cpp
@@ -12,6 +12,7 @@
 #include "transformText.h"
 
 std::string SendMailTask::defaultMailServer;
+std::string SendMailTask::defaultMailEncoding;
 
 class CustomSendMailTaskLoader : public ElementLoaderImpl<SendMailTask> {
 	public:
@@ -21,6 +22,8 @@ class CustomSendMailTaskLoader : public ElementLoaderImpl<SendMailTask> {
 			opts
 				("sendmail.defaultServer", Options::value(&SendMailTask::defaultMailServer),
 				 "The address of the default mail relay server")
+				("sendmail.defaultEncoding", Options::value(&SendMailTask::defaultMailEncoding, "utf-8"),
+				 "The default encoding to use in email content")
 				;
 		}
 		const Options *
@@ -84,16 +87,18 @@ SendMailTask::SortMailParts::operator()(const MailPartPtr & a, const MailPartPtr
 
 class BoundaryBegin : public SendMailTask::MailPart {
 	public:
-		BoundaryBegin(const std::string & ct, uint8_t m) :
+		BoundaryBegin(const std::string & ct, const std::string & enc, uint8_t m) :
 			MailPart(m, MailPart::mimeIdx, 0),
-			contentType(ct) {
+			contentType(ct),
+			encoding(enc) {
 		}
 		const char * write(char ** buf, int * len) {
-			*len = asprintf(buf, "\r\n--<<divider>>\r\nContent-Type: %s\r\n\r\n", contentType.c_str());
+			*len = asprintf(buf, "\r\n--<<divider>>\r\nContent-Type: %s; %s\r\n\r\n", contentType.c_str(), encoding.c_str());
 			return *buf;
 		}
 	private:
 		const std::string contentType;
+		const std::string encoding;
 };
 
 class BoundaryEnd : public SendMailTask::MailPart {
@@ -133,60 +138,40 @@ class Header : public SendMailTask::MailPart {
 
 class MimeContent : public SendMailTask::MailPart {
 	public:
-		MimeContent(const char * s, int l, uint8_t m) :
+		MimeContent(const std::string & s, uint8_t m) :
 			MailPart(m, MailPart::mimeIdx, 1),
-			str(s),
-			length(l) {
+			str(s) {
 		}
 		const char * write(char ** buf, int * len) {
 			*buf = NULL;
-			*len = length;
-			return str;
+			*len = str.length();
+			return str.c_str();
 		}
 	private:
-		const char * str;
-		const int length;
+		const std::string str;
 };
 
-class TransformHtmlToEmail : public TransformImpl<HtmlDocument, SendMailTask::Parts> {
+class TransformWritableContentToEmail : public TransformImpl<WritableContent, SendMailTask::Parts> {
 	public:
-		TransformHtmlToEmail() : buf(NULL)
-		{
-		}
-		~TransformHtmlToEmail()
-		{
-			if (buf) {
-				xmlFree(buf);
-			}
+		TransformWritableContentToEmail() :
+			encoding(SendMailTask::defaultMailEncoding) {
 		}
-		void transform(const HtmlDocument * cdoc,  SendMailTask::Parts * parts) const
+		void transform(const WritableContent * wc, SendMailTask::Parts * parts) const
 		{
-			if (buf) {
-				xmlFree(buf);
-				buf = NULL;
-			}
-			xmlDoc * doc = const_cast<xmlDoc *>(cdoc->doc);
-			int len = 0;
-			xmlDocDumpFormatMemoryEnc(doc, &buf, &len, "utf-8", 1);
-			parts->parts.insert(new BoundaryBegin("text/html; utf-8", 2));
-			parts->parts.insert(new MimeContent(reinterpret_cast<const char *>(buf), len, 2));
+			parts->parts.insert(new BoundaryBegin(wc->getContentType(), encoding, 1));
+			std::stringstream str;
+			wc->writeTo(str, encoding);
+			parts->parts.insert(new MimeContent(str.str(), 1));
 			SendMailTask::MailPart::mimeIdx += 1;
 		}
-	private:
-		mutable xmlChar * buf;
-};
-DECLARE_TRANSFORM(TransformHtmlToEmail);
-
-class TransformTextToEmail : public TransformImpl<TextDocument, SendMailTask::Parts> {
-	public:
-		void transform(const TextDocument * str, SendMailTask::Parts * parts) const
+		void configure(ScriptNodePtr s)
 		{
-			parts->parts.insert(new BoundaryBegin("text/plain; utf-8", 1));
-			parts->parts.insert(new MimeContent(str->doc.c_str(), str->doc.length(), 1));
-			SendMailTask::MailPart::mimeIdx += 1;
+			s->applyValue("encoding", encoding);
 		}
+	private:
+		VariableType encoding;
 };
-DECLARE_TRANSFORM(TransformTextToEmail);
+DECLARE_TRANSFORM(TransformWritableContentToEmail);
 
 class EmailViewHost : public ViewHost {
 	public:
diff --git a/project2/mail/sendmailTask.h b/project2/mail/sendmailTask.h
index 9cf4922..3703203 100644
--- a/project2/mail/sendmailTask.h
+++ b/project2/mail/sendmailTask.h
@@ -51,7 +51,9 @@ class SendMailTask : public Task {
 
 		// Configurables
 		friend class CustomSendMailTaskLoader;
+		friend class TransformWritableContentToEmail;
 		static std::string defaultMailServer;
+		static std::string defaultMailEncoding;
 };
 
 #endif
diff --git a/project2/xml/transformHtml.cpp b/project2/xml/transformHtml.cpp
index 54919ef..8cc9202 100644
--- a/project2/xml/transformHtml.cpp
+++ b/project2/xml/transformHtml.cpp
@@ -8,7 +8,7 @@ HtmlDocument::HtmlDocument(ScriptNodePtr s) :
 	TransformSource(s),
 	SourceOf<HtmlDocument>(s),
 	SourceOf<WritableContent>(s),
-	contentType(s, "contenttype", "text/html; charset=utf-8")
+	contentType(s, "contenttype", "text/html")
 {
 }
 
@@ -27,10 +27,10 @@ int	xmlstrmwritecallback(void * context, const char * buffer, int len)
 }
 
 void
-HtmlDocument::writeTo(std::ostream & o) const
+HtmlDocument::writeTo(std::ostream & o, const std::string & encoding) const
 {
 	xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(xmlstrmwritecallback, xmlstrmclosecallback, &o, NULL);
-	htmlDocContentDumpFormatOutput(buf, doc, "utf-8", 0);
+	htmlDocContentDumpFormatOutput(buf, doc, encoding.c_str(), 0);
 	xmlOutputBufferClose(buf);
 }
 
diff --git a/project2/xml/transformHtml.h b/project2/xml/transformHtml.h
index 8a34f2f..3ea10fd 100644
--- a/project2/xml/transformHtml.h
+++ b/project2/xml/transformHtml.h
@@ -11,7 +11,7 @@ class HtmlDocument : public SourceOf<HtmlDocument>, public WritableContent, publ
 		operator const HtmlDocument * () const;
 		operator const WritableContent * () const;
 		Glib::ustring getContentType() const;
-		void writeTo(std::ostream &) const;
+		void writeTo(std::ostream &, const std::string & encoding) const;
 		Variable contentType;
 };
 
diff --git a/project2/xml/transformText.cpp b/project2/xml/transformText.cpp
index 6bc43a7..b9b5e56 100644
--- a/project2/xml/transformText.cpp
+++ b/project2/xml/transformText.cpp
@@ -4,12 +4,13 @@
 #include "transformHtml.h"
 #include <sys/wait.h>
 #include <misc.h>
+#include <glibmm/convert.h>
 
 TextDocument::TextDocument(ScriptNodePtr s) :
 	TransformSource(s),
 	SourceOf<TextDocument>(s),
 	SourceOf<WritableContent>(s),
-	contentType(s, "contenttype", "text/plain; charset=utf-8")
+	contentType(s, "contenttype", "text/plain")
 {
 }
 
@@ -17,9 +18,9 @@ TextDocument::operator const TextDocument * () const { return this; }
 TextDocument::operator const WritableContent * () const { return this; }
 
 void
-TextDocument::writeTo(std::ostream & o) const
+TextDocument::writeTo(std::ostream & o, const std::string & encoding) const
 {
-	o << doc;
+	o << Glib::convert(doc.raw(), encoding, "utf-8");
 }
 
 Glib::ustring
@@ -43,14 +44,15 @@ class TransformHtmlToText : public TransformImpl<HtmlDocument, TextDocument> {
 				"/usr/bin/lynx", "-dump", "-stdin", "-width=105", "-nonumbers", "-nolist", NULL };
 			popenrw(callLynx, fds);
 			FILE * lynxIn = fdopen(fds[0], "w");
-			FILE * lynxOut = fdopen(fds[1], "r");
-			htmlNodeDumpFile(lynxIn, doc, xmlDocGetRootElement(doc));
+			// Fixed encoding as we want the result to go back into a ustring
+			htmlNodeDumpFileFormat(lynxIn, doc, xmlDocGetRootElement(doc), "utf-8", 0);
 			fclose(lynxIn);
 			close(fds[0]);
-			for (int ch ; ((ch = fgetc(lynxOut)) >= 0); ) {
-				str->doc.push_back(ch);
+			char buf[1024];
+			int r;
+			while ((r = read(fds[1], buf, sizeof(buf))) > 0) {
+				str->doc.append(std::string(buf, r));
 			}
-			fclose(lynxOut);
 			close(fds[1]);
 			int status;
 			wait(&status);
diff --git a/project2/xml/transformText.h b/project2/xml/transformText.h
index 07eb583..7fd1e8f 100644
--- a/project2/xml/transformText.h
+++ b/project2/xml/transformText.h
@@ -7,11 +7,11 @@
 class TextDocument : public SourceOf<TextDocument>, public WritableContent, public SourceOf<WritableContent> {
 	public:
 		TextDocument(ScriptNodePtr);
-		std::string doc;
+		Glib::ustring doc;
 		operator const TextDocument * () const;
 		operator const WritableContent * () const;
 		Glib::ustring getContentType() const;
-		void writeTo(std::ostream &) const;
+		void writeTo(std::ostream &, const std::string & encoding) const;
 		Variable contentType;
 };
 
diff --git a/project2/xml/xmlPresenter.cpp b/project2/xml/xmlPresenter.cpp
index e9d9a18..91d43aa 100644
--- a/project2/xml/xmlPresenter.cpp
+++ b/project2/xml/xmlPresenter.cpp
@@ -152,6 +152,6 @@ XmlPresenter::getContentType() const {
 }
 
 void
-XmlPresenter::writeTo(std::ostream & o) const {
-	responseDoc->write_to_stream_formatted(o, "UTF-8");
+XmlPresenter::writeTo(std::ostream & o, const std::string & enc) const {
+	responseDoc->write_to_stream_formatted(o, enc);
 }
diff --git a/project2/xml/xmlPresenter.h b/project2/xml/xmlPresenter.h
index 0671548..eacc793 100644
--- a/project2/xml/xmlPresenter.h
+++ b/project2/xml/xmlPresenter.h
@@ -29,7 +29,7 @@ class XmlPresenter : public Presenter, public ContentPresenter, public SourceOf<
 		operator const boost::shared_ptr<xmlpp::Document> * () const;
 		operator const WritableContent * () const;
 		Glib::ustring getContentType() const;
-		void writeTo(std::ostream &) const;
+		void writeTo(std::ostream &, const std::string & enc) const;
 
 	private:
 		void createDoc(const Glib::ustring & responseRootNodeName, const Glib::ustring & responseStyle) const;
-- 
cgit v1.2.3