import the first version of cmslu

git-svn-id: svn://ttmath.org/publicrep/cmslu/trunk@460 e52654a7-88a9-db11-a3e9-0013d4bc506e
pull/3/head 0.1.0
Tomasz Sowa 14 years ago
parent d4a5f1f963
commit c53e985a92

@ -0,0 +1,46 @@
CC = g++
o = templates.o log.o requestcontroller.o main.o misc.o db.o session.o request.o content.o sessionmanager.o httpsimpleparser.o data.o dir.o error.o done.o dircontainer.o ezc.o
CFLAGS = -Wall -pedantic -g -I/usr/local/include -L/usr/local/lib
name = cmslu.fcgi
all: $(name) $(mod_cms)
$(name): $(o)
g++ -o $(name) $(CFLAGS) $(o) -lfcgi -lpq
.SUFFIXES: .cpp .o
.cpp.o:
$(CC) -c $(CFLAGS) $<
templates.o: core/templates.cpp core/templates.h core/../../ezc/src/ezc.h core/data.h core/misc.h core/log.h core/item.h core/error.h core/dir.h core/db.h core/dircontainer.h core/request.h core/requesttypes.h core/session.h core/done.h core/getparser.h core/httpsimpleparser.h core/postparser.h core/cookieparser.h
log.o: core/log.cpp core/log.h
requestcontroller.o: core/requestcontroller.cpp core/requestcontroller.h core/data.h core/misc.h core/log.h core/item.h core/error.h core/dir.h core/db.h core/dircontainer.h core/request.h core/requesttypes.h core/session.h core/done.h core/getparser.h core/httpsimpleparser.h core/postparser.h core/cookieparser.h core/content.h core/templates.h core/../../ezc/src/ezc.h core/sessionmanager.h
main.o: core/main.cpp core/requestcontroller.h core/data.h core/misc.h core/log.h core/item.h core/error.h core/dir.h core/db.h core/dircontainer.h core/request.h core/requesttypes.h core/session.h core/done.h core/getparser.h core/httpsimpleparser.h core/postparser.h core/cookieparser.h core/content.h core/templates.h core/../../ezc/src/ezc.h core/sessionmanager.h
misc.o: core/misc.cpp core/misc.h core/log.h core/item.h
db.o: core/db.cpp core/db.h core/log.h core/item.h core/misc.h core/error.h core/dircontainer.h
session.o: core/session.cpp core/session.h core/requesttypes.h core/error.h core/log.h core/item.h core/done.h
request.o: core/request.cpp core/request.h core/requesttypes.h core/log.h core/session.h core/error.h core/item.h core/done.h core/getparser.h core/httpsimpleparser.h core/postparser.h core/cookieparser.h
content.o: core/content.cpp core/content.h core/templates.h core/../../ezc/src/ezc.h core/data.h core/misc.h core/log.h core/item.h core/error.h core/dir.h core/db.h core/dircontainer.h core/request.h core/requesttypes.h core/session.h core/done.h core/getparser.h core/httpsimpleparser.h core/postparser.h core/cookieparser.h
sessionmanager.o: core/sessionmanager.cpp core/sessionmanager.h core/request.h core/requesttypes.h core/log.h core/session.h core/error.h core/item.h core/done.h core/getparser.h core/httpsimpleparser.h core/postparser.h core/cookieparser.h core/data.h core/misc.h core/dir.h core/db.h core/dircontainer.h
httpsimpleparser.o: core/httpsimpleparser.cpp core/httpsimpleparser.h
data.o: core/data.cpp core/data.h core/misc.h core/log.h core/item.h core/error.h core/dir.h core/db.h core/dircontainer.h
dir.o: core/dir.cpp core/dir.h core/item.h core/error.h core/log.h core/db.h core/misc.h core/dircontainer.h
error.o: core/error.cpp core/error.h core/log.h
done.o: core/done.cpp core/done.h
dircontainer.o: core/dircontainer.cpp core/dircontainer.h core/item.h core/log.h
ezc.o: ../ezc/src/ezc.cpp ../ezc/src/ezc.h
clean:
rm -f *.o
rm -f $(name)

