1041 lines
26 KiB
C++
1041 lines
26 KiB
C++
/**
|
|
* fileinfo.cpp
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* File info holder
|
|
*
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
|
* Copyright (C) 2013-2023 Null Team
|
|
*
|
|
* This software is distributed under multiple licenses;
|
|
* see the COPYING file in the main directory for licensing
|
|
* information for this specific distribution.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
|
|
#include <yatephone.h>
|
|
|
|
using namespace TelEngine;
|
|
namespace { // anonymous
|
|
|
|
class FIItem;
|
|
class FIDirectory;
|
|
class FIFileData;
|
|
class FIFile;
|
|
class FileInfoMsgHandler;
|
|
class FIAccount; // Holds account data and share items
|
|
class FileInfo;
|
|
|
|
class ResultSetMngt
|
|
{
|
|
public:
|
|
inline ResultSetMngt()
|
|
{ reset(); }
|
|
inline ResultSetMngt(const NamedList& list)
|
|
{ reset(&list); }
|
|
void reset(const NamedList* list = 0);
|
|
bool m_addRsm;
|
|
int m_max;
|
|
int m_index;
|
|
};
|
|
|
|
class FIFileData : public RefObject
|
|
{
|
|
YNOCOPY(FIFileData);
|
|
public:
|
|
inline FIFileData(const char* file, u_int64_t size, unsigned int time,
|
|
const char* desc = 0)
|
|
: m_file(file), m_size(size), m_time(time),
|
|
m_description(desc)
|
|
{}
|
|
inline FIFileData(const char* file, const char* desc = 0)
|
|
: m_file(file), m_size(0), m_time(0), m_description(desc)
|
|
{}
|
|
inline const String& file() const
|
|
{ return m_file; }
|
|
inline u_int64_t size() const
|
|
{ return m_size; }
|
|
inline unsigned int time() const
|
|
{ return m_time; }
|
|
inline const String& description() const
|
|
{ return m_description; }
|
|
void addToList(NamedList& list, const String& prefix) const;
|
|
bool operator==(const FIFileData& other);
|
|
inline bool operator!=(const FIFileData& other)
|
|
{ return !operator==(other); }
|
|
virtual const String& toString() const
|
|
{ return m_file; }
|
|
static FIFileData* build(const char* file, const char* desc = 0, int* error = 0);
|
|
protected:
|
|
String m_file; // File path and name
|
|
u_int64_t m_size; // File size
|
|
unsigned int m_time; // File time
|
|
String m_description; // File description
|
|
|
|
private:
|
|
FIFileData() {} // No default contructor
|
|
};
|
|
|
|
class FIItem : public RefObject
|
|
{
|
|
public:
|
|
inline FIItem(const char* name)
|
|
: m_name(name)
|
|
{}
|
|
inline const String& name() const
|
|
{ return m_name; }
|
|
virtual FIDirectory* directory()
|
|
{ return 0; }
|
|
virtual FIFile* file()
|
|
{ return 0; }
|
|
virtual const String& toString() const
|
|
{ return name(); }
|
|
private:
|
|
FIItem() {}
|
|
String m_name;
|
|
};
|
|
|
|
class FIDirectory : public FIItem
|
|
{
|
|
friend class FileInfo;
|
|
public:
|
|
FIDirectory(const char* name, const char* path, bool setMutex = false, bool updated = false);
|
|
~FIDirectory();
|
|
inline const String& path() const
|
|
{ return m_path; }
|
|
inline Mutex* mutex()
|
|
{ return m_mutex; }
|
|
inline bool lock(long maxwait = -1)
|
|
{ return m_mutex && m_mutex->lock(maxwait); }
|
|
inline bool unlock()
|
|
{ return m_mutex && m_mutex->unlock(); }
|
|
inline bool updated() const
|
|
{ return m_updated; }
|
|
// Update content from file system
|
|
void update();
|
|
// Add/replace an item. This method is not thread safe
|
|
bool setItemUnsafe(FIItem* item, const String& oldName);
|
|
// Remove an item. This method is not thread safe
|
|
bool removeUnsafe(const String& itemName);
|
|
// Find a directory by path. This method is not thread safe
|
|
FIDirectory* findDirPath(const String& path);
|
|
// Find a file by path. This method is not thread safe
|
|
FIFile* findFilePath(const String& path);
|
|
// Clear children
|
|
inline void clear()
|
|
{ m_children.clear(); }
|
|
virtual FIDirectory* directory()
|
|
{ return this; }
|
|
void addDirInfoRsp(NamedList& list, const ResultSetMngt& rsm);
|
|
// Append FIItem data to a list of parameters
|
|
static void addFIItem(NamedList& list, FIItem* fi, bool full = true,
|
|
const String& prefix = String::empty());
|
|
|
|
protected:
|
|
inline FIItem* findChild(const String& name) {
|
|
ObjList* o = m_children.find(name);
|
|
return o ? static_cast<FIItem*>(o->get()) : 0;
|
|
}
|
|
inline FIDirectory* findDir(const String& name) {
|
|
FIItem* ch = findChild(name);
|
|
return ch ? ch->directory() : 0;
|
|
}
|
|
inline FIFile* findFile(const String& name) {
|
|
FIItem* ch = findChild(name);
|
|
return ch ? ch->file() : 0;
|
|
}
|
|
// Add a file
|
|
// Replace file data if already in the list and changed
|
|
FIFile* internalAddFile(FIFileData* fd, const String& name);
|
|
|
|
String m_path;
|
|
Mutex* m_mutex;
|
|
bool m_updated;
|
|
ObjList m_children;
|
|
};
|
|
|
|
class FIFile : public FIItem
|
|
{
|
|
friend class FileInfo;
|
|
public:
|
|
inline FIFile(const char* name, FIFileData* data)
|
|
: FIItem(name), m_data(0) {
|
|
if (data && data->ref())
|
|
m_data = data;
|
|
}
|
|
virtual ~FIFile()
|
|
{ TelEngine::destruct(m_data); }
|
|
inline const FIFileData* data() const
|
|
{ return m_data; }
|
|
virtual FIFile* file()
|
|
{ return this; }
|
|
protected:
|
|
FIFileData* m_data;
|
|
};
|
|
|
|
class FileInfoMsgHandler : public MessageHandler
|
|
{
|
|
public:
|
|
// Message handlers
|
|
// Non-negative enum values will be used as handler priority
|
|
enum {
|
|
EngineStart = -1,
|
|
FileInfo = -2,
|
|
CallRoute = 90,
|
|
};
|
|
FileInfoMsgHandler(int handler, int prio);
|
|
protected:
|
|
virtual bool received(Message& msg);
|
|
private:
|
|
int m_handler;
|
|
};
|
|
|
|
// Holds account data and share items
|
|
class FIAccount : public RefObject, public Mutex
|
|
{
|
|
public:
|
|
FIAccount(const char* name);
|
|
inline const String& name() const
|
|
{ return m_name; }
|
|
inline bool canRoute() const
|
|
{ return m_canRoute; }
|
|
inline void canRoute(bool val)
|
|
{ m_canRoute = val; }
|
|
// Handle file info set
|
|
bool handleFileInfoSet(const NamedList& list);
|
|
// Handle file info remove
|
|
bool handleFileInfoRemove(const NamedList& list, const String& contact);
|
|
// Handle file info query
|
|
bool handleFileInfoQuery(const NamedList& list);
|
|
// Handle call.route
|
|
bool route(Message& msg, const String& contact);
|
|
// Remove share for a given contact
|
|
bool removeShare(const String& contact);
|
|
virtual const String& toString() const
|
|
{ return name(); }
|
|
|
|
protected:
|
|
// Find shared for a given contact
|
|
bool findShare(const String& contact, RefPointer<FIDirectory>& dir, bool add = false);
|
|
virtual void destroyed();
|
|
|
|
ObjList m_share; // List of share dirs. Dir name is the contact name
|
|
bool m_canRoute; // File transfers can be routed for this account
|
|
|
|
private:
|
|
FIAccount() {} // No default contructor
|
|
String m_name;
|
|
};
|
|
|
|
class FileInfo : public Module
|
|
{
|
|
public:
|
|
FileInfo();
|
|
~FileInfo();
|
|
inline Message* message(const char* msg) {
|
|
Message* m = new Message(msg);
|
|
m->addParam("module",name());
|
|
return m;
|
|
}
|
|
inline void getSendTarget(String& buf) {
|
|
Lock lck(this);
|
|
buf = m_sendTarget;
|
|
}
|
|
inline void copyRouteParams(NamedList& dest) {
|
|
Lock lck(this);
|
|
dest.copyParams(m_routeParams);
|
|
}
|
|
virtual void initialize();
|
|
bool findAccount(const String& name, RefPointer<FIAccount>& acc, bool add = false);
|
|
bool removeAccount(FIAccount* acc, bool delObj = true);
|
|
bool handleFileInfo(Message& msg);
|
|
bool handleCallRoute(Message& msg);
|
|
|
|
protected:
|
|
virtual bool received(Message& msg, int id);
|
|
|
|
ObjList m_accounts;
|
|
String m_sendTarget; // File send target to set when routing
|
|
NamedList m_routeParams; // Parameters to be set when routing
|
|
};
|
|
|
|
INIT_PLUGIN(FileInfo);
|
|
static const char* cfgFile = "fileinfo";
|
|
static bool s_engineStarted = false;
|
|
|
|
// Message handlers installed by the module
|
|
static const TokenDict s_msgHandler[] = {
|
|
{"engine.start", FileInfoMsgHandler::EngineStart},
|
|
{"file.info", FileInfoMsgHandler::FileInfo},
|
|
{"call.route", FileInfoMsgHandler::CallRoute},
|
|
{0,0}
|
|
};
|
|
|
|
|
|
//
|
|
// ResultSetMngt
|
|
//
|
|
void ResultSetMngt::reset(const NamedList* list)
|
|
{
|
|
m_max = -1;
|
|
m_index = -1;
|
|
m_addRsm = false;
|
|
if (!list)
|
|
return;
|
|
m_max = list->getIntValue(YSTRING("rsm_max"),-1);
|
|
m_index = list->getIntValue(YSTRING("rsm_index"),-1);
|
|
m_addRsm = (m_max >= 0) || (m_index >= 0);
|
|
}
|
|
|
|
|
|
//
|
|
// FIFileData
|
|
//
|
|
void FIFileData::addToList(NamedList& list, const String& prefix) const
|
|
{
|
|
if (time())
|
|
list.addParam(prefix + "time",String(time()));
|
|
list.addParam(prefix + "size",String((unsigned int)size()));
|
|
if (description())
|
|
list.addParam(prefix + "description",description());
|
|
}
|
|
|
|
bool FIFileData::operator==(const FIFileData& other)
|
|
{
|
|
if (m_file != other.file())
|
|
return false;
|
|
if (m_size != other.size() || m_time != other.time())
|
|
return false;
|
|
return m_description == other.description();
|
|
}
|
|
|
|
FIFileData* FIFileData::build(const char* file, const char* desc, int* error)
|
|
{
|
|
XDebug(&__plugin,DebugAll,"FIFileData::build(%s,%s)",file,desc);
|
|
if (TelEngine::null(file))
|
|
return 0;
|
|
File f;
|
|
bool ok = false;
|
|
u_int64_t size = 0;
|
|
unsigned int time = 0;
|
|
while (f.openPath(file)) {
|
|
int64_t len = f.length();
|
|
if (len < 0 || f.error())
|
|
break;
|
|
size = len;
|
|
if (!f.getFileTime(time))
|
|
break;
|
|
ok = true;
|
|
break;
|
|
}
|
|
if (ok)
|
|
return new FIFileData(file,size,time,desc);
|
|
String tmp;
|
|
Thread::errorString(tmp,f.error());
|
|
Debug(&__plugin,DebugNote,"FileData failed to build file '%s': %d '%s'",
|
|
file,f.error(),tmp.c_str());
|
|
if (error)
|
|
*error = f.error();
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// FIDirectory
|
|
//
|
|
FIDirectory::FIDirectory(const char* name, const char* path, bool setMutex, bool updated)
|
|
: FIItem(name),
|
|
m_path(path),
|
|
m_mutex(0),
|
|
m_updated(updated)
|
|
{
|
|
if (setMutex)
|
|
m_mutex = new Mutex(false,"FIDirectory");
|
|
}
|
|
|
|
FIDirectory::~FIDirectory()
|
|
{
|
|
if (m_mutex)
|
|
delete m_mutex;
|
|
}
|
|
|
|
// Update content from file system
|
|
void FIDirectory::update()
|
|
{
|
|
if (m_updated || !m_path)
|
|
return;
|
|
int error = 0;
|
|
ObjList dirs;
|
|
ObjList files;
|
|
bool ok = File::listDirectory(m_path,&dirs,&files,&error);
|
|
if (Thread::check(false))
|
|
return;
|
|
if (!ok) {
|
|
String s;
|
|
Thread::errorString(s,error);
|
|
Debug(&__plugin,DebugNote,"Failed to list directory '%s': %d %s",
|
|
m_path.c_str(),error,s.c_str());
|
|
return;
|
|
}
|
|
if (m_updated)
|
|
return;
|
|
m_children.clear();
|
|
String p;
|
|
p << m_path << Engine::pathSeparator();
|
|
ObjList* o = dirs.skipNull();
|
|
ObjList* last = &m_children;
|
|
for (; o; o = o->skipNext()) {
|
|
String* n = static_cast<String*>(o->get());
|
|
if (!*n)
|
|
continue;
|
|
if (Thread::check(false))
|
|
break;
|
|
last = last->append(new FIDirectory(*n,p + *n));
|
|
}
|
|
o = !Thread::check(false) ? files.skipNull() : 0;
|
|
for (; o; o = o->skipNext()) {
|
|
String* n = static_cast<String*>(o->get());
|
|
if (!*n)
|
|
continue;
|
|
String tmp = p + *n;
|
|
FIFileData* fd = FIFileData::build(tmp);
|
|
if (fd) {
|
|
last = last->append(new FIFile(*n,fd));
|
|
TelEngine::destruct(fd);
|
|
}
|
|
if (Thread::check(false))
|
|
break;
|
|
}
|
|
m_updated = !Thread::check(false);
|
|
if (!m_updated)
|
|
m_children.clear();
|
|
}
|
|
|
|
// Add/replace an item. This method is not thread safe
|
|
bool FIDirectory::setItemUnsafe(FIItem* item, const String& oldName)
|
|
{
|
|
if (!item)
|
|
return false;
|
|
ObjList* last = 0;
|
|
if (oldName && oldName != item->name()) {
|
|
ObjList* o = m_children.find(oldName);
|
|
if (o) {
|
|
if (!m_children.find(item->name())) {
|
|
o->set(item);
|
|
return true;
|
|
}
|
|
o->remove();
|
|
}
|
|
}
|
|
for (ObjList* o = m_children.skipNull(); o;) {
|
|
FIItem* it = static_cast<FIItem*>(o->get());
|
|
if (it == item)
|
|
return true;
|
|
if (it->name() == item->name()) {
|
|
o->remove();
|
|
o->append(item);
|
|
return true;
|
|
}
|
|
ObjList* tmp = o->skipNext();
|
|
if (!tmp) {
|
|
last = o;
|
|
break;
|
|
}
|
|
o = tmp;
|
|
}
|
|
if (last)
|
|
last->append(item);
|
|
else
|
|
m_children.append(item);
|
|
return true;
|
|
}
|
|
|
|
// Remove an item. This method is not thread safe
|
|
bool FIDirectory::removeUnsafe(const String& itemName)
|
|
{
|
|
if (!itemName)
|
|
return false;
|
|
GenObject* gen = m_children.remove(itemName,false);
|
|
XDebug(&__plugin,DebugAll,"FIDirectory::removeUnsafe(%s) found=%p [%p]",
|
|
itemName.c_str(),gen,this);
|
|
if (!gen)
|
|
return false;
|
|
TelEngine::destruct(gen);
|
|
return true;
|
|
}
|
|
|
|
// Find a directory by path. This method is not thread safe
|
|
FIDirectory* FIDirectory::findDirPath(const String& path)
|
|
{
|
|
ObjList* list = path.split('/',false);
|
|
FIDirectory* dir = this;
|
|
for (ObjList* o = list->skipNull(); dir && o; o = o->skipNext())
|
|
dir = dir->findDir(o->get()->toString());
|
|
TelEngine::destruct(list);
|
|
return dir;
|
|
}
|
|
|
|
// Find a file by path. This method is not thread safe
|
|
FIFile* FIDirectory::findFilePath(const String& path)
|
|
{
|
|
int pos = path.rfind('/');
|
|
if (pos < 0)
|
|
return findFile(path);
|
|
FIDirectory* d = findDirPath(path.substr(0,pos));
|
|
if (d)
|
|
return d->findFile(path.substr(pos + 1));
|
|
return 0;
|
|
}
|
|
|
|
void FIDirectory::addDirInfoRsp(NamedList& list, const ResultSetMngt& rsm)
|
|
{
|
|
// Item count request
|
|
if (rsm.m_max == 0) {
|
|
if (!rsm.m_addRsm)
|
|
return;
|
|
list.addParam("rsm_count",String(m_children.count()));
|
|
return;
|
|
}
|
|
ObjList* o = m_children.skipNull();
|
|
int index = 0;
|
|
if (rsm.m_index >= 0) {
|
|
while (index < rsm.m_index && o) {
|
|
index++;
|
|
o = o->skipNext();
|
|
}
|
|
}
|
|
FIItem* first = 0;
|
|
FIItem* last = 0;
|
|
int maxItems = rsm.m_max > 0 ? rsm.m_max : 0;
|
|
unsigned int n = 1;
|
|
for (; o; o = o->skipNext()) {
|
|
FIItem* item = static_cast<FIItem*>(o->get());
|
|
if (!(item->file() || item->directory()))
|
|
continue;
|
|
String prefix = "item.";
|
|
prefix << n++;
|
|
prefix << ".";
|
|
addFIItem(list,item,true,prefix);
|
|
if (!first)
|
|
first = item;
|
|
last = item;
|
|
if (maxItems) {
|
|
maxItems--;
|
|
if (!maxItems)
|
|
break;
|
|
}
|
|
}
|
|
if (!rsm.m_addRsm)
|
|
return;
|
|
if (first) {
|
|
list.addParam("rsm_first",first->name());
|
|
list.addParam("rsm_first.index",String(index));
|
|
}
|
|
if (last)
|
|
list.addParam("rsm_last",last->name());
|
|
list.addParam("rsm_count",String(m_children.count()));
|
|
}
|
|
|
|
// Append FIItem data to a list of parameters
|
|
void FIDirectory::addFIItem(NamedList& list, FIItem* fi, bool full, const String& prefix)
|
|
{
|
|
if (!fi)
|
|
return;
|
|
if (prefix) {
|
|
if (prefix.endsWith("."))
|
|
list.addParam(prefix.substr(0,prefix.length() - 1),fi->name());
|
|
else
|
|
list.addParam(prefix,fi->name());
|
|
if (fi->file())
|
|
list.addParam(prefix + "isfile",String::boolText(true));
|
|
}
|
|
else
|
|
list.addParam("name",fi->name());
|
|
FIFile* f = fi->file();
|
|
if (!f)
|
|
return;
|
|
const FIFileData* d = f->data();
|
|
if (d)
|
|
d->addToList(list,prefix);
|
|
}
|
|
|
|
// Add a file
|
|
FIFile* FIDirectory::internalAddFile(FIFileData* fd, const String& fn)
|
|
{
|
|
if (!(fd && fn))
|
|
return 0;
|
|
ObjList* o = m_children.find(fn);
|
|
FIFile* f = 0;
|
|
if (!o) {
|
|
if (fd->ref()) {
|
|
f = new FIFile(fn,fd);
|
|
m_children.append(f);
|
|
DDebug(&__plugin,DebugAll,"Dir(%s) added file '%s' (%s) [%p]",
|
|
name().c_str(),fn.c_str(),fd->file().c_str(),this);
|
|
}
|
|
}
|
|
else {
|
|
FIItem* ch = static_cast<FIItem*>(o->get());
|
|
f = ch->file();
|
|
if (f) {
|
|
const FIFileData* e = f->data();
|
|
if (e != fd || *fd != *e) {
|
|
if (fd->ref()) {
|
|
DDebug(&__plugin,DebugAll,"Dir(%s) replacing file '%s' %s -> %s [%p]",
|
|
name().c_str(),fn.c_str(),e ? e->file().c_str() : "",
|
|
fd->file().c_str(),this);
|
|
f = new FIFile(fn,fd);
|
|
o->set(f);
|
|
}
|
|
else
|
|
f = 0;
|
|
}
|
|
}
|
|
else
|
|
DDebug(&__plugin,DebugInfo,
|
|
"Dir(%s) can't add file '%s': a non-file item already in the list [%p]",
|
|
name().c_str(),fn.c_str(),this);
|
|
|
|
}
|
|
return f;
|
|
}
|
|
|
|
|
|
//
|
|
// FileInfoMsgHandler
|
|
//
|
|
FileInfoMsgHandler::FileInfoMsgHandler(int handler, int prio)
|
|
: MessageHandler(lookup(handler,s_msgHandler),prio,__plugin.name()),
|
|
m_handler(handler)
|
|
{
|
|
}
|
|
|
|
bool FileInfoMsgHandler::received(Message& msg)
|
|
{
|
|
String* module = msg.getParam(YSTRING("module"));
|
|
if (module && *module == __plugin.name())
|
|
return false;
|
|
switch (m_handler) {
|
|
case FileInfo:
|
|
return __plugin.handleFileInfo(msg);
|
|
case CallRoute:
|
|
return __plugin.handleCallRoute(msg);
|
|
case EngineStart:
|
|
s_engineStarted = true;
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//
|
|
// FIAccount
|
|
//
|
|
FIAccount::FIAccount(const char* name)
|
|
: Mutex(false,"FIAccount"),
|
|
m_canRoute(true),
|
|
m_name(name)
|
|
{
|
|
}
|
|
|
|
// Handle file info set for a given account
|
|
bool FIAccount::handleFileInfoSet(const NamedList& list)
|
|
{
|
|
const String& contact = list[YSTRING("contact")];
|
|
XDebug(&__plugin,DebugAll,"Account(%s) handleFileInfoSet(%s) [%p]",
|
|
name().c_str(),contact.c_str(),this);
|
|
RefPointer<FIDirectory> dir;
|
|
String prefix = "item";
|
|
for (unsigned int i = 0; true; i++) {
|
|
String pref = prefix;
|
|
if (i)
|
|
pref << "." << i;
|
|
String* shareName = list.getParam(pref);
|
|
if (!shareName) {
|
|
if (i)
|
|
break;
|
|
continue;
|
|
}
|
|
if (!*shareName)
|
|
continue;
|
|
// Share name can't contain '/'
|
|
if (shareName->find('/') >= 0) {
|
|
Debug(&__plugin,DebugNote,"Share name '%s' contains '/' (not accepted)",
|
|
shareName->c_str());
|
|
continue;
|
|
}
|
|
String path = list[pref + ".path"];
|
|
if (path.endsWith("/") || path.endsWith("\\"))
|
|
path = path.substr(0,path.length() - 1);
|
|
if (!path)
|
|
continue;
|
|
FIItem* item = 0;
|
|
if (list.getBoolValue(pref + ".isfile")) {
|
|
Debug(&__plugin,DebugNote,"Can't set share file: not implemented");
|
|
continue;
|
|
}
|
|
else
|
|
item = new FIDirectory(*shareName,path);
|
|
if (!dir) {
|
|
if (!findShare(contact,dir,true)) {
|
|
TelEngine::destruct(item);
|
|
break;
|
|
}
|
|
dir->lock();
|
|
}
|
|
bool ok = dir->setItemUnsafe(item,list[pref + ".oldname"]);
|
|
Debug(&__plugin,ok ? DebugAll : DebugNote,
|
|
"Account(%s) contact=%s %s item name=%s path=%s [%p]",
|
|
name().c_str(),contact.c_str(),ok ? "set" : "failed to set",
|
|
shareName->c_str(),path.c_str(),this);
|
|
}
|
|
if (dir)
|
|
dir->unlock();
|
|
dir = 0;
|
|
return true;
|
|
}
|
|
|
|
// Handle file info remove
|
|
bool FIAccount::handleFileInfoRemove(const NamedList& list, const String& contact)
|
|
{
|
|
XDebug(&__plugin,DebugAll,"Account(%s) handleFileInfoRemove(%s) [%p]",
|
|
name().c_str(),contact.c_str(),this);
|
|
RefPointer<FIDirectory> dir;
|
|
String prefix = "item";
|
|
bool something = false;
|
|
for (unsigned int i = 0; true; i++) {
|
|
String pref = prefix;
|
|
if (i)
|
|
pref << "." << i;
|
|
String* shareName = list.getParam(pref);
|
|
if (!shareName) {
|
|
if (i)
|
|
break;
|
|
continue;
|
|
}
|
|
something = true;
|
|
if (!*shareName)
|
|
continue;
|
|
if (!dir) {
|
|
if (!findShare(contact,dir))
|
|
break;
|
|
dir->lock();
|
|
}
|
|
if (dir->removeUnsafe(*shareName))
|
|
Debug(&__plugin,DebugAll,"Account(%s) contact=%s removed item %s [%p]",
|
|
name().c_str(),contact.c_str(),shareName->c_str(),this);
|
|
}
|
|
if (dir)
|
|
dir->unlock();
|
|
dir = 0;
|
|
if (!something)
|
|
return removeShare(contact);
|
|
return true;
|
|
}
|
|
|
|
// Handle file info query for a given account
|
|
bool FIAccount::handleFileInfoQuery(const NamedList& list)
|
|
{
|
|
const String& contact = list[YSTRING("from")];
|
|
XDebug(&__plugin,DebugAll,"Account(%s) handleFileInfoQuery(%s) [%p]",
|
|
name().c_str(),contact.c_str(),this);
|
|
if (!contact)
|
|
return false;
|
|
String* dir = list.getParam(YSTRING("dir"));
|
|
String* file = dir ? 0 : list.getParam(YSTRING("file"));
|
|
if (!dir && TelEngine::null(file))
|
|
return false;
|
|
RefPointer<FIDirectory> cdir;
|
|
bool ok = findShare(contact,cdir);
|
|
if (!ok) {
|
|
// Don't respond if there is no subscription
|
|
const String& sub = list[YSTRING("subscription")];
|
|
ok = sub && (sub == YSTRING("both") || sub == YSTRING("from"));
|
|
}
|
|
XDebug(&__plugin,ok ? DebugAll : DebugNote,
|
|
"Account(%s) query from '%s' dir=%s file=%s [%p]",name().c_str(),
|
|
contact.c_str(),TelEngine::c_safe(dir),TelEngine::c_safe(file),this);
|
|
Message* m = __plugin.message("file.info");
|
|
m->copyParams(list,"account,id");
|
|
m->addParam("to",contact);
|
|
m->addParam("to_instance",list.getValue(YSTRING("from_instance")),false);
|
|
m->addParam("operation","result");
|
|
if (cdir) {
|
|
cdir->lock();
|
|
if (dir) {
|
|
FIDirectory* directory = cdir;
|
|
if (*dir)
|
|
directory = directory->findDirPath(*dir);
|
|
if (directory) {
|
|
// TODO: Use a separate thread for update in server mode ?
|
|
directory->update();
|
|
ResultSetMngt rsm(list);
|
|
directory->addDirInfoRsp(*m,rsm);
|
|
}
|
|
}
|
|
else
|
|
// TODO: update path ?
|
|
FIDirectory::addFIItem(*m,cdir->findFilePath(*file));
|
|
cdir->unlock();
|
|
cdir = 0;
|
|
}
|
|
Engine::enqueue(m);
|
|
return true;
|
|
}
|
|
|
|
// Handle call.route
|
|
bool FIAccount::route(Message& msg, const String& contact)
|
|
{
|
|
if (!contact)
|
|
return false;
|
|
const String& file = msg[YSTRING("file_name")];
|
|
if (!file)
|
|
return false;
|
|
RefPointer<FIDirectory> cdir;
|
|
if (!findShare(contact,cdir)) {
|
|
Debug(&__plugin,DebugAll,"Account(%s) routing: contact '%s' not found [%p]",
|
|
name().c_str(),contact.c_str(),this);
|
|
return false;
|
|
}
|
|
Lock lck(cdir->mutex());
|
|
FIFile* f = cdir->findFilePath(file);
|
|
String s;
|
|
if (f && f->data())
|
|
s = f->data()->file();
|
|
lck.drop();
|
|
Debug(&__plugin,DebugAll,"Account(%s) routing contact='%s' file='%s' found='%s' [%p]",
|
|
name().c_str(),contact.c_str(),file.c_str(),s.c_str(),this);
|
|
if (!s)
|
|
return false;
|
|
__plugin.copyRouteParams(msg);
|
|
__plugin.getSendTarget(msg.retValue());
|
|
msg.retValue() << s;
|
|
return true;
|
|
}
|
|
|
|
// Find shared for a given contact
|
|
bool FIAccount::findShare(const String& contact, RefPointer<FIDirectory>& dir, bool add)
|
|
{
|
|
if (!contact)
|
|
return false;
|
|
Lock lck(this);
|
|
ObjList* o = m_share.find(contact);
|
|
XDebug(&__plugin,DebugInfo,"Account(%s) findShare('%s',%u) found=%p [%p]",
|
|
name().c_str(),contact.c_str(),add,o,this);
|
|
if (!o) {
|
|
if (!add)
|
|
return false;
|
|
FIDirectory* d = new FIDirectory(contact,0,true,true);
|
|
o = m_share.append(d);
|
|
Debug(&__plugin,DebugInfo,"Account(%s) added contact '%s' [%p]",
|
|
name().c_str(),contact.c_str(),this);
|
|
}
|
|
dir = static_cast<FIDirectory*>(o->get());
|
|
return dir != 0;
|
|
}
|
|
|
|
// Remove share for a given contact
|
|
bool FIAccount::removeShare(const String& contact)
|
|
{
|
|
if (!contact)
|
|
return false;
|
|
Lock lck(this);
|
|
GenObject* gen = m_share.remove(contact,false);
|
|
if (!gen)
|
|
return false;
|
|
lck.drop();
|
|
Debug(&__plugin,DebugInfo,"Account(%s) removed contact '%s' [%p]",
|
|
name().c_str(),contact.c_str(),this);
|
|
TelEngine::destruct(gen);
|
|
return true;
|
|
}
|
|
|
|
void FIAccount::destroyed()
|
|
{
|
|
m_share.clear();
|
|
__plugin.removeAccount(this,false);
|
|
RefObject::destroyed();
|
|
}
|
|
|
|
|
|
//
|
|
// FileInfo
|
|
//
|
|
FileInfo::FileInfo()
|
|
: Module("fileinfo","misc"),
|
|
m_routeParams("")
|
|
{
|
|
Output("Loaded module FileInfo");
|
|
}
|
|
|
|
FileInfo::~FileInfo()
|
|
{
|
|
Output("Unloading module FileInfo");
|
|
}
|
|
|
|
// Utility used in FileInfo::initialize()
|
|
static inline const NamedList* getSafeSect(Configuration& cfg, const String& name)
|
|
{
|
|
NamedList* tmp = cfg.getSection(name);
|
|
if (tmp)
|
|
return tmp;
|
|
return &NamedList::empty();
|
|
}
|
|
|
|
void FileInfo::initialize()
|
|
{
|
|
static bool first = true;
|
|
Output("Initializing module FileInfo");
|
|
Configuration cfg(Engine::configFile(cfgFile));
|
|
const NamedList* callRoute = getSafeSect(cfg,YSTRING("call.route"));
|
|
if (first) {
|
|
first = false;
|
|
setup();
|
|
for (const TokenDict* d = s_msgHandler; d->token; d++) {
|
|
int prio = d->value;
|
|
if (d->value == FileInfoMsgHandler::CallRoute)
|
|
prio = callRoute->getIntValue("priority",prio);
|
|
if (prio < 0)
|
|
prio = 100;
|
|
FileInfoMsgHandler* h = new FileInfoMsgHandler(d->value,prio);
|
|
Engine::install(h);
|
|
}
|
|
}
|
|
lock();
|
|
m_sendTarget = callRoute->getValue(YSTRING("file_send_target"),"filetransfer/send/");
|
|
m_routeParams.clearParams();
|
|
if (callRoute->getBoolValue(YSTRING("set_default_params"),true)) {
|
|
m_routeParams.addParam("autoclose",String::boolText(true));
|
|
m_routeParams.addParam("wait_on_drop","10000");
|
|
}
|
|
m_routeParams.copySubParams(*callRoute,YSTRING("param_"),true);
|
|
unlock();
|
|
}
|
|
|
|
bool FileInfo::findAccount(const String& name, RefPointer<FIAccount>& acc, bool add)
|
|
{
|
|
if (!name)
|
|
return false;
|
|
Lock lck(this);
|
|
ObjList* o = m_accounts.find(name);
|
|
if (!o) {
|
|
if (!add)
|
|
return false;
|
|
FIAccount* a = new FIAccount(name);
|
|
o = m_accounts.append(a);
|
|
Debug(this,DebugInfo,"Added account '%s' (%p)",a->name().c_str(),a);
|
|
}
|
|
acc = static_cast<FIAccount*>(o->get());
|
|
return acc != 0;
|
|
}
|
|
|
|
bool FileInfo::removeAccount(FIAccount* acc, bool delObj)
|
|
{
|
|
if (!acc)
|
|
return false;
|
|
Lock lck(this);
|
|
GenObject* gen = m_accounts.remove(acc,false);
|
|
if (!gen)
|
|
return false;
|
|
lck.drop();
|
|
Debug(this,DebugInfo,"Removed account '%s' (%p) delObj=%u",acc->name().c_str(),acc,delObj);
|
|
if (delObj)
|
|
TelEngine::destruct(gen);
|
|
return true;
|
|
}
|
|
|
|
bool FileInfo::handleFileInfo(Message& msg)
|
|
{
|
|
const String& account = msg[YSTRING("account")];
|
|
if (!account)
|
|
return false;
|
|
bool set = false;
|
|
bool create = false;
|
|
bool remove = false;
|
|
bool query = false;
|
|
const String& oper = msg[YSTRING("operation")];
|
|
if (oper == YSTRING("set"))
|
|
create = set = true;
|
|
else if (oper == YSTRING("remove"))
|
|
remove = true;
|
|
else if (oper == YSTRING("query"))
|
|
query = true;
|
|
else
|
|
return false;
|
|
RefPointer<FIAccount> acc;
|
|
if (!findAccount(account,acc,create))
|
|
return false;
|
|
if (create) {
|
|
const String* canRoute = msg.getParam(YSTRING("canroute"));
|
|
if (canRoute)
|
|
acc->canRoute(canRoute->toBoolean());
|
|
}
|
|
bool ok = false;
|
|
while (true) {
|
|
if (set) {
|
|
ok = acc->handleFileInfoSet(msg);
|
|
break;
|
|
}
|
|
if (remove) {
|
|
const String& contact = msg[YSTRING("contact")];
|
|
if (contact)
|
|
ok = acc->handleFileInfoRemove(msg,contact);
|
|
else
|
|
removeAccount(acc);
|
|
break;
|
|
}
|
|
if (query) {
|
|
ok = acc->handleFileInfoQuery(msg);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
acc = 0;
|
|
return ok;
|
|
}
|
|
|
|
bool FileInfo::handleCallRoute(Message& msg)
|
|
{
|
|
if (msg[YSTRING("format")] != YSTRING("data"))
|
|
return false;
|
|
const String& oper = msg[YSTRING("operation")];
|
|
bool send = (oper == YSTRING("send"));
|
|
if (!send)
|
|
return false;
|
|
const String& account = msg[YSTRING("in_line")];
|
|
if (!account)
|
|
return false;
|
|
// Jingle puts caller party as 'callername'
|
|
const String* contact = 0;
|
|
if (msg[YSTRING("module")] == YSTRING("jingle"))
|
|
contact = msg.getParam(YSTRING("callername"));
|
|
else
|
|
contact = msg.getParam(YSTRING("caller"));
|
|
if (TelEngine::null(contact))
|
|
return false;
|
|
lock();
|
|
ObjList* o = m_accounts.find(account);
|
|
FIAccount* a = o ? static_cast<FIAccount*>(o->get()) : 0;
|
|
if (a && !(a->canRoute() && a->ref()))
|
|
a = 0;
|
|
unlock();
|
|
bool ok = a && a->route(msg,*contact);
|
|
TelEngine::destruct(a);
|
|
return ok;
|
|
}
|
|
|
|
bool FileInfo::received(Message& msg, int id)
|
|
{
|
|
return Module::received(msg,id);
|
|
}
|
|
|
|
}; // anonymous namespace
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|