added: notifications to users' emails

(core/notify.h core/notify.cpp)
       templatesnotify directory
       all notifications are managed by a second thread


git-svn-id: svn://ttmath.org/publicrep/cmslu/trunk@512 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
2009-09-30 22:31:20 +00:00
parent 9902ce2b78
commit 85b678a8fb
30 changed files with 769 additions and 37 deletions

View File

@@ -37,7 +37,8 @@ main.o: sessionmanager.h sessioncontainer.h session.h done.h item.h error.h
main.o: log.h user.h functionparser.h requesttypes.h ../app/content.h data.h
main.o: dirs.h dircontainer.h users.h ugcontainer.h groups.h group.h
main.o: functions.h function.h lastcontainer.h mounts.h mount.h request.h
main.o: thread.h compress.h db.h config.h ../confparser/confparser.h
main.o: thread.h compress.h db.h config.h ../confparser/confparser.h notify.h
main.o: ../templatesnotify/templatesnotify.h ../core/mount.h
misc.o: misc.h item.h log.h
mount.o: mount.h
mountparser.o: mountparser.h mount.h item.h error.h log.h data.h dirs.h
@@ -47,6 +48,11 @@ mounts.o: mounts.h mount.h data.h dirs.h item.h dircontainer.h users.h user.h
mounts.o: ugcontainer.h log.h groups.h group.h functions.h function.h
mounts.o: lastcontainer.h request.h requesttypes.h session.h done.h error.h
mounts.o: thread.h compress.h mountparser.h
notify.o: log.h notify.h ../templatesnotify/templatesnotify.h ../core/mount.h
notify.o: ../../ezc/src/ezc.h data.h dirs.h item.h dircontainer.h users.h
notify.o: user.h ugcontainer.h groups.h group.h functions.h function.h
notify.o: lastcontainer.h mounts.h mount.h misc.h request.h requesttypes.h
notify.o: session.h done.h error.h thread.h compress.h
request.o: request.h requesttypes.h session.h done.h item.h error.h log.h
request.o: user.h function.h thread.h compress.h getparser.h
request.o: httpsimpleparser.h postparser.h cookieparser.h data.h dirs.h
@@ -61,7 +67,8 @@ requestcontroller.o: functionparser.h requesttypes.h ../app/content.h data.h
requestcontroller.o: dirs.h dircontainer.h users.h ugcontainer.h groups.h
requestcontroller.o: group.h functions.h function.h lastcontainer.h mounts.h
requestcontroller.o: mount.h request.h thread.h compress.h postparser.h
requestcontroller.o: httpsimpleparser.h cookieparser.h
requestcontroller.o: httpsimpleparser.h cookieparser.h notify.h
requestcontroller.o: ../templatesnotify/templatesnotify.h ../core/mount.h
session.o: session.h done.h item.h error.h log.h user.h
sessioncontainer.o: sessioncontainer.h session.h done.h item.h error.h log.h
sessioncontainer.o: user.h data.h dirs.h dircontainer.h users.h ugcontainer.h

View File

@@ -1 +1 @@
o = compress.o config.o data.o db.o db_itemcolumns.o dircontainer.o dirs.o done.o error.o function.o functioncodeparser.o functionparser.o functions.o groups.o httpsimpleparser.o lastcontainer.o log.o main.o misc.o mount.o mountparser.o mounts.o request.o requestcontroller.o session.o sessioncontainer.o sessionmanager.o users.o
o = compress.o config.o data.o db.o db_itemcolumns.o dircontainer.o dirs.o done.o error.o function.o functioncodeparser.o functionparser.o functions.o groups.o httpsimpleparser.o lastcontainer.o log.o main.o misc.o mount.o mountparser.o mounts.o notify.o request.o requestcontroller.o session.o sessioncontainer.o sessionmanager.o users.o

View File