@ -0,0 +1,477 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008, Tomasz Sowa
* All rights reserved.
*
*/
#include "content.h"
bool Content::Init()
{
templates.Read();
return true;
}
void Content::AddItem()
{
if( !request.session->is_logged )
return;
try
{
request.session->done = Done::added_item;
// request.item_table.resize(1);
// Item & item = request.item_table[0];
request.item.subject = request.PostVar("subject");
request.item.content = request.PostVar("content");
request.item.parent_id = data.dir.GetDirId( request.PostVar("directory") );
request.item.type = Item::file;
PrepareUrlSubject(request.item);
request.session->done_status = db.AddItem(request.item);
request.session->done_timer = 1;
// if( request.session->done_status != Request::added_item )
//request.item_table.resize(1);
//request.item_table[0] = request.item;
}
catch(const Error & e)
{
request.session->done_status = e;
}
}
void Content::EditItem()
{
if( !request.session->is_logged )
return;
try
{
request.session->done = Done::edited_item;
// request.item_table.resize(1);
// Item & item = request.item_table[0];
request.item.subject = request.PostVar("subject");
request.item.content = request.PostVar("content");
request.item.id = atol( request.PostVar("id").c_str() );
bool with_subject = false;
if( request.PostVar("old_subject") != request.item.subject )
with_subject = true;
request.item.parent_id = data.dir.GetDirId( request.PostVar("directory") );
request.item.type = Item::file;
PrepareUrlSubject(request.item);
request.session->done_status = db.EditItem(request.item, with_subject);
request.session->done_timer = 1;
}
catch(const Error & e)
{
request.session->done_status = e;
}
}
void Content::LogUser()
{
try
{
std::string & login = request.PostVar("login");
std::string & pass = request.PostVar("password");
long user_id;
if( db.CheckUser(login, pass, user_id) )
{
request.session->is_logged = true;
request.session->user_id = user_id;
request.session->user = login;
log << log2 << "User " << login << " (id: " << user_id << ") logged" << logend;
}
}
catch(const Error &)
{
}
}
void Content::MakeDirectoryStructure()
{
GetTable::size_type get_table_len = request.get_table.size();
long parent = -1;
for( get_index = 0 ; get_index < get_table_len ; ++get_index)
{
Item * pdir;
if( !data.dir.GetDir(request.get_table[get_index], parent, &pdir) )
break;
parent = pdir->id;
request.cur_dir_table.push_back( *pdir );
}
// parent - last directory (or -1 if none)
request.dir = parent;
data.dir.GetDirChilds(parent, request.dir_table);
}
Request::Result Content::StandardFunction(std::string & name)
{
Request::Result res = Request::err404;
// language polish
// in the future there'll be something here
// names of functions should not begin with an underscore '_'
if( name == "dodaj" )
res = Request::add_item;
else
if( name == "edytuj" )
res = Request::edit_item;
else
if( name == "id" )
res = Request::show_item_by_id;
else
if( name == "usun" )
res = Request::del_item;
else
if( name == "potwierdz" )
res = Request::confirm;
else
if( name == "wyloguj" )
res = Request::logout;
return res;
}
void Content::PrepareUrlSubject(Item & item)
{
SetUrlSubjectFromSubject(item);
if( StandardFunction(item.url_subject) != Request::err404 )
{
// names of functions should not begin with an underscore '_'
// and we can simply add one '_' at the beginning
// and the name will be unique
item.url_subject.insert(item.url_subject.begin(), '_');
}
}
bool Content::MakeGetCheckDir()
{
if( get_index == request.get_table.size() )
{
// request was for a directory
db.GetItems(request.item_table, item);
// !! temporarily
long default_id = -1;
if( request.cur_dir_table.empty() )
{
default_id = data.dir.root.default_item;
}
else
{
default_id = request.cur_dir_table.back().default_item;
}
if( default_id != -1 )
{
request.result = Request::show_item;
std::vector<Item>::iterator i;
for(i = request.item_table.begin() ; i != request.item_table.end() ; ++i)
{
if( i->id == default_id )
request.item_table[0] = *i;
}
request.item_table.resize(1);
}
else
if( data.one_item_is_showed &&
request.item_table.size() == 1 &&
request.item_table[0].type == Item::file )
{
request.result = Request::show_item;
}
else
{
request.result = Request::show_dir;
}
return true;
}
return false;
}
// zrobic tak ze gdzie podajemy cala liste aby nie ladowac contentu z bazy danych do itemow
void Content::MakeGet()
{
MakeDirectoryStructure();
item.Clear();
// request.dir is set by MakeDirectoryStructure()
item.parent_id = request.dir;
if( MakeGetCheckDir() )
return;
// request was for an item or a standard function
request.result = StandardFunction( request.get_table[get_index] );
if( request.result != Request::err404 )
{
// request for a standard function
++get_index;
return;
}
if( request.get_table[get_index].empty() )
// we do not have an empty url_subject
// err404 at the end
return;
// if we want to search by url_subject then request.get_table[get_index] should not be empty
item.url_subject = request.get_table[get_index++];
db.GetItems(request.item_table, item);
if( request.item_table.empty() )
// err404 at the end
return;
if( get_index == request.get_table.size() )
{
request.result = Request::show_item;
return;
}
// there is a standard function at the end
request.result = StandardFunction( request.get_table[get_index++] );
}
void Content::MakeEditItem()
{
if( !request.item_table.empty() )
request.item = request.item_table[0];
else
{
log << log1 << "Content: request.item_table should not be empty" << logend;
request.result = Request::err404;
}
}
void Content::AppendCurDir(std::string & dir)
{
std::vector<Item>::size_type a;
for(a=0 ; a<request.cur_dir_table.size() ; ++a)
{
dir += request.cur_dir_table[a].url_subject;
dir += '/';
}
}
void Content::MakeShowItemById()
{
if( get_index == request.get_table.size() )
{
request.result = Request::err404;
}
else
{
long id = atol( request.get_table[get_index++].c_str() );
db.GetItem(request.item_table, id);
if( request.item_table.empty() )
request.result = Request::err404;
else
{
request.result = Request::redirect;
std::string path;
data.dir.MakePath(request.item_table[0].parent_id, path);
request.str = data.base_url + path + request.item_table[0].url_subject;
}
}
}
void Content::MakeDelItem()
{
if( !request.session->is_logged )
return;
if( request.item_table.empty() )
{
request.result = Request::err404;
return;
}
if( get_index == request.get_table.size() )
{
request.CopyFirstItem();
request.session->item = request.item;
if( db.DelItem( request.item.id ) )
{
request.session->done = Done::deleted_item;
request.session->done_status = Error::ok;
log << log2 << "Content: deleted item: subject: " << request.item.subject << ", id: " << request.item.id << logend;
}
else
{
request.session->done = Done::deleted_item;
request.session->done_status = Error::db_no_item;
}
request.result = Request::redirect;
request.str = data.base_url;
AppendCurDir(request.str);
request.session->done_timer = 2;
}
else
{
if( StandardFunction( request.get_table[get_index++] ) == Request::confirm )
{
request.result = Request::del_item_confirm;
request.CopyFirstItem();
}
else
request.result = Request::err404;
}
}
void Content::MakeLogout()
{
if( request.session->is_logged )
{
log << log2 << "User " << request.session->user << " (id: " << request.session->user_id << ") logged out" << logend;
request.session->is_logged = false;
request.session->user.clear();
request.session->user_id = 0;
}
request.result = Request::redirect;
std::string path;
data.dir.MakePath(request.dir, path);
request.str = data.base_url + path;
if( !request.item_table.empty() )
request.str += request.item_table[0].url_subject;
request.session->done = Done::loggedout;
request.session->done_timer = 2;
}
void Content::MakeStandardFunction()
{
if( request.result == Request::edit_item )
{
MakeEditItem();
}
else
if( request.result == Request::show_item_by_id )
{
MakeShowItemById();
}
else
if( request.result == Request::del_item )
{
MakeDelItem();
}
else
if( request.result == Request::logout )
{
MakeLogout();
}
}
void Content::MakePost()
{
if( request.IsPostVar("add_item") )
AddItem();
else
if( request.IsPostVar("edit_item") )
EditItem();
else
if( request.IsPostVar("logging") )
LogUser();
}
void Content::Make()
{
MakePost();
MakeGet();
MakeStandardFunction();
templates.Generate();
// request.PrintGetTable();
//request.PrintEnv();
// request.PrintIn();
}

