summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project2/Jamfile.jam6
-rw-r--r--project2/appEngine.h4
-rw-r--r--project2/cgiAppEngine.cpp54
-rw-r--r--project2/cgiAppEngine.h10
-rw-r--r--project2/cgiEnvironment.h2
-rw-r--r--project2/environment.h7
-rw-r--r--project2/presenter.cpp38
-rw-r--r--project2/presenter.h27
-rw-r--r--project2/rdbmsDataSource.cpp15
-rw-r--r--project2/rdbmsDataSource.h1
-rw-r--r--project2/sendmailTask.cpp107
-rw-r--r--project2/sendmailTask.h42
-rw-r--r--project2/sqlIterate.cpp1
-rw-r--r--project2/sqlIterate.h1
-rw-r--r--project2/task.cpp3
-rw-r--r--project2/xmlObjectLoader.h2
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;
}
}