@@ -90,6 +90,7 @@ bool Config::ReadConfig(bool errors_to_stdout_)
void Config::AssignValues()
{
data.log_file = Text("log_file");
data.log_notify_file = Text("log_notify_file");
data.fcgi_socket = Text("fcgi_socket");
data.fcgi_socket_chmod = Int("fcgi_socket_chmod");
data.fcgi_socket_user = Text("fcgi_socket_user");

View File

@@ -34,8 +34,8 @@ public:
// name of the config file (full path can be)
std::string config_file;
// log file name
std::string log_file;
// log file name, log file name for notifications (sending emails, etc)
std::string log_file, log_notify_file;
// 1 - minimum
// 2 - (default)

View File

@@ -1227,7 +1227,7 @@ void Db::GetUsers(UGContainer<User> & user_table)
AssertConnection();
std::ostringstream query;
query << "select id, login, super_user, group_id from core.user left outer join core.group_mem on core.user.id = core.group_mem.user_id order by id asc;";
query << "select id, login, super_user, group_id, email, cms_notify, thread_notify from core.user left outer join core.group_mem on core.user.id = core.group_mem.user_id order by id asc;";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
@@ -1238,6 +1238,9 @@ void Db::GetUsers(UGContainer<User> & user_table)
int cname = AssertColumn(r, "login");
int csuper_user = AssertColumn(r, "super_user");
int cgroup_id = AssertColumn(r, "group_id");
int cemail = AssertColumn(r, "email");
int ccms_notify = AssertColumn(r, "cms_notify");
int cthread_notify = AssertColumn(r, "thread_notify");
User u;
long last_id = -1;
@@ -1251,6 +1254,10 @@ void Db::GetUsers(UGContainer<User> & user_table)
{
u.name = AssertValue(r, i, cname);
u.super_user = static_cast<bool>( atoi( AssertValue(r, i, csuper_user) ) );
u.email = AssertValue(r, i, cemail);
u.cms_notify = atoi( AssertValue(r, i, ccms_notify) );
u.thread_notify = atoi( AssertValue(r, i, cthread_notify) );
log << log1 << "Db: get user: id:" << u.id << ", name:" << u.name << ", super_user:" << u.super_user << logend;
iter = user_table.PushBack( u );

View File

@@ -8,7 +8,7 @@
*/
#include "log.h"
#include <ctime>
Log::Log()
@@ -27,6 +27,22 @@ void Log::Init(int log_l, const std::string & log_f, bool log_std)
void Log::PutDate(Manipulators m)
{
time_t t = std::time(0);
std::tm * loct = std::localtime(&t);
char buffer[70];
sprintf(buffer, "%d.%02d.%02d %02d:%02d:%02d ", int(loct->tm_year + 1900),
int(loct->tm_mon + 1),
int(loct->tm_mday),
int(loct->tm_hour),
int(loct->tm_min),
int(loct->tm_sec));
(*this) << m << buffer;
}
Log & Log::operator<<(const char * s)

View File

@@ -36,6 +36,7 @@ public:
void Init(int log_l, const std::string & log_f, bool log_std);
void PutDate(Manipulators m);
Log & operator<<(const char * s);
Log & operator<<(const std::string & s);
Log & operator<<(int s);
@@ -51,6 +52,7 @@ public:
extern Log log;
extern Log nlog;

View File

@@ -18,16 +18,19 @@
#include "request.h"
#include "db.h"
#include "config.h"
#include "notify.h"
// singletons
// first 'data' then 'log' then 'request'
Data data;
Log log;
Log nlog; // notify log (used by another thread)
Request request;
Db db;
Config config;
RequestController req_controller;
Notify notify;
@@ -87,6 +90,16 @@ int main(int argv, char ** argc)
}
log.Init(data.log_level, data.log_file, data.log_stdout);
nlog.Init(data.log_level, data.log_notify_file, false);
db.Init(data.db_database, data.db_user, data.db_pass);
request.Init();
if( !req_controller.Init() )
return 1;
if( !notify.Init(data.templates) )
return 2;
signal(SIGTERM, signal_term);
signal(SIGINT, signal_term);
@@ -95,13 +108,6 @@ int main(int argv, char ** argc)
while( true )
{
log.Init(data.log_level, data.log_file, data.log_stdout);
db.Init(data.db_database, data.db_user, data.db_pass);
request.Init();
if( !req_controller.Init() )
return 1;
//log << log2 << "checking for table consistency:" << logend;
// !! zrobic wyjatek dla root
//db.CheckAllUrlSubject();

View File

@@ -358,3 +358,47 @@ std::string::size_type i;
s[i] = s[i] - 'A' + 'a';
}
}
bool ValidateEmail(const std::string & email)
{
if( email.empty() )
return false;
bool correct = true;
size_t i;
char allowed_chars[] = "!#$%&'*+-/=?^_`{|}~.@";
int at = 0;
for(i=0 ; i<email.length() && correct ; ++i)
{
correct = false;
if( (email[i] >= 'A' && email[i]<='Z') ||
(email[i] >= 'a' && email[i]<='z') ||
(email[i] >= '0' && email[i]<='9') )
{
correct = true;
}
else
{
for(size_t a=0 ; a < sizeof(allowed_chars)-1 ; ++a)
{
if( email[i] == allowed_chars[a] )
{
correct = true;
break;
}
}
}
if( email[i] == '@' )
++at;
}
if( at != 1 )
return false;
return correct;
}

