You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sems/apps/msg_storage/MsgStorage.cpp

269 lines
6.7 KiB

#include "AmPlugIn.h"
#include "log.h"
#include "MsgStorageAPI.h"
#include "MsgStorage.h"
#include "AmConfigReader.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#define MSG_DIR "/var/spool/voicebox/" // default
MsgStorage* MsgStorage::_instance = 0;
EXPORT_PLUGIN_CLASS_FACTORY(MsgStorage, MOD_NAME);
MsgStorage::MsgStorage(const string& name)
: AmDynInvokeFactory(name) {
_instance = this;
}
MsgStorage::~MsgStorage() { }
int MsgStorage::onLoad() {
msg_dir = MSG_DIR;
AmConfigReader cfg;
if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) {
DBG("no configuration could be loaded, assuming defaults.\n");
} else {
msg_dir = cfg.getParameter("storage_dir",MSG_DIR);
DBG("storage_dir set to '%s'.\n", msg_dir.c_str());
}
string path = msg_dir;
int status = mkdir(path.c_str(),
S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status && (errno != EEXIST)) {
ERROR("creating storage path '%s': %s\n",
path.c_str(),strerror(errno));
return -1;
}
path = msg_dir + "/_test_dir_";
status = mkdir(path.c_str(),
S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status && (errno != EEXIST)) {
ERROR("creating test path in storage '%s': %s\n",
path.c_str(),strerror(errno));
return -1;
}
rmdir(path.c_str());
DBG("MsgStorage loaded.\n");
return 0;
}
void MsgStorage::invoke(const string& method,
const AmArg& args, AmArg& ret) {
if(method == "msg_new"){
MessageDataFile* f =
dynamic_cast<MessageDataFile*>(args.get(3).asObject());
if (NULL == f) {
throw(string("message data is not a file ptr."));
}
ret.push(msg_new(args.get(0).asCStr(),
args.get(1).asCStr(),
args.get(2).asCStr(),
f->fp));
} else if(method == "msg_get"){
msg_get(args.get(0).asCStr(),
args.get(1).asCStr(),
args.get(2).asCStr(),
ret);
} else if(method == "msg_markread"){
ret.push(msg_markread(args.get(0).asCStr(),
args.get(1).asCStr(),
args.get(2).asCStr()));
} else if(method == "msg_delete"){
ret.push(msg_delete(args.get(0).asCStr(),
args.get(1).asCStr(),
args.get(2).asCStr()));
} else if(method == "userdir_open"){
userdir_open(args.get(0).asCStr(),
args.get(1).asCStr(),
ret);
} else if(method == "userdir_close"){
ret.push(userdir_close(args.get(0).asCStr(),
args.get(1).asCStr()));
} else if(method == "userdir_getcount"){
userdir_getcount(args.get(0).asCStr(),
args.get(1).asCStr(),
ret);
} else if(method == "_list"){
ret.push("msg_new");
ret.push("msg_get");
ret.push("msg_markread");
ret.push("msg_delete");
ret.push("userdir_open");
ret.push("userdir_close");
ret.push("userdir_getcount");
}
else
throw AmDynInvoke::NotImplemented(method);
}
int MsgStorage::msg_new(string domain, string user,
string msg_name, FILE* data) {
string path = msg_dir+ "/" + domain + "/" ;
int status = mkdir(path.c_str(),
S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status && (errno != EEXIST)) {
ERROR("creating '%s': %s\n",
path.c_str(),strerror(errno));
return MSG_EUSRNOTFOUND;
}
path = msg_dir+ "/" + domain + "/" + user + "/";
status = mkdir(path.c_str(),
S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (status && (errno != EEXIST)) {
ERROR("creating '%s': %s\n",
path.c_str(),strerror(errno));
return MSG_EUSRNOTFOUND;
}
FILE* fp = fopen((path + msg_name).c_str(), "wb");
if (!fp) {
ERROR("creating '%s': %s\n",
(path + msg_name).c_str(),strerror(errno));
return MSG_ESTORAGE;
}
if (data)
filecopy(data, fp);
fclose(fp);
return MSG_OK;
}
void MsgStorage::msg_get(string domain, string user,
string msg_name, AmArg& ret) {
string fname = msg_dir + "/" + domain + "/" + user + "/"+ msg_name;
FILE* fp = fopen(fname.c_str(), "r");
if (!fp)
ret.push(MSG_EMSGNOTFOUND);
else
ret.push(MSG_OK);
AmArg af;
af.setBorrowedPointer(new MessageDataFile(fp));
ret.push(af);
}
int MsgStorage::msg_markread(string domain, string user, string msg_name) {
string path = msg_dir + "/" + domain + "/" + user + "/" + msg_name;
struct stat e_stat;
if (stat(path.c_str(), &e_stat)) {
ERROR("cannot stat '%s': %s\n",
path.c_str(),strerror(errno));
return MSG_EMSGNOTFOUND;
}
struct utimbuf buf;
buf.actime = e_stat.st_mtime+1;
buf.modtime = e_stat.st_mtime;
if (utime(path.c_str(), &buf)) {
ERROR("cannot utime '%s': %s\n",
path.c_str(),strerror(errno));
return MSG_EREADERROR;
}
return MSG_OK;
}
int MsgStorage::msg_delete(string domain, string user, string msg_name) {
// TODO: check the directory lock
string path = msg_dir + "/" + domain + "/" + user + "/" + msg_name;
if (unlink(path.c_str())) {
ERROR("cannot unlink '%s': %s\n",
path.c_str(),strerror(errno));
return MSG_EMSGNOTFOUND;
}
return MSG_OK;
}
void MsgStorage::userdir_open(string domain, string user, AmArg& ret) {
// TODO: block the directory from delete (increase lock)
string path = msg_dir + "/" + domain + "/" + user + "/";
DBG("trying to list '%s'\n", path.c_str());
DIR* dir = opendir(path.c_str());
if (!dir) {
ret.push(MSG_EUSRNOTFOUND);
ret.push(AmArg()); // empty list
return;
}
int err=0;
struct dirent* entry;
AmArg msglist;
msglist.assertArray(0); // make it an array
while( ((entry = readdir(dir)) != NULL) && (err == 0) ){
string msgname(entry->d_name);
if(!msgname.length() ||
msgname[0] == '.'){
continue;
}
struct stat e_stat;
if (stat((path+msgname).c_str(), &e_stat)) {
ERROR("cannot stat '%s': %s\n",
(path+msgname).c_str(),strerror(errno));
continue;
}
AmArg msg;
msg.push(msgname.c_str());
// TODO: change the system here, st_atime/mtime/...
// is not really safe for saving read status!
if (e_stat.st_atime != e_stat.st_mtime) {
msg.push(0);
} else {
msg.push(1);
}
msg.push((int)e_stat.st_size);
msglist.push(msg);
}
closedir(dir);
// uh, this is really inefficient...
ret.push(MSG_OK);
ret.push(msglist);
}
int MsgStorage::userdir_close(string domain, string user) {
// TODO: unblock the directory from delete (decrease lock)
return 0;
}
void MsgStorage::userdir_getcount(string domain, string user, AmArg& ret) { }
// copies ifp to ofp, blockwise
void MsgStorage::filecopy(FILE* ifp, FILE* ofp) {
size_t nread;
char buf[1024];
rewind(ifp);
while (!feof(ifp)) {
nread = fread(buf, 1, 1024, ifp);
if (fwrite(buf, 1, nread, ofp) != nread)
break;
}
}