@ -0,0 +1,73 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008, Tomasz Sowa
* All rights reserved.
*
*/
#ifndef headerfilecontent
#define headerfilecontent
#include <cstdlib>
#include <fcgiapp.h>
#include "templates.h"
#include "request.h"
#include "error.h"
#include "misc.h"
#include "db.h"
class Content
{
// index to request.get_table
// set after calling MakeDirectoryStructure()
// index of the last item which is not a directory (or is equal get_table.size())
GetTable::size_type get_index;
// temporarily object used for some purposes
Item item;
Templates templates;
void AddItem();
void EditItem();
void LogUser();
void MakeDirectoryStructure();
Request::Result StandardFunction(std::string & name);
void PrepareUrlSubject(Item & item);
bool MakeGetCheckDir();
void MakeEditItem();
void MakeShowItemById();
void MakeDelItem();
void MakeLogout();
void MakeStandardFunction();
void AppendCurDir(std::string & dir);
void MakePost();
void MakeGet();
public:
bool Init();
void Make();
};
#endif

@ -0,0 +1,70 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008, Tomasz Sowa
* All rights reserved.
*
*/
#ifndef headerfilecookieparser
#define headerfilecookieparser
#include <fcgiapp.h>
#include "httpsimpleparser.h"
#include "requesttypes.h"
class CookieParser : public HttpSimpleParser
{
const char * cookie_string;
CookieTable & cookie_table;
protected:
virtual int GetChar()
{
if( !cookie_string || *cookie_string == 0 )
return -1;
return (int)(unsigned char)*(cookie_string++);
}
virtual void Parameter(std::string & name, std::string & value)
{
// Cookie names are case insensitive according to section 3.1 of RFC 2965
ToLower(name);
std::pair<CookieTable::iterator, bool> res = cookie_table.insert( std::make_pair(name, value) );
log << log2 << "Cookie, name: \"" << name << "\", value: \"" << value << "\"";
if( res.second == false )
log << log2 << " (skipped)";
log << log2 << logend;
}
public:
CookieParser(const char * cookie_string_, CookieTable & cookie_table_) : cookie_string(cookie_string_), cookie_table(cookie_table_)
{
HttpSimpleParser::separator = ';';
HttpSimpleParser::value_can_be_quoted = true;
HttpSimpleParser::skip_white_chars = true;
HttpSimpleParser::recognize_special_chars = false;
}
};
#endif