View File

@@ -43,4 +43,6 @@ bool IsWhite(int s);
void TrimWhite(std::string & s);
void ToSmall(std::string & s);
bool ValidateEmail(const std::string & email);
#endif

245
core/notify.cpp Executable file
View File

@@ -0,0 +1,245 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008-2009, Tomasz Sowa
* All rights reserved.
*
*/
#include <pthread.h>
#include <unistd.h>
#include "log.h"
#include "notify.h"
#include "data.h"
#include "misc.h"
#include "request.h"
// the second thread uses this pointer to reference to 'this'
// (methods for the thread should be static)
Notify * Notify::obj;
/*
methods for second thread
second thread can reference to 'this' by using 'obj' pointer
*/
void * Notify::ThreadRoutine(void * arg)
{
obj = (Notify*)arg;
obj->templates_notify.Read(obj->templates_dir);
while( true )
{
CheckQueue();
sleep(30);
}
}
void Notify::CheckQueue()
{
std::list<NotifyMsg>::iterator i = obj->notify_pool.begin();
while( i != obj->notify_pool.end() )
{
SendEmail(*i);
obj->Lock();
i = obj->notify_pool.erase(i);
obj->Unlock();
sleep(3);
}
}
void Notify::SendEmail(NotifyMsg & n)
{
TemplatesNotifyFunctions::notify_msg = &n;
obj->templates_notify.Generate();
SendEmail(n.email, obj->templates_notify.notify_str.str());
}
void Notify::SendEmail(const std::string & email, const std::string & message)
{
nlog.PutDate(log1);
if( !ValidateEmail(email) )
{
nlog << "Notify: email: " << email << " is not correct" << logend;
return;
}
obj->command = "sendmail " + email;
FILE * sendmail = popen(obj->command.c_str(), "w");
if( !sendmail )
{
nlog << "Notify: can't run sendmail" << logend;
return;
}
SendMessage(sendmail, message);
pclose(sendmail);
nlog << "Notify: email to: " << email << " has been sent" << logend;
}
void Notify::SendMessage(FILE * sendmail, const std::string & message)
{
for(size_t i=0 ; i<message.length() ; ++i)
{
if( message[i] == '\n' )
fprintf(sendmail, "\r\n");
else
fputc(message[i], sendmail);
}
}
/*
methods for main thread
*/
Notify::Notify() : mutex(PTHREAD_MUTEX_INITIALIZER)
{
}
Notify::~Notify()
{
}
bool Notify::Init(const std::string & tdir)
{
templates_dir = tdir;
int t = pthread_create(&thread, 0, ThreadRoutine, (void*)this);
if( t != 0 )
{
// log (not nlog) here
log << log1 << "Notify: can't create a thread" << logend;
return false;
}
return true;
}
bool Notify::Lock()
{
if( pthread_mutex_lock(&mutex) != 0 )
return false;
return true;
}
void Notify::Unlock()
{
pthread_mutex_unlock(&mutex);
}
void Notify::CreateItemDir(std::string & dir, bool clear)
{
if( clear )
dir.clear();
for(size_t a=0 ; a<request.dir_table.size() ; ++a)
{
dir += request.dir_table[a]->url;
dir += '/';
}
}
void Notify::CreateItemLink(std::string & link)
{
link = data.base_url;
CreateItemDir(link, false);
link += request.item.url;
}
void Notify::ItemChanged(int notify_code)
{
bool sending;
Users::Iterator i;
if( notify_code == 0 )
return;
n.notify_code = notify_code;
n.current_mount_type = data.mounts.CurrentMountType();
n.doc_base_url = data.base_url;
CreateItemDir(n.item_dir);
CreateItemLink(n.item_link);
Lock();
try
{
// don't clear notify_pool here -- it is used (and will be cleared) by the second thread
for(i=data.users.Begin() ; i != data.users.End() ; ++i)
{
sending = false;
if( data.mounts.CurrentMountType() == Mount::thread )
{
if( (i->thread_notify & notify_code) != 0 )
sending = true;
}
else
if( data.mounts.CurrentMountType() == Mount::cms )
{
if( (i->cms_notify & notify_code) != 0 )
sending = true;
}
if( sending )
{
n.email = i->email;
notify_pool.insert(notify_pool.end(), n);
}
}
}
catch(...)
{
Unlock();
throw;
}
Unlock();
}

