#include "AmPlugIn.h" #include "log.h" #include "MsgStorageAPI.h" #include "MsgStorage.h" #include "AmConfigReader.h" #include #include #include #include #include #include #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(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; } }