@ -0,0 +1,46 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008, Tomasz Sowa
* All rights reserved.
*
*/
#include "data.h"
Data::Data()
{
// all these data will be reading from a config file
log_file = "cmslu.log";
fcgi_socket = "/var/cmslu/s1";
fcgi_socket_chmod = 0770;
fcgi_socket_user = "tomek";
fcgi_socket_group = "www";
log_level = 2;
log_stdout = true;
templates = "../httpd/templates"; // templates dir
default_index = "index.html";
http_session_id_name = "slimaczek_sid";
db_database = "tomek";
db_user = "tomek";
db_pass = "monitorekasd";
// !! add a method which will be adding a slash at the end of url-es
base_url = "http://s10.slimaczek.pl/"; // with a slash at the end
one_item_is_showed = true;
dir.root.default_item = 134;
}

@ -0,0 +1,88 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008, Tomasz Sowa
* All rights reserved.
*
*/
#ifndef headerfiledata
#define headerfiledata
#include <string>
#include <vector>
#include <map>
#include "misc.h"
#include "item.h"
#include "error.h"
#include "dir.h"
class Data
{
public:
// log file name
std::string log_file;
// 1 - minimum
// 2 - (default)
// 3 - maximum - all logs
int log_level;
// logging to stdout too
bool log_stdout;
// fast cgi: socket (unix domain)
std::string fcgi_socket;
// fast cgi: socket permissions
int fcgi_socket_chmod;
// fast cgi: owner of the socket
std::string fcgi_socket_user;
// fast cgi: group of the socket
std::string fcgi_socket_group;
std::string templates;
std::string default_index;
std::string db_database;
std::string db_user;
std::string db_pass;
std::string base_url;
std::string http_session_id_name;
Dir dir;
// if there is one item in a directory
// it will be showed
// (instead of showing directory contents)
bool one_item_is_showed;
Data();
};
extern Data data;
#endif