76
core/notify.h Executable file
View File

@@ -0,0 +1,76 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008-2009, Tomasz Sowa
* All rights reserved.
*
*/
#ifndef headerfilecmslunotify
#define headerfilecmslunotify
#include <list>
#include <string>
#include <cstdio>
#include "../templatesnotify/templatesnotify.h"
#define CMSLU_NOTIFY_ITEM_ADD 1
#define CMSLU_NOTIFY_ITEM_EDIT 2
#define CMSLU_NOTIFY_ITEM_DELETE 4
#define CMSLU_NOTIFY_DIR_ADD 8
class Notify
{
public:
Notify();
~Notify();
bool Init(const std::string & tdir);
/*
this method added an item to our special pool
the pool is used by a second thread
*/
void ItemChanged(int notify_code);
private:
static void * ThreadRoutine(void * arg);
bool Lock();
void Unlock();
static void CheckQueue();
static void SendEmail(NotifyMsg & n);
static void SendEmail(const std::string & email);
static void SendEmail(const std::string & email, const std::string & message);
static void SendMessage(FILE * sendmail, const std::string & message);
void CreateItemDir(std::string & dir, bool clear = true);
void CreateItemLink(std::string & link);
pthread_t thread;
pthread_mutex_t mutex;
NotifyMsg n;
/*
objects accessed by the second thread
*/
std::list<NotifyMsg> notify_pool;
static Notify * obj;
std::string command;
std::string templates_dir;
TemplatesNotify templates_notify;
};
extern Notify notify;
#endif

View File

@@ -44,6 +44,7 @@ void Request::Clear()
headers.str("");
page.str("");
debug.str("");
notify.str("");
env_request_method = &char_empty;
env_request_uri = &char_empty;
@@ -75,6 +76,9 @@ void Request::Clear()
thread.Clear();
thread_tab.clear();
notify_code = 0;
}
@@ -283,18 +287,8 @@ void Request::ReadParameters()
void Request::StandardLog()
{
time_t t = std::time(0);
std::tm * loct = std::localtime(&t);
char buffer[70];
sprintf(buffer, "%d.%02d.%02d %02d:%02d:%02d ", int(loct->tm_year + 1900),
int(loct->tm_mon + 1),
int(loct->tm_mday),
int(loct->tm_hour),
int(loct->tm_min),
int(loct->tm_sec));
log << log1 << buffer << env_remote_addr << ' ' << env_request_method << ' ' << env_request_uri << ' ' << env_http_user_agent << logend;
log.PutDate(log1);
log << env_remote_addr << ' ' << env_request_method << ' ' << env_request_uri << ' ' << env_http_user_agent << logend;
}
@@ -357,6 +351,9 @@ void Request::SendAll()
bool Request::IsParam(const char * s)
{
std::vector<std::string*>::iterator i;

View File

@@ -13,7 +13,6 @@
#include <fcgiapp.h>
#include <sstream>
#include <vector>
#include <ctime>
#include <iomanip>
#include "requesttypes.h"
@@ -43,7 +42,8 @@ struct Request
// headers, page and debug
std::ostringstream headers, page, debug;
// notify (for mailing)
std::ostringstream headers, page, debug, notify;
GetTable get_table;
@@ -94,6 +94,10 @@ struct Request
Error status;
// last notify
int notify_code;
// ------------------
// !! stare skladowe
@@ -150,7 +154,7 @@ struct Request
void ReadParameters();
void Read();
void SendAll();
void SendNotify();
bool CanChangeUser(const Item & item, long new_user_id);
bool CanChangeGroup(const Item & item, long new_group_id);

View File

@@ -13,6 +13,7 @@
#include "request.h"
#include "postparser.h"
#include "cookieparser.h"
#include "notify.h"
@@ -165,6 +166,7 @@ void RequestController::Loop()
}
request.SendAll();
notify.ItemChanged(request.notify_code);
}
catch(const std::exception & e)
{

View File

@@ -14,13 +14,18 @@
#include <vector>
struct User
{
long id;
std::string name;
bool super_user;
std::vector<long> groups;
std::string email;
int cms_notify;
int thread_notify;
User()
{
@@ -34,6 +39,9 @@ struct User
name.clear();
super_user = false;
groups.clear();
email.clear();
cms_notify = 0;
thread_notify = 0;
}
@@ -48,7 +56,6 @@ struct User
return false;
}
};