diff options
| -rw-r--r-- | project2/Jamfile.jam | 6 | ||||
| -rw-r--r-- | project2/appEngine.h | 4 | ||||
| -rw-r--r-- | project2/cgiAppEngine.cpp | 54 | ||||
| -rw-r--r-- | project2/cgiAppEngine.h | 10 | ||||
| -rw-r--r-- | project2/cgiEnvironment.h | 2 | ||||
| -rw-r--r-- | project2/environment.h | 7 | ||||
| -rw-r--r-- | project2/presenter.cpp | 38 | ||||
| -rw-r--r-- | project2/presenter.h | 27 | ||||
| -rw-r--r-- | project2/rdbmsDataSource.cpp | 15 | ||||
| -rw-r--r-- | project2/rdbmsDataSource.h | 1 | ||||
| -rw-r--r-- | project2/sendmailTask.cpp | 107 | ||||
| -rw-r--r-- | project2/sendmailTask.h | 42 | ||||
| -rw-r--r-- | project2/sqlIterate.cpp | 1 | ||||
| -rw-r--r-- | project2/sqlIterate.h | 1 | ||||
| -rw-r--r-- | project2/task.cpp | 3 | ||||
| -rw-r--r-- | project2/xmlObjectLoader.h | 2 | 
16 files changed, 278 insertions, 42 deletions
| diff --git a/project2/Jamfile.jam b/project2/Jamfile.jam index 4fdd54c..771a473 100644 --- a/project2/Jamfile.jam +++ b/project2/Jamfile.jam @@ -3,15 +3,20 @@ using gcc ;  alias libxmlpp : : : :  	<cflags>"`pkg-config --cflags libxml++-2.6`"  	<linkflags>"`pkg-config --libs libxml++-2.6`" ; +alias libxslt : : : : +	<cflags>"`pkg-config --cflags libexslt`" +	<linkflags>"`pkg-config --libs libexslt`" ;  lib fcgi : : <name>fcgi ;  lib fcgi++ : : <name>fcgi++ ;  lib odbc : : <name>odbc ;  lib boost_regex : : <name>boost_regex ;  lib cgicc : : <name>cgicc ; +lib esmtp : : <name>esmtp ;  exe p2web :  	libxmlpp +	libxslt  	[ glob *.cpp ]  	../libmisc  	../libodbcpp : @@ -19,6 +24,7 @@ exe p2web :  	<include>../libodbcpp/  	<library>boost_regex  	<library>odbc +	<library>esmtp  	<library>cgicc  	<library>fcgi++  	<library>fcgi ; diff --git a/project2/appEngine.h b/project2/appEngine.h index 59e0d4d..7f37cfd 100644 --- a/project2/appEngine.h +++ b/project2/appEngine.h @@ -4,6 +4,7 @@  #include "environment.h"  #include "dataSource.h"  #include "session.h" +#include "presenter.h"  class ApplicationEngine {  	public: @@ -16,6 +17,7 @@ class ApplicationEngine {  		virtual void process() const = 0;  		virtual const Environment * env() const = 0;  		virtual SessionPtr session() const = 0; +		virtual PresenterPtr getPresenter(const std::string & group, const std::string & id) const = 0;  		template <class DataSourceType>  		const DataSourceType * dataSource(const std::string & name) const @@ -31,7 +33,7 @@ class ApplicationEngine {  			return s;  		}  		static ApplicationEngine * getCurrent() { return currentEngine; } -	protected: +	// protected:  		mutable DataSources datasources;  	private:  		static ApplicationEngine * currentEngine; diff --git a/project2/cgiAppEngine.cpp b/project2/cgiAppEngine.cpp index a419be0..e041d20 100644 --- a/project2/cgiAppEngine.cpp +++ b/project2/cgiAppEngine.cpp @@ -72,18 +72,13 @@ CgiApplicationEngine::Stage::~Stage()  CgiApplicationEngine::PresentStage::PresentStage(const CgiApplicationEngine * e, const std::string & id) :  	CgiApplicationEngine::Stage(e), -	present("present/" + id + ".xml") +	Presenter("present", id) +{ +} +CgiApplicationEngine::PresentStage::PresentStage(const CgiApplicationEngine * e, const std::string & group, const std::string & id) : +	CgiApplicationEngine::Stage(e), +	Presenter(group, id)  { -	while (xmlXIncludeProcessFlags(present.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0); -	xmlpp::Element * presentRoot = present.get_document()->get_root_node(); -	responseRootNodeName = presentRoot->get_attribute_value("root"); -	responseStyle = presentRoot->get_attribute_value("style"); - -	Loaders loaders; -	_DataSource::AddLoaders(loaders, appEngine->datasources); -	_ParamChecker::AddLoaders(loaders, parameterChecks); -	_View::AddLoaders(loaders, views); -	_LoaderBase::collectAll(loaders, "project2", presentRoot, true, true);  }  CgiApplicationEngine::PresentStage::~PresentStage()  { @@ -91,20 +86,16 @@ CgiApplicationEngine::PresentStage::~PresentStage()  CgiApplicationEngine::Stage *  CgiApplicationEngine::PresentStage::run()  { -	BOOST_FOREACH(OrderedParamCheckers::value_type pc, parameterChecks) { -		if (!pc.second->performCheck(appEngine)) { -			return new PresentStage(appEngine, pc.second->present); -		} -	} -	boost::shared_ptr<xmlpp::Document> responseDoc = boost::shared_ptr<xmlpp::Document>(new xmlpp::Document("1.0")); -	xmlpp::Element * responseRoot = responseDoc->create_root_node(responseRootNodeName); -	BOOST_FOREACH(Views::value_type s, views) { -		s.second->execute(responseRoot, this->appEngine); -	} -	// These were for debug... but why not pass them on? -	xmlNewNs(responseRoot->cobj(), BAD_CAST "http://project2.randomdan.homeip.net/", BAD_CAST "project2"); -	responseRoot->add_child("fqdn", "project2")->set_child_text(appEngine->_env->getServerName()); -	responseRoot->add_child("requesturi", "project2")->set_child_text(appEngine->_env->getScriptName()); +	appEngine->doc = getDataDocument(); +	sessionsContainer->CleanUp(); +	return NULL; +} + +Presenter::XmlDocumentPtr +CgiApplicationEngine::PresentStage::getDataDocument() const +{ +	XmlDocumentPtr responseDoc = Presenter::getDataDocument(); +	xmlpp::Element * responseRoot = responseDoc->get_root_node();  	// URL elements  	xmlpp::Element * uriElems = responseRoot->add_child("uriElems", "project2");  	BOOST_FOREACH(std::string s, appEngine->_env->elems) { @@ -136,12 +127,11 @@ CgiApplicationEngine::PresentStage::run()  				xmlNewDocPI(responseDoc->cobj(), BAD_CAST "xml-stylesheet", BAD_CAST buf));  	}  	free(buf); -	appEngine->doc = responseDoc; -	sessionsContainer->CleanUp(); -	return NULL; +	return responseDoc;  } -CgiApplicationEngine::RequestStage::RequestStage(const CgiApplicationEngine * e, const std::string & id) : CgiApplicationEngine::Stage(e) +CgiApplicationEngine::RequestStage::RequestStage(const CgiApplicationEngine * e, const std::string & id) : +	CgiApplicationEngine::Stage(e)  {  	xmlpp::DomParser request("request/" + id + ".xml");  	while (xmlXIncludeProcessFlags(request.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0); @@ -191,3 +181,9 @@ CgiApplicationEngine::session() const  	return sessionsContainer->GetSession(sessionID);  } +PresenterPtr +CgiApplicationEngine::getPresenter(const std::string & group, const std::string & id) const +{ +	return PresenterPtr(new PresentStage(this, group, id)); +} + diff --git a/project2/cgiAppEngine.h b/project2/cgiAppEngine.h index 0172eee..d2a1f44 100644 --- a/project2/cgiAppEngine.h +++ b/project2/cgiAppEngine.h @@ -4,6 +4,7 @@  #include "appEngine.h"  #include "task.h"  #include "paramChecker.h" +#include "presenter.h"  #include <boost/shared_ptr.hpp>  #include <boost/uuid/uuid.hpp>  #include <libxml++/document.h> @@ -28,6 +29,7 @@ class CgiApplicationEngine : public ApplicationEngine {  		}  		const Environment * env() const;  		SessionPtr session() const; +		PresenterPtr getPresenter(const std::string & group, const std::string & id) const;  		const CgiEnvironment * _env;  	protected:  		cgicc::HTTPContentHeader * header; @@ -51,16 +53,14 @@ class CgiApplicationEngine : public ApplicationEngine {  			protected:  				NoOutputExecutes tasks;  		}; -		class PresentStage : public Stage { +		class PresentStage : public Stage, public Presenter {  			public:  				PresentStage(const CgiApplicationEngine *, const std::string & id); +				PresentStage(const CgiApplicationEngine *, const std::string & group, const std::string & id);  				virtual ~PresentStage();  				virtual Stage * run();  			protected: -				Views views; -				Glib::ustring responseRootNodeName; -				Glib::ustring responseStyle; -				xmlpp::DomParser present; +				XmlDocumentPtr getDataDocument() const;  		};  		mutable Stage * currentStage;  		mutable boost::uuids::uuid sessionID; diff --git a/project2/cgiEnvironment.h b/project2/cgiEnvironment.h index 7d9b820..7df65c9 100644 --- a/project2/cgiEnvironment.h +++ b/project2/cgiEnvironment.h @@ -17,6 +17,8 @@ class CgiEnvironment : public Environment, public cgicc::CgiEnvironment {  		Glib::ustring getParamUri(const std::string & idx) const;  		Glib::ustring getParamQuery(const std::string & idx) const; +		std::string getServerName() const { return cgicc::CgiEnvironment::getServerName(); } +		std::string getScriptName() const { return cgicc::CgiEnvironment::getScriptName(); }  		std::vector<std::string> elems;  		const cgicc::Cgicc * const cgi; diff --git a/project2/environment.h b/project2/environment.h index b5e9307..2925f13 100644 --- a/project2/environment.h +++ b/project2/environment.h @@ -11,8 +11,11 @@ class Environment {  		Environment();  		virtual ~Environment() = 0; -		virtual Glib::ustring getParamUri(const std::string & idx) const; -		virtual Glib::ustring getParamQuery(const std::string & idx) const; +		virtual Glib::ustring getParamUri(const std::string & idx) const = 0; +		virtual Glib::ustring getParamQuery(const std::string & idx) const = 0; + +		virtual std::string getServerName() const = 0; +		virtual std::string getScriptName() const = 0;  	private:  }; diff --git a/project2/presenter.cpp b/project2/presenter.cpp new file mode 100644 index 0000000..95ac53d --- /dev/null +++ b/project2/presenter.cpp @@ -0,0 +1,38 @@ +#include "presenter.h" +#include <libxml/xinclude.h> +#include "dataSource.h" +#include "appEngine.h" + +Presenter::Presenter(const std::string & group, const std::string & file) : +	present(group + "/" + file + ".xml"), +	responseRootNodeName(present.get_document()->get_root_node()->get_attribute_value("root")), +	responseStyle(present.get_document()->get_root_node()->get_attribute_value("style")) +{ +	while (xmlXIncludeProcessFlags(present.get_document()->cobj(), XML_PARSE_NOXINCNODE) > 0); + +	Loaders loaders; +	_DataSource::AddLoaders(loaders, ApplicationEngine::getCurrent()->datasources); +	_View::AddLoaders(loaders, views); +	_LoaderBase::collectAll(loaders, "project2", present.get_document()->get_root_node(), true, true); +} + +Presenter::~Presenter() +{ +} + +Presenter::XmlDocumentPtr +Presenter::getDataDocument() const +{ +	ApplicationEngine * appEngine = ApplicationEngine::getCurrent(); +	XmlDocumentPtr responseDoc = boost::shared_ptr<xmlpp::Document>(new xmlpp::Document("1.0")); +	xmlpp::Element * responseRoot = responseDoc->create_root_node(responseRootNodeName); +	BOOST_FOREACH(Views::value_type s, views) { +		s.second->execute(responseRoot, appEngine); +	} +	// These were for debug... but why not pass them on? +	xmlNewNs(responseRoot->cobj(), BAD_CAST "http://project2.randomdan.homeip.net/", BAD_CAST "project2"); +	responseRoot->add_child("servername", "project2")->set_child_text(appEngine->env()->getServerName()); +	responseRoot->add_child("scriptname", "project2")->set_child_text(appEngine->env()->getScriptName()); +	return responseDoc; +} + diff --git a/project2/presenter.h b/project2/presenter.h new file mode 100644 index 0000000..d862f1f --- /dev/null +++ b/project2/presenter.h @@ -0,0 +1,27 @@ +#ifndef PRESENTER_H +#define PRESENTER_H + +#include <boost/shared_ptr.hpp> +#include <glibmm/ustring.h> +#include <libxml++/parsers/domparser.h> +#include "view.h" + +class Presenter { +	public: +		typedef boost::shared_ptr<xmlpp::Document> XmlDocumentPtr; +		Presenter(const std::string & group, const std::string & file); +		~Presenter(); + +		virtual XmlDocumentPtr getDataDocument() const; + +	protected: +		Views views; +		xmlpp::DomParser present; +	public: +		const Glib::ustring responseRootNodeName; +		const Glib::ustring responseStyle; +}; +typedef boost::shared_ptr<Presenter> PresenterPtr; + +#endif + diff --git a/project2/rdbmsDataSource.cpp b/project2/rdbmsDataSource.cpp index ab515a1..e9c0089 100644 --- a/project2/rdbmsDataSource.cpp +++ b/project2/rdbmsDataSource.cpp @@ -12,7 +12,8 @@ _RdbmsDataSource::_RdbmsDataSource(const xmlpp::Element * p) :  	_SourceObject(p),  	_DataSource(p),  	masterDsn(xmlChildText(p, "masterdsn")), -	preferLocal(p->get_attribute_value("preferlocal") != "false") +	preferLocal(p->get_attribute_value("preferlocal") != "false"), +	changesMade(false)  {  	BOOST_FOREACH(const xmlpp::Node * node, p->find("readonly/dsn")) {  		const xmlpp::Element * elem = dynamic_cast<const xmlpp::Element *>(node); @@ -29,16 +30,21 @@ _RdbmsDataSource::getWritable() const  	if (!master->inTx()) {  		master->beginTx();  	} +	changesMade = true;  	return *master;  }  ODBC::Connection &  _RdbmsDataSource::getReadonly() const  { +	if (changesMade) { +		return *connectTo(masterDsn); +	}  	if (localhost.length() == 0 && preferLocal) {  		struct utsname name;  		if (uname(&name)) { -			syslog(LOG_WARNING, "%s: Unable to determine local host name (%d:%s)", __FUNCTION__, errno, strerror(errno)); +			syslog(LOG_WARNING, "%s: Unable to determine local host name (%d:%s)", +					__FUNCTION__, errno, strerror(errno));  			localhost = "unknown";  		}  		else { @@ -48,7 +54,8 @@ _RdbmsDataSource::getReadonly() const  	if (preferLocal) {  		ReadonlyDSNs::const_iterator ro = roDSNs.find(localhost);  		if (ro == roDSNs.end()) { -			syslog(LOG_WARNING, "%s: No database host makes local host name (%s) Will use master DSN", __FUNCTION__, localhost.c_str()); +			syslog(LOG_WARNING, "%s: No database host makes local host name (%s) Will use master DSN", +					__FUNCTION__, localhost.c_str());  			return *connectTo(masterDsn);  		}  		return *connectTo(ro->second); @@ -81,7 +88,9 @@ _RdbmsDataSource::rollback()  	if (m != dbhosts.end() && m->second->inTx()) {  		m->second->rollbackTx();  	} +	changesMade = false;  } +  _RdbmsDataSource::ConnectionPtr  _RdbmsDataSource::connectTo(const std::string & dsn)  { diff --git a/project2/rdbmsDataSource.h b/project2/rdbmsDataSource.h index e5709b7..57a607f 100644 --- a/project2/rdbmsDataSource.h +++ b/project2/rdbmsDataSource.h @@ -23,6 +23,7 @@ class _RdbmsDataSource : public _DataSource {  	protected:  		static ConnectionPtr connectTo(const std::string & dsn);  		ReadonlyDSNs roDSNs; +		mutable bool changesMade;  	private:  		mutable std::string localhost; diff --git a/project2/sendmailTask.cpp b/project2/sendmailTask.cpp new file mode 100644 index 0000000..bb11017 --- /dev/null +++ b/project2/sendmailTask.cpp @@ -0,0 +1,107 @@ +#include "sendmailTask.h" +#include <boost/foreach.hpp> +#include <stdexcept> +#include "xmlObjectLoader.h" +#include "modifycommand.h" +#include "appEngine.h" +#include "rdbmsDataSource.h" +#include "presenter.h" +#include <libxslt/transform.h> +#include <libesmtp.h> +#include <libxslt/xsltutils.h> +_SendMailTask::_SendMailTask(const xmlpp::Element * p) : +	_SourceObject(p), +	_Task(p), +	to(p->get_attribute("to")), +	subject(p->get_attribute("subject")), +	from(p->get_attribute("from")), +	server(p->get_attribute("server")), +	present(p->get_attribute_value("present").raw()), +	body(NULL) +{ +} + +_SendMailTask::~_SendMailTask() +{ +	if (body) { +		xmlFree(body); +	} +} + +const char * +_SendMailTask::writeMailWrapper(void ** buf, int * len, void * arg) +{ +	return static_cast<const _SendMailTask*>(arg)->writeMail(buf, len); +} + +void +_SendMailTask::execute(const ApplicationEngine * ep, const PerRowValues * parent) const +{ +	PresenterPtr p = ep->getPresenter("emails", present); +	Presenter::XmlDocumentPtr data = p->getDataDocument(); +	typedef boost::shared_ptr<xsltStylesheet> XsltStyleSheetPtr; +	typedef boost::shared_ptr<xmlDoc> XmlDocumentPtr; + +	// Do transform +	XsltStyleSheetPtr cur = XsltStyleSheetPtr(xsltParseStylesheetFile(BAD_CAST p->responseStyle.c_str()), +			xsltFreeStylesheet); +	if (!cur) { throw std::runtime_error("Failed to load stylesheet"); } +	XmlDocumentPtr result = XmlDocumentPtr(xsltApplyStylesheet(cur.get(), data->cobj(), NULL), xmlFreeDoc); +	if (!result) { throw std::runtime_error("Failed to perform transformation"); } +	xmlDocDumpFormatMemoryEnc(result.get(), &body, &bodyLength, "utf-8", 0); + +	headers[-1] = HeaderPtr(new Header("To", to)); +	headers[-2] = HeaderPtr(new Header("From", from)); +	headers[-3] = HeaderPtr(new Header("Subject", subject)); +	headers[-4] = HeaderPtr(new Header("Content-Type", "multipart/alternative; boundary=\"<<divider>>\"")); +	headers[-5] = HeaderPtr(new Header("MIME-Version", "1.0")); +	headers[-6] = HeaderPtr(new Header("Content-Transfer-Encoding", "binary")); +	position = -1; + +	// Write email +	smtp_session_t session = smtp_create_session(); +	smtp_message_t message = smtp_add_message(session); +	smtp_set_server(session, server().c_str()); +	smtp_set_header(message, "To", NULL, NULL); +	smtp_add_recipient(message, to().c_str()); +    smtp_set_messagecb(message, writeMailWrapper, (_SendMailTask*)this); +	if (!smtp_start_session(session)) { +		char buf[BUFSIZ]; +		smtp_strerror(smtp_errno(), buf, sizeof buf); +		throw std::runtime_error(buf); +	} +} + +const char * +_SendMailTask::writeMail(void ** buf, int * len) const +{ +	if (!len) { +		position = -1; +		return NULL; +	} +	Headers::const_iterator h = headers.find(position); +	if (h != headers.end()) { +		*len = asprintf((char**)buf, "%s: %s\r\n", h->second->first.c_str(), h->second->second.c_str()); +		position -= 1; +		return (const char *)*buf; +	} +	else if (position < 0) { +		position = 0; +		*len = 51; +		return "\r\n--<<divider>>\r\nContent-Type: text/html; utf-8\r\n\r\n"; +	} +	else if (position == bodyLength) { +		position = bodyLength + 1; +		*len = 17; +		return "--<<divider>>--\r\n"; +	} +	else if (position >= 0 && position < bodyLength) { +		*buf = NULL; +		*len = bodyLength; +		position = bodyLength; +		return (const char *)body; +	} +	else { +		return NULL; +	} +} diff --git a/project2/sendmailTask.h b/project2/sendmailTask.h new file mode 100644 index 0000000..e3504b6 --- /dev/null +++ b/project2/sendmailTask.h @@ -0,0 +1,42 @@ +#ifndef SENDMAILTASK_H +#define SENDMAILTASK_H + +#include <libxml++/nodes/element.h> +#include <boost/shared_ptr.hpp> +#include <map> +#include "task.h" +#include "variables.h" + +class ApplicationEngine; +class _View; +typedef unsigned char xmlChar; + +class _SendMailTask : public _Task { +	public: +		_SendMailTask(const xmlpp::Element * p); +		virtual ~_SendMailTask(); +		virtual void execute(const ApplicationEngine *, const PerRowValues * parent = NULL) const; + +	protected: +		const Variable to; +		const Variable subject; +		const Variable from; +		const Variable server; +		const std::string present; + +	private: +		static const char * writeMailWrapper(void ** buf, int * len, void * arg); +		const char * writeMail(void ** buf, int * len) const; + +		typedef std::pair<std::string, const Glib::ustring> Header; +		typedef boost::shared_ptr<Header> HeaderPtr; +		typedef std::map<int, HeaderPtr> Headers; +		mutable Headers headers; +		mutable xmlChar * body; +		mutable int bodyLength; +		mutable int position; +}; +typedef boost::shared_ptr<_SendMailTask> SendMailTask; +typedef std::map<std::string, SendMailTask> SendMailTasks; + +#endif diff --git a/project2/sqlIterate.cpp b/project2/sqlIterate.cpp index 5499940..81f1561 100644 --- a/project2/sqlIterate.cpp +++ b/project2/sqlIterate.cpp @@ -1,6 +1,7 @@  #include "sqlIterate.h"  #include "xml.h"  #include "selectcommand.h" +#include "rdbmsDataSource.h"  #include "column.h"  #include <string.h>  #include <syslog.h> diff --git a/project2/sqlIterate.h b/project2/sqlIterate.h index 41b5648..0c26bdd 100644 --- a/project2/sqlIterate.h +++ b/project2/sqlIterate.h @@ -6,7 +6,6 @@  #include <map>  #include "selectcommand.h"  #include "iterate.h" -#include "rdbmsDataSource.h"  #include "iHaveParameters.h"  class ApplicationEngine; diff --git a/project2/task.cpp b/project2/task.cpp index a84ad7e..65ce359 100644 --- a/project2/task.cpp +++ b/project2/task.cpp @@ -4,6 +4,7 @@  #include "sqlTask.h"  #include "sessionClearTask.h"  #include "sessionSetTask.h" +#include "sendmailTask.h"  _Task::_Task(const xmlpp::Element * p) :  	_SourceObject(p), @@ -21,6 +22,7 @@ _Task::AddLoaders(Loaders & l, OrderedTasks & tasks)  	l.insert(LoadersVT("sqltask", _LoaderBase::Make<_SqlTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));  	l.insert(LoadersVT("sessionclear", _LoaderBase::Make<_SessionClearTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));  	l.insert(LoadersVT("sessionset", _LoaderBase::Make<_SessionSetTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); +	l.insert(LoadersVT("sendmail", _LoaderBase::Make<_SendMailTask, _Task, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));  }  void @@ -29,5 +31,6 @@ _Task::AddLoaders(Loaders & l, NoOutputExecutes & tasks)  	l.insert(LoadersVT("sqltask", _LoaderBase::Make<_SqlTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));  	l.insert(LoadersVT("sessionclear", _LoaderBase::Make<_SessionClearTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));  	l.insert(LoadersVT("sessionset", _LoaderBase::Make<_SessionSetTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks))); +	l.insert(LoadersVT("sendmail", _LoaderBase::Make<_SendMailTask, _NoOutputExecute, unsigned int, _SourceObject, &_SourceObject::order>(&tasks)));  } diff --git a/project2/xmlObjectLoader.h b/project2/xmlObjectLoader.h index 59c4f1c..e68a7d8 100644 --- a/project2/xmlObjectLoader.h +++ b/project2/xmlObjectLoader.h @@ -29,7 +29,7 @@ class _LoaderBase {  		void  		storeByX(std::map<KeyT, boost::shared_ptr<T> > * m, boost::shared_ptr<T> o)  		{ -			if (o) { +			if (o && m->find(o.get()->*Var) == m->end()) {  				(*m)[o.get()->*Var] = o;  			}  		} | 