@ -0,0 +1,668 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008, Tomasz Sowa
* All rights reserved.
*
*/
#include "db.h"
Db::Db()
{
pg_conn = 0;
}
Db::~Db()
{
Close();
}
void Db::Init(const std::string & d, const std::string & u, const std::string & p)
{
db_database = d;
db_user = u;
db_pass = p;
Connect();
}
void Db::Connect()
{
std::ostringstream buf;
buf << "dbname=" << db_database << " user=" << db_user << " password=" << db_pass;
pg_conn = PQconnectdb(buf.str().c_str());
if( !pg_conn )
log << log1 << "Db: Fatal error during connecting" << logend;
if( PQsetClientEncoding(pg_conn, "LATIN2") == -1 )
log << log1 << "Db: Can't set the proper client encoding" << logend;
}
void Db::Close()
{
if( pg_conn )
{
PQfinish(pg_conn);
pg_conn = 0;
}
}
void Db::AssertConnection()
{
if( !pg_conn )
{
log << log1 << "Db: Trying to connect into the database...";
Connect();
if( !pg_conn )
// logend from Connect()
throw Error(Error::db_fatal_error_during_connecting);
log << log1 << "ok" << logend;
}
if( PQstatus(pg_conn) != CONNECTION_OK )
{
log << log2 << "Db: connection into the database is lost, trying to recover..." << logend;
PQreset(pg_conn);
if( PQstatus(pg_conn) != CONNECTION_OK )
{
log << log2 << "no" << logend;
throw Error(Error::db_fatal_error_during_connecting);
}
else
{
log << log2 << "ok" << logend;
}
}
}
std::string Db::Escape(const std::string & s)
{
std::string result;
result.resize(s.length() * 2 + 1);
size_t len = PQescapeStringConn(pg_conn, const_cast<char*>( result.c_str() ), s.c_str(), s.length(), 0);
result.resize(len);
return result;
}
std::string Db::Escape(const char * s)
{
std::string result;
int len;
for(len=0 ; s[len] != 0 ; ++len);
result.resize(len * 2 + 1);
size_t len_new = PQescapeStringConn(pg_conn, const_cast<char*>( result.c_str() ), s, len, 0);
result.resize(len_new);
return result;
}
// ------------------
PGresult * Db::AssertQuery(const std::string & q)
{
PGresult * r = PQexec(pg_conn, q.c_str());
if( !r )
{
log << log1 << "Db: Problem with query: " << PQerrorMessage(pg_conn) << logend;
throw Error(Error::db_incorrect_query);
}
return r;
}
void Db::AssertResultStatus(PGresult * r, ExecStatusType t)
{
if( PQresultStatus(r) != t )
{
log << "Db: Incorrect result status: " << PQerrorMessage(pg_conn) << logend;
throw Error(Error::db_incorrent_result_status);
}
}
int Db::AssertColumn(PGresult * r, const char * column_name)
{
int c = PQfnumber(r, column_name);
if( c == -1 )
{
log << log1 << "Db: there is no column: " << column_name << logend;
throw Error(Error::db_no_column);
}
return c;
}
const char * Db::AssertValue(PGresult * r, int row, int col)
{
const char * res = PQgetvalue(r, row, col);
if( !res )
{
log << log1 << "Db: there is no such an item in the result, row:" << row << ", col:" << col << logend;
throw Error(Error::db_no_item);
}
return res;
}
void Db::ClearResult(PGresult * r)
{
if( r )
PQclear(r);
}
bool Db::CheckUser(std::string & login, std::string & password, long & user_id)
{
PGresult * r = 0;
bool user_ok = false;
try
{
AssertConnection();
std::ostringstream query;
query << "select id from core.user where login='" << Escape(login) << "' and password='" << Escape(password) << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
if( rows == 0 )
throw Error(Error::db_incorrect_login);
if( rows > 1 )
{
log << log1 << "Db: there is more than one user: " << login << " (with the same password)" << logend;
throw Error(Error::db_more_than_one_login);
}
int cuser_id = AssertColumn(r, "id");
const char * fuser_id = AssertValue(r, 0, cuser_id);
user_id = atol( fuser_id );
user_ok = true;
}
catch(const Error &)
{
}
ClearResult(r);
return user_ok;
}
bool Db::AddItemCreateUrlSubject(Item & item)
{
bool is_that_url;
PGresult * r = 0;
int index = 1;
const int max_index = 100;
char appendix[20];
appendix[0] = 0;
try
{
do
{
std::ostringstream query;
// this Escape can be put at the beginning (performance)
query << "select id from core.item where url_subject='" << Escape(item.url_subject) << appendix << "' and parent_id='" << item.parent_id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
if( PQntuples(r) != 0 )
{
sprintf(appendix, "_(%d)", ++index);
is_that_url = true;
}
else
{
item.url_subject += appendix;
is_that_url = false;
}
ClearResult(r);
r = 0;
}
while( is_that_url && index <= max_index );
}
catch(const Error &)
{
is_that_url = true; // for return false
}
ClearResult(r);
return !is_that_url;
}
// for testing consistency
void Db::CheckAllUrlSubjectModifyItem(Item & item)
{
PGresult * r = 0;
try
{
std::ostringstream query;
query << "update core.item set url_subject=";
// url_subject
if( AddItemCreateUrlSubject(item) )
query << '\'' << Escape(item.url_subject) << '\'';
else
{
query << '\'' << item.id << '\'';
item.url_subject.clear();
}
query << " where id='" << item.id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
}
catch(const Error &)
{
}
ClearResult(r);
}
// for checking consistency
void Db::CheckAllUrlSubject()
{
PGresult * r = 0;
Item item;
try
{
AssertConnection();
std::ostringstream query, query2;
query << "select id, subject from core.item where url_subject is null or url_subject=''";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
int rows = PQntuples(r);
int cid = AssertColumn(r, "id");
int csubject = AssertColumn(r, "subject");
for(int i = 0 ; i<rows ; ++i)
{
item.id = atol( AssertValue(r, i, cid) );
item.subject = AssertValue(r, i, csubject);
CheckAllUrlSubjectModifyItem(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
long Db::AssertCurrval(const char * table)
{
PGresult * r;
std::ostringstream query;
query << "select currval('" << table << "');";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_TUPLES_OK);
if( PQntuples(r) != 1 )
{
log << log1 << "Db: error (currval) for table: " << table << ", " << PQerrorMessage(pg_conn) << logend;
throw Error(Error::db_err_currval);
}
long res = strtol( AssertValue(r, 0, 0), 0, 10 );
return res;
}
Error Db::AddItem(Item & item)
{
PGresult * r = 0;
Error result = Error::ok;
bool url_without_id = false;
try
{
AssertConnection();
std::ostringstream query;
query << "insert into core.item (subject, content, url_subject, type, parent_id) values (";
query << '\'' << Escape(item.subject) << "', ";
query << '\'' << Escape(item.content) << "', ";
// url_subject
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
{
query << '\'' << Escape(item.url_subject) << "', ";
}
else
{
query << "currval('core.item_id_seq')" << ", ";
}
query << '\'' << static_cast<int>(item.type) << "', ";
query << '\'' << item.parent_id << "' ";
query << ");";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
item.id = AssertCurrval("core.item_id_seq");
if( !url_without_id )
ToString(item.url_subject, item.id);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
Error Db::EditItem(Item & item, bool with_subject)
{
PGresult * r = 0;
Error result = Error::ok;
bool url_without_id = false;
try
{
AssertConnection();
std::ostringstream query;
query << "update core.item set (";
if( with_subject )
query << "subject, url_subject, ";
query << "content, type, parent_id) = (";
if( with_subject )
{
query << '\'' << Escape(item.subject) << "', ";
// url_subject
url_without_id = AddItemCreateUrlSubject(item);
if( url_without_id )
{
query << '\'' << Escape(item.url_subject) << "', ";
}
else
{
query << '\'' << item.id << "', ";
}
}
query << '\'' << Escape(item.content) << "', ";
query << '\'' << static_cast<int>(item.type) << "', ";
query << '\'' << item.parent_id << "' ";
query << ") where id='" << item.id << "';";
r = AssertQuery(query.str());
AssertResultStatus(r, PGRES_COMMAND_OK);
if( with_subject && !url_without_id )
ToString(item.url_subject, item.id);
}
catch(const Error & e)
{
result = e;
}
ClearResult(r);
return result;
}
PGresult * Db::GetItemsQuery(Item & item_ref)
{
std::ostringstream query;
query << "select * from core.item where type!='0' and parent_id='" << item_ref.parent_id << "'";
if( item_ref.id != -1 )
query << " and id='" << item_ref.id << "'";
if( !item_ref.url_subject.empty() )
query << " and url_subject='" << Escape(item_ref.url_subject) << "'";
query << ';';
return AssertQuery(query.str());
}
void Db::GetItems(std::vector<Item> & item_table, Item & item_ref)
{
item_table.clear();
PGresult * r = 0;
try
{
AssertConnection();
r = GetItemsQuery(item_ref);
AssertResultStatus(r, PGRES_TUPLES_OK);
Item item;
int rows = PQntuples(r);
ItemColumns col;
col.SetColumns(r);
for(int i = 0 ; i<rows ; ++i)
{
col.SetItem(r, i, item);
item_table.push_back(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
void Db::GetItem(std::vector<Item> & item_table, long id)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select * from core.item where type='1' and id='" << id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
Item item;
int rows = PQntuples(r);
if( rows > 1 )
log << log1 << "Db: we have more than one item with id: " << id << logend;
ItemColumns col;
col.SetColumns(r);
for(int i = 0 ; i<rows ; ++i)
{
col.SetItem(r, i, item);
item_table.push_back(item);
}
}
catch(const Error &)
{
}
ClearResult(r);
}
bool Db::DelItem(long id)
{
long rows = 0;
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "delete from core.item where type='1' and id='" << id << "';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_COMMAND_OK);
const char * crows = PQcmdTuples(r);
if( crows )
{
rows = atol(crows);
if( rows > 1 )
log << log1 << "Db: more than one item were deleted" << logend;
}
}
catch(const Error &)
{
}
ClearResult(r);
return rows != 0;
}
void Db::GetDirs(DirContainer & dir_table)
{
PGresult * r = 0;
try
{
AssertConnection();
std::ostringstream query;
query << "select * from core.item where type='0';";
r = AssertQuery( query.str() );
AssertResultStatus(r, PGRES_TUPLES_OK);
Item item;
int rows = PQntuples(r);
ItemColumns col;
col.SetColumns(r);
for(int i = 0 ; i<rows ; ++i)
{
col.SetItem(r, i, item);
dir_table.PushBack( item );
}
}
catch(const Error &)
{
}
ClearResult(r);
}

@ -0,0 +1,119 @@
/*
* This file is a part of CMSLU -- Content Management System like Unix
* and is not publicly distributed
*
* Copyright (c) 2008, Tomasz Sowa
* All rights reserved.
*
*/
#ifndef headerfilecoredb
#define headerfilecoredb
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <libpq-fe.h>
#include <cstdio>
#include "log.h"
#include "item.h"
#include "misc.h"
#include "error.h"
#include "dircontainer.h"
class Db
{
protected:
PGconn * pg_conn;
std::string db_database, db_user, db_pass;
public:
Db();
~Db();
void Init(const std::string & database, const std::string & user, const std::string & pass);
void Connect();
void Close();
void AssertConnection();
std::string Escape(const std::string & s);
std::string Escape(const char * s);
PGresult * AssertQuery(const std::string & q);
void AssertResultStatus(PGresult * r, ExecStatusType t);
static int AssertColumn(PGresult * r, const char * column_name);
static const char * AssertValue(PGresult * r, int row, int col);
void ClearResult(PGresult * r);
bool CheckUser(std::string & login, std::string & password, long & user_id);
long AssertCurrval(const char * table);
bool AddItemCreateUrlSubject(Item & item);
Error AddItem(Item & item);
Error EditItem(Item & item, bool with_subject = true);
void CheckAllUrlSubjectModifyItem(Item & item);
void CheckAllUrlSubject();
PGresult * GetItemsQuery(Item & item_ref);
void GetItems(std::vector<Item> & item_table, Item & item_ref);
void GetItem(std::vector<Item> & item_table, long id);
bool DelItem(long id);
void GetDirs(DirContainer & dir_table);
struct ItemColumns
{
int id, subject, content, url_subject, type, parent_id, default_item;
void SetColumns(PGresult * r)
{
id = Db::AssertColumn(r, "id");
subject = Db::AssertColumn(r, "subject");
content = Db::AssertColumn(r, "content");
url_subject = Db::AssertColumn(r, "url_subject");