# the library search path.
lib_LTLIBRARIES = libi2ncommon.la
-include_HEADERS = daemonfunc.hxx pidfile.hxx logread.hxx insocketstream.hxx oftmpstream.hxx pipestream.hxx filefunc.hxx stringfunc.hxx timefunc.hxx ipfunc.hxx ip_type.hxx
-libi2ncommon_la_SOURCES = daemonfunc.cpp pidfile.cpp logread.cpp oftmpstream.cpp ipfunc.cpp timefunc.cpp filefunc.cpp stringfunc.cpp
+include_HEADERS = userfunc.hxx daemonfunc.hxx pidfile.hxx logread.hxx insocketstream.hxx oftmpstream.hxx pipestream.hxx filefunc.hxx stringfunc.hxx timefunc.hxx ipfunc.hxx ip_type.hxx
+libi2ncommon_la_SOURCES = userfunc.cpp daemonfunc.cpp pidfile.cpp logread.cpp oftmpstream.cpp ipfunc.cpp timefunc.cpp filefunc.cpp stringfunc.cpp
# Note: If you specify a:b:c as the version in the next line,
# the library that is made has version (a-c).c.b. In this
#include <string>
#include <stdexcept>
#include "daemonfunc.hxx"
+#include "stringfunc.hxx"
+#include "filefunc.hxx"
+
+namespace i2n
+{
+namespace daemon
+{
using namespace std;
/**
* Fork into the background.
*/
-void daemon::daemonize()
+void daemonize()
{
int pid=fork();
}
/**
- * Lookup uid for given username
- * @param username username to convert
- * @return uid of the user
- */
-uid_t daemon::lookup_uid(const std::string &username)
-{
- struct passwd *user = getpwnam(username.c_str());
- if (user == NULL) {
- throw runtime_error("user " + username + " not found");
- }
-
- return user->pw_uid;
-}
-
-/**
- * Lookup gid for given group
- * @param group group to convert
- * @return gid of the group
- */
-gid_t daemon::lookup_gid(const std::string &group)
-{
- struct group *my_group = getgrnam(group.c_str());
- if (my_group == NULL) {
- throw runtime_error("group " + group + " not found");
- }
-
- return my_group->gr_gid;
-}
-
-/**
* Drop root privileges
* @param username User to become. Don't change user if empty
* @param group Group to become. Don't change group if empty
*/
-void daemon::drop_root_privileges(const std::string &username,
+void drop_root_privileges(const std::string &username,
const std::string &group)
{
+/*
if (!group.empty()) {
if (setgid(daemon::lookup_gid(group))) {
throw runtime_error("Can't change to group " + group);
throw runtime_error("Can't change to user " + username);
}
}
+*/
+}
+
+/**
+ * @brief determine the pids for a given program
+ * @param[in] name name (or full path) of the binary
+ * @param[out] result the pids associated with the name.
+ * @return @a true if the function performed without errors.
+ *
+ * Walk though the /proc/\<pid\>'s and search for the name.
+ *
+ * @note Since this function uses /proc, it's system specific. Currently:
+ * Linux only!
+ *
+ * @todo check cmdline and stat in /proc/\<pid\> dir for the searched name.
+ */
+bool pid_of(const std::string& name, std::vector< pid_t >& result)
+{
+ std::vector< std::string > entries;
+ std::vector< pid_t > fuzz1_result;
+ std::vector< pid_t > fuzz2_result;
+ result.clear();
+ if (!i2n::getDir("/proc", entries)) return false;
+ for(std::vector< std::string >::const_iterator it= entries.begin();
+ it != entries.end();
+ ++it)
+ {
+ pid_t pid;
+ if (! stringTo<pid_t>(*it, pid)) continue;
+ std::string base_path= std::string("/proc/") + *it;
+ std::string exe_path= base_path + "/exe";
+ Stat stat(exe_path, false);
+ if (not stat or not stat.isLink()) continue;
+ std::string real_exe= readLink(exe_path);
+ if (real_exe == name)
+ {
+ result.push_back( pid );
+ continue;
+ }
+
+ std::string proc_stat= readFile( base_path + "/stat");
+ if (proc_stat.empty()) continue; // process vanished
+
+ //TODO some more fuzz tests here?! (cmdline, stat(us))
+
+ if (basename(real_exe) == name)
+ {
+ fuzz2_result.push_back(pid);
+ continue;
+ }
+ }
+ if (result.empty())
+ {
+ result.swap(fuzz1_result);
+ }
+ if (result.empty())
+ {
+ result.swap(fuzz2_result);
+ }
+ return true;
+} // eo pidOf(const std::string&,std::vector< pid_t >&)
+
+}
}
#include <sys/types.h>
#include <string>
+#include <vector>
/// Collection of functions for daemons
-class daemon
+namespace i2n
{
-public:
- static void daemonize();
+namespace daemon
+{
+ void daemonize();
- static uid_t lookup_uid(const std::string &username);
- static gid_t lookup_gid(const std::string &group);
+ void drop_root_privileges(const std::string &username,
+ const std::string &group);
- static void drop_root_privileges(const std::string &username,
- const std::string &group);
-};
+ bool pid_of(const std::string& name, std::vector< pid_t >& result);
+}
+}
#endif
email : info@intra2net.com
***************************************************************************/
+#include <list>
#include <string>
+#include <fstream>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <unistd.h>
#include <errno.h>
-#include <filefunc.hxx>
+#include <boost/scoped_array.hpp>
+#include "filefunc.hxx"
+#include "stringfunc.hxx"
+
+namespace i2n
+{
using namespace std;
-bool file_exists (const string &name)
+/*
+** implementation of Stat
+*/
+
+Stat::Stat()
+: m_valid(false)
{
- struct stat statbuff;
+ clear();
+} // eo Stat::Stat()
+
+
+Stat::Stat(const std::string& path, bool follow_links)
+{
+ stat(path,follow_links);
+} // eo Stat::Stat(const std::string&,bool)
+
+
+Stat::~Stat()
+{
+} // eo Stat::~Stat()
+
+
+/**
+ * @brief updates the internal data.
+ *
+ * In other words: stat()'s the file again.
+ */
+void Stat::recheck()
+{
+ if (! m_path.empty())
+ {
+ stat(m_path, m_follow_links);
+ }
+} // eo Stat::recheck()
+
+
+/**
+ * @brief calls stat() or lstat() to get the information for the given path
+ * and stores that information.
+ * @param path the path which should be checked
+ * @param follow_links determine if (symbalic) links should be followed.
+ */
+void Stat::stat(const std::string& path, bool follow_links)
+{
+ clear();
+ m_path= path;
+ m_follow_links= follow_links;
+ struct stat stat_info;
+ int res;
+ res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
+ if ( 0 == res )
+ {
+ m_device = stat_info.st_dev;
+ m_inode = stat_info.st_ino;
+ m_mode = (stat_info.st_mode & ~(S_IFMT));
+ m_num_links = stat_info.st_nlink;
+ m_uid = stat_info.st_uid;
+ m_gid = stat_info.st_gid;
+ m_device_type = stat_info.st_rdev;
+ m_size = stat_info.st_size;
+ m_atime = stat_info.st_atime;
+ m_mtime = stat_info.st_mtime;
+ m_ctime = stat_info.st_atime;
+
+ m_is_link= S_ISLNK( stat_info.st_mode );
+ m_is_regular= S_ISREG( stat_info.st_mode );
+ m_is_directory= S_ISDIR( stat_info.st_mode );
+ m_is_character_device= S_ISCHR( stat_info.st_mode );
+ m_is_block_device= S_ISBLK( stat_info.st_mode );
+ m_is_fifo= S_ISFIFO( stat_info.st_mode );
+ m_is_socket= S_ISSOCK( stat_info.st_mode );
+ }
+ m_valid = (0 == res);
+} // eo Stat::stat(const std::string&,bool)
- if (stat(name.c_str(), &statbuff) < 0)
- return false;
- else
- return true;
-}
-long fsize (const string &name)
+/**
+ * @brief clears the internal data.
+ */
+void Stat::clear()
+{
+ m_path.clear();
+ m_valid= false;
+
+ m_device = 0;
+ m_inode = 0;
+ m_mode = 0;
+ m_num_links = 0;
+ m_uid = 0;
+ m_gid = 0;
+ m_device_type = 0;
+ m_size = 0;
+ m_atime = 0;
+ m_mtime = 0;
+ m_ctime = 0;
+
+ m_is_link= false;
+ m_is_regular= false;
+ m_is_directory= false;
+ m_is_character_device= false;
+ m_is_block_device= false;
+ m_is_fifo= false;
+ m_is_socket= false;
+} // eo Stat::clear()
+
+
+/**
+ * @brief checks if another instance describes the same file.
+ * @param rhs the other instance.
+ * @return @a true iff the other instance describes the same file.
+ * @note
+ * The "same file" means that the files are located on the same device and use the same inode.
+ * They might still have two different directory entries (different paths)!
+ */
+bool Stat::isSameAs(const Stat& rhs)
+{
+ return m_valid and rhs.m_valid
+ and ( m_device == rhs.m_device)
+ and ( m_inode == rhs.m_inode);
+} // eo Stat::isSameAs(const Stat& rhs);
+
+
+/**
+ * @brief checks if this and the other instance describe the same device.
+ * @param rhs the other instance.
+ * @return @a true if we and the other instance describe a device and the same device.
+ *
+ * "Same device" means that the devices have the same type and the same major and minor id.
+ */
+bool Stat::isSameDeviceAs(const Stat& rhs)
+{
+ return isDevice() and rhs.isDevice()
+ and ( m_is_block_device == rhs.m_is_block_device )
+ and ( m_is_character_device == rhs.m_is_character_device )
+ and ( m_device_type == rhs.m_device_type);
+} // eo Stat::isSameDeviceAs(const Stat&)
+
+/**
+ * @brief check existence of a path.
+ * @param path path which should be tested.
+ * @return @a true iff path exists.
+ */
+bool path_exists(const std::string& path)
+{
+ struct stat stat_info;
+ int res = ::stat(path.c_str(), &stat_info);
+ if (res) return false;
+ return true;
+} // eo path_exists(const std::string&)
+
+/**
+ * @brief check existence of a regular file.
+ * @param path path which should be tested.
+ * @return @a true if path exists and is a regular file (or a link pointing to a regular file).
+ * @note this checks for regular files; not for the pure exitsnace of a path; use pathExists() for that.
+ * @see pathExists
+ */
+bool file_exists(const std::string& path)
+{
+ struct stat stat_info;
+ int res = ::stat(path.c_str(), &stat_info);
+ if (res) return false;
+ return S_ISREG(stat_info.st_mode);
+} // eo file_exists(const std::string&)
+
+// TODO: Use Stat class
+/**
+ * Get size of file
+ * @param name filename to get size for
+ * @return file size or -1 if file does not exist
+ */
+long file_size (const string &name)
{
long iReturn = -1;
return iReturn;
}
-string load_file(const string &name)
+/**
+ * @brief tests the last modification time stamp of a path.
+ * @param path path which should be tested.
+ * @return the last modification time or 0 if the path doen't exist.
+ */
+time_t fileMTime(const std::string& path)
{
- FILE *f;
- string s;
+ struct stat stat_info;
+ int res = ::stat(path.c_str(), &stat_info);
+ if (res) return 0;
+ return stat_info.st_mtime;
+} // eo fileMTime(const std::string&)
- if (name.find ("..") != string::npos)
- return ("can't load file (..): " + name);
- f=::fopen(name.c_str(),"rb");
- if (!f)
+/**
+ * @brief reads the contents of a directory.
+ * @param path the path to the directory whose contents should be read.
+ * @param[out] result the resulting list of names.
+ * @param include_dot_names determines if dot-files should be included in the list.
+ * @return @a true if reading the directory was succesful, @a false on error.
+ */
+bool getDir(
+ const std::string& path,
+ std::vector< std::string >& result,
+ bool include_dot_names )
+{
+ DIR* dir = ::opendir( path.c_str());
+ if (!dir)
+ {
+ return false;
+ }
+ struct dirent *entry;
+ while ( NULL != (entry = ::readdir(dir)) )
{
- return ("");
+ std::string name( entry->d_name );
+ if (! include_dot_names && (name[0] == '.') )
+ {
+ continue;
+ }
+ result.push_back( name );
}
+ ::closedir(dir);
+ return true;
+} // eo getDir(const std::string&,std::vector< std::string >&,bool)
+
+
+/**
+ * @brief reads the contents of a directory
+ * @param path the path to the directory whose contents should be read.
+ * @param include_dot_names determines if dot-files should be included in the list.
+ * @return the list of names (empty on error).
+ */
+std::vector< std::string > getDir(const std::string& path, bool include_dot_names )
+{
+ std::vector< std::string > result;
+ getDir(path,result,include_dot_names);
+ return result;
+} // eo getDir(const std::string&,bool)
- ::fseek(f,0,SEEK_END);
- int size=::ftell(f);
- ::fseek(f,0,SEEK_SET);
- char *c=new char[size+1];
- ::fread(c,1,size,f);
- s.assign(c,size);
- delete[] c;
- ::fclose (f);
- return s;
-}
-bool chown(const char* file,const char* owner, const char* group)
+/**
+ * @brief removes a file from a filesystem.
+ * @param path path to the file.
+ * @return @a true iff the unlink was successful.
+ */
+bool unlink( const std::string& path )
{
- struct passwd *p;
- struct group *g;
- uid_t uid;
- gid_t gid;
+ int res = ::unlink( path.c_str() );
+ return (res == 0);
+} // eo unlink(const std::string&)
+
- p = getpwnam(owner);
- if (p == NULL)
- return false;
- uid=p->pw_uid;
- g = getgrnam(group);
- if (g == NULL)
+/**
+ * @brief creates a symbolic link named @a link_name to @a target.
+ * @param target the target the link should point to.
+ * @param link_name the name of the link.
+ * @param force if @a true, the (file or link) @a link_name is removed if it exists.
+ * @return @a true iff the symlink was successfully created.
+ */
+bool symlink(const std::string& target, const std::string& link_name, bool force)
+{
+ int res= -1;
+ if (target.empty() or link_name.empty() or target == link_name)
+ {
+ // no, we don't do this!
return false;
- gid=g->gr_gid;
+ }
+ std::string n_target;
+ if (target[0] == '/') // absolute target?
+ {
+ n_target= target;
+ }
+ else // relative target
+ {
+ // for stat'ing: prepend dir of link_name:
+ n_target= dirname(link_name)+"/"+ target;
+ }
+ Stat target_stat(n_target, false);
+ Stat link_name_stat(link_name, false);
+ if (target_stat.exists() && link_name_stat.exists())
+ {
+ if (link_name_stat.isSameAs(target_stat)
+ or link_name_stat.isSameDeviceAs(target_stat) )
+ {
+ return false;
+ }
+ //TODO: more consistency checks?!
+ }
+ if (link_name_stat.exists())
+ {
+ // the link name already exists.
+ if (force)
+ {
+ // "force" as given, so try to remove the link_name:
+ unlink(link_name);
+ // update the stat:
+ link_name_stat.recheck();
+ }
+ }
+ if (link_name_stat.exists())
+ {
+ // well, if the link_name still exists; we cannot create that link:
+ errno = EEXIST;
+ return false;
+ }
+ res= ::symlink(target.c_str(), link_name.c_str());
+ return (0 == res);
+} // eo symlink(const std::string&,const std::string&,bool)
+
- if (!::chown(file,uid,gid))
- return true;
+
+/**
+ * @brief reads the target of a symbolic link.
+ * @param path path to the symbolic link
+ * @return the target of the link or an empty string on error.
+ */
+std::string readLink(const std::string& path)
+{
+ errno= 0;
+ Stat stat(path,false);
+ if (!stat || !stat.isLink())
+ {
+ return std::string();
+ }
+ int buffer_size= PATH_MAX+1 + 128;
+ boost::scoped_array<char> buffer_ptr( new char[buffer_size] );
+ int res= ::readlink( path.c_str(), buffer_ptr.get(), buffer_size-1 );
+ if (res >= 0)
+ {
+ return std::string( buffer_ptr.get(), res );
+ }
+ return std::string();
+} // eo readLink(const std::string&)
+
+
+
+/**
+ * @brief returns content of a file as string.
+ *
+ * A simple (q'n'd) function for retrieving content of a file as string.<br>
+ * Also able to read special (but regular) files which don't provide a size when stat'ed
+ * (like files in the /proc filesystem).
+ *
+ * @param path path to the file.
+ * @return the content of the file as string (empty if file could be opened).
+ */
+std::string readFile(const std::string& path)
+{
+ Stat stat(path);
+ if (!stat.isReg())
+ {
+ return std::string();
+ }
+ std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
+ std::string result;
+ if (f.is_open())
+ {
+ // NOTE: there are cases where we don't know the correct size (/proc files...)
+ // therefore we only use the size for reserving space if we know it, but don't
+ // use it when reading the file!
+ if (stat.size() > 0)
+ {
+ // if we know the size, we reserve enough space.
+ result.reserve( stat.size() );
+ }
+ char buffer[2048];
+ while (f.good())
+ {
+ f.read(buffer, sizeof(buffer));
+ result.append(buffer, f.gcount());
+ }
+ }
+ return result;
+} // eo readFile
+
+
+/**
+ * @brief writes a string to a file.
+ * @param path path to the file
+ * @param data the data which should be written into the file
+ * @return @a true if the data was written to the file.
+ *
+ * A simple (q'n'd) function for writing a string to a file.
+ */
+bool writeFile(const std::string& path, const std::string& data)
+{
+ std::ofstream f( path.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
+ if (f.good())
+ {
+ f.write( data.data(), data.size() );
+ return f.good();
+ }
else
+ {
return false;
-}
+ }
+} // eo writeFile
+
+
+/**
+ * @brief returns the filename part of a path (last component)
+ * @param path the path.
+ * @return the last component of the path.
+ */
+std::string basename(const std::string& path)
+{
+ std::string::size_type pos= path.rfind('/');
+ if (pos != std::string::npos)
+ {
+ return path.substr(pos+1);
+ }
+ return path;
+} // eo basename(const std::string&)
+
+
+/**
+ * @brief returns the directory part of a path.
+ * @param path the path.
+ * @return the directory part of the path.
+ */
+std::string dirname(const std::string& path)
+{
+ std::string::size_type pos= path.rfind('/');
+ if (pos != std::string::npos)
+ {
+ std::string result(path,0,pos);
+ if (result.empty())
+ {
+ return ".";
+ }
+ return result;
+ }
+ return ".";
+} // eo dirname(const std::string&)
+
+
+/**
+ * @brief normalizes a path.
+ *
+ * This method removes empty and "." elements.
+ * It also resolves ".." parts by removing previous path elements if possible.
+ * Leading ".." elements are preserved when a relative path was given; else they are removed.
+ * Trailing slashes are removed.
+ *
+ * @param path the path which should be normalized.
+ * @return the normalized path.
+ */
+
+std::string normalizePath(const std::string& path)
+{
+ if (path.empty())
+ {
+ return std::string();
+ }
+ // remember if the given path was absolute since this information vanishes when
+ // we split the path (since we split with omitting empty parts...)
+ bool is_absolute= (path[0]=='/');
+ std::list< std::string > parts;
+ std::list< std::string > result_parts;
+
+ splitString(path,parts,"/",true);
+
+ for(std::list< std::string >::const_iterator it_parts= parts.begin();
+ it_parts != parts.end();
+ ++it_parts)
+ {
+ std::string part(*it_parts); //convenience..
+ if (part == std::string(".") )
+ {
+ // single dot is "current path"; ignore!
+ continue;
+ }
+ if (part == std::string("..") )
+ {
+ // double dot is "one part back"
+ if (result_parts.empty())
+ {
+ if (is_absolute)
+ {
+ // ignore since we cannot move behind / on absolute paths...
+ }
+ else
+ {
+ // on relative path, we need to store the "..":
+ result_parts.push_back(part);
+ }
+ }
+ else if (result_parts.back() == std::string("..") )
+ {
+ // if last element was already "..", we need to store the new one again...
+ // (PS: no need for "absolute" check; this can only be the case on relative path)
+ result_parts.push_back(part);
+ }
+ else
+ {
+ // remove last element.
+ result_parts.pop_back();
+ }
+ continue;
+ }
+ result_parts.push_back(part);
+ }
+ std::string result;
+ if (is_absolute)
+ {
+ result= "/";
+ }
+ result+= joinString(result_parts,"/");
+ return result;
+} // eo normalizePath(const std::string&)
+
+
+/**
+ * @brief changes the file(/path) mode.
+ * @param path the path to change the mode for.
+ * @param mode the new file mode.
+ * @return @a true iff the file mode was sucessfully changed.
+ */
+bool chmod(const std::string& path, int mode)
+{
+ int res= ::chmod(path.c_str(), mode);
+ return (res==0);
+} // eo chmod(const std::string&,int)
+
+
+/**
+ * @brief changed the owner of a file(/path)
+ * @param path the path to change the owner for.
+ * @param user the new file owner.
+ * @param group the new file group.
+ * @return @a true iff the file owner was succesfully changed.
+ *
+ * @note
+ * the validity of user and group within the system is not checked.
+ * This is intentional since this way we can use id's which are not assigned.
+ */
+bool chown(const std::string& path, const i2n::User& user, const i2n::Group& group)
+{
+ uid_t uid= user.uid;
+ if (uid<0) return false;
+ gid_t gid= group.gid;
+ if (gid<0) gid= user.gid;
+ if (gid<0) return false;
+ int res= ::chown( path.c_str(), uid, gid);
+ return (res==0);
+} // eo chown(const std::string&,const User&,const Group&)
/**
* Recursive delete of files and directories
return rtn;
}
+
+}
/***************************************************************************
- * Copyright (C) 2004 by Gerd v. Egidy *
+ * Copyright (C) 2004-2008 by Intra2net AG *
* info@intra2net.com *
* *
***************************************************************************/
#ifndef __FILEFUNC_HXX
#define __FILEFUNC_HXX
-bool file_exists (const std::string &name);
+#include "userfunc.hxx"
-long fsize (const std::string &name);
+namespace i2n
+{
-std::string load_file (const std::string &name);
+/**
+ * @brief helper class representing a file state.
+ *
+ * This basically wraps struct stat and provides some nicer access to the values.
+ */
+class Stat
+{
+ public:
+ Stat();
+ Stat(const std::string& path, bool follow_links= true);
+ ~Stat();
-bool chown(const char* file,const char* owner, const char* group);
+ void recheck();
+
+ bool isValid() const { return m_valid; }
+
+ bool exists() const { return m_valid; }
+ std::string path() const { return m_path; }
+
+ dev_t device() const { return m_device; }
+ ino_t inode() const { return m_inode; }
+ mode_t mode() const { return m_mode; }
+ nlink_t nlink() const { return m_num_links; }
+ uid_t uid() const { return m_uid; }
+ gid_t gid() const { return m_gid; }
+ dev_t rdev() const { return m_device_type; }
+ off_t size() const { return m_size; }
+ time_t atime() const { return m_atime; }
+ time_t mtime() const { return m_mtime; }
+ time_t ctime() const { return m_ctime; }
+
+ nlink_t numHardLinks() const { return m_num_links; }
+ dev_t deviceType() const { return m_device_type; }
+ time_t lastModifiedTime() const { return m_mtime; }
+ time_t createdTime() const { return m_ctime; }
+
+ /*
+ ** unix style like type queries:
+ */
+
+ bool isLnk() const { return m_is_link; }
+ bool isReg() const { return m_is_regular; }
+ bool isDir() const { return m_is_directory; }
+ bool isChr() const { return m_is_character_device; }
+ bool isBlk() const { return m_is_block_device; }
+ bool isFifo() const { return m_is_fifo; }
+ bool isSock() const { return m_is_socket; }
+
+ /*
+ ** readable style type queries:
+ */
+
+ bool isLink () const { return m_is_link; }
+ bool isRegular() const { return m_is_regular; }
+ bool isRegularFile() const { return m_is_regular; }
+ bool isDirectory() const { return m_is_directory; }
+ bool isCharacterDevice() const {return m_is_character_device; }
+ bool isBlockDevice() const { return m_is_block_device; }
+ bool isFIFO() const { return m_is_fifo; }
+ bool isSocket() const { return m_is_socket; }
+
+ bool isDevice() const { return m_is_character_device or m_is_block_device; }
+
+
+ /*
+ ** "high level" queries
+ */
+
+ bool isSameAs(const Stat& rhs);
+ bool isSameDeviceAs(const Stat& rhs);
+
+ /*
+ ** convenience methods
+ */
+
+ operator bool() const { return m_valid; }
+
+ protected:
+
+ void stat(const std::string& path, bool follow_links= true);
+ void clear();
+
+ protected:
+ std::string m_path;
+ bool m_follow_links;
+
+ bool m_valid;
+
+ dev_t m_device;
+ ino_t m_inode;
+ mode_t m_mode;
+ nlink_t m_num_links;
+ uid_t m_uid;
+ gid_t m_gid;
+ dev_t m_device_type;
+ off_t m_size;
+ time_t m_atime;
+ time_t m_mtime;
+ time_t m_ctime;
+
+ bool m_is_link : 1;
+ bool m_is_regular : 1;
+ bool m_is_directory : 1;
+ bool m_is_character_device : 1;
+ bool m_is_block_device : 1;
+ bool m_is_fifo : 1;
+ bool m_is_socket : 1;
+}; // eo class Stat
+
+/*
+** misc tool functions
+** (basically wrappers around system functions)
+*/
+
+bool path_exists(const std::string& path);
+bool file_exists(const std::string& path);
+long file_size (const std::string &name);
+
+time_t fileMTime(const std::string& path);
+
+bool getDir(const std::string& path, std::vector< std::string >& result, bool include_dot_names= false );
+std::vector< std::string > getDir(const std::string& path, bool include_dot_names= false );
+
+bool unlink(const std::string& path);
+
+bool symlink(const std::string& target, const std::string& link_name, bool force= false);
+
+std::string readLink(const std::string& path);
+
+
+std::string readFile(const std::string& path);
+
+bool writeFile(const std::string& path, const std::string& data);
+
+
+std::string basename(const std::string& path);
+
+std::string dirname(const std::string& path);
+
+std::string normalizePath(const std::string& path);
+
+bool chmod(const std::string& path, int mode);
+bool chown(const std::string& path, const i2n::User& user, const i2n::Group& group= i2n::Group());
bool recursive_delete(const std::string &path, std::string *error=NULL);
+}
+
#endif
-/***************************************************************************
- escape.cpp - escaping of strings
- -------------------
- begin : Sun Nov 14 1999
- copyright : (C) 1999 by Intra2net AG
- email : info@intra2net.com
- ***************************************************************************/
+/** @file
+ *
+ * (c) Copyright 2007-2008 by Intra2net AG
+ *
+ * info@intra2net.com
+ */
#include <iostream>
#include <string>
using namespace std;
+namespace i2n {
+
+
+namespace {
+
+const std::string hexDigitsLower("0123456789abcdef");
+const std::string hexDigitsUpper("0123456789ABCDEF");
+
+
+struct UpperFunc
+{
+ char operator() (char c)
+ {
+ return std::toupper(c);
+ }
+}; // eo struct UpperFunc
+
+
+struct LowerFunc
+{
+ char operator() (char c)
+ {
+ return std::tolower(c);
+ }
+}; // eo struct LowerFunc
+
+
+} // eo namespace <anonymous>
+
+
+
+/**
+ * default list of whitespaces (" \t\r\n");
+ */
+const std::string whitespaces = " \t\r\n";
+
+/**
+ * default list of lineendings ("\r\n");
+ */
+const std::string lineends= "\r\n";
+
+
+
+/**
+ * @brief checks if a string begins with a given prefix.
+ * @param[in,out] str the string which is tested
+ * @param prefix the prefix which should be tested for.
+ * @return @a true iff the prefix is not empty and the string begins with that prefix.
+ */
+bool hasPrefix(const std::string& str, const std::string& prefix)
+{
+ if (prefix.empty() || str.empty() || str.size() < prefix.size())
+ {
+ return false;
+ }
+ return str.compare(0, prefix.size(), prefix) == 0;
+} // eo hasPrefix(const std::string&,const std::string&)
+
+
+/**
+ * @brief checks if a string ends with a given suffix.
+ * @param[in,out] str the string which is tested
+ * @param suffix the suffix which should be tested for.
+ * @return @a true iff the suffix is not empty and the string ends with that suffix.
+ */
+bool hasSuffix(const std::string& str, const std::string& suffix)
+{
+ if (suffix.empty() || str.empty() || str.size() < suffix.size())
+ {
+ return false;
+ }
+ return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+} // eo hasSuffix(const std::string&,const std::string&)
+
+
+/**
+ * cut off characters from a given list from front and end of a string.
+ * @param[in,out] str the string which should be trimmed.
+ * @param charlist the list of characters to remove from beginning and end of string
+ * @return the result string.
+ */
+std::string trimMod(std::string& str, const std::string& charlist)
+{
+ // first: trim the beginning:
+ std::string::size_type pos= str.find_first_not_of(charlist);
+ if (pos == std::string::npos)
+ {
+ // whole string consists of charlist (or is already empty)
+ str.clear();
+ return str;
+ }
+ else if (pos>0)
+ {
+ // str starts with charlist
+ str.erase(0,pos);
+ }
+ // now let's look at the tail:
+ pos= str.find_last_not_of(charlist)+1; // note: we already know there is at least one other char!
+ if ( pos < str.size() )
+ {
+ str.erase(pos, str.size()-pos);
+ }
+ return str;
+} // eo trimMod(std::string&,const std::string&)
+
+
+
+/**
+ * removes last character from a string when it is in a list of chars to be removed.
+ * @param[in,out] str the string.
+ * @param what the list of chars which will be tested for.
+ * @return the resulting string with last char removed (if applicable)
+ */
+std::string chompMod(std::string& str, const std::string& what)
+{
+ if (str.empty() || what.empty())
+ {
+ return str;
+ }
+ if (what.find( str.at(str.size()-1) ) != std::string::npos)
+ {
+ str.erase( str.size() - 1);
+ }
+ return str;
+} // eo chompMod(std::string&,const std::string&)
+
+
+/**
+ * @brief converts a string to lower case.
+ * @param[in,out] str the string to modify.
+ * @return the string
+ */
+std::string lowerMod(std::string& str)
+{
+ std::transform( str.begin(), str.end(), str.begin(), LowerFunc() );
+ return str;
+} // eo lowerMod(std::string&)
+
+
+/**
+ * @brief converts a string to upper case.
+ * @param[in,out] str the string to modify.
+ * @return the string
+ */
+std::string upperMod(std::string& str)
+{
+ std::transform( str.begin(), str.end(), str.begin(), UpperFunc() );
+ return str;
+} // eo upperMod(std::string&)
+
+
+
+/**
+ * cut off characters from a given list from front and end of a string.
+ * @param str the string which should be trimmed.
+ * @param charlist the list of characters to remove from beginning and end of string
+ * @return the result string.
+ */
+std::string trim(const std::string& str, const std::string& charlist)
+{
+ // first: trim the beginning:
+ std::string::size_type pos0= str.find_first_not_of(charlist);
+ if (pos0 == std::string::npos)
+ {
+ // whole string consists of charlist (or is already empty)
+ return std::string();
+ }
+ // now let's look at the end:
+ std::string::size_type pos1= str.find_last_not_of(charlist);
+ return str.substr(pos0, pos1 - pos0 + 1);
+} // eo trim(const std:.string&,const std::string&)
+
+
+/**
+ * removes last character from a string when it is in a list of chars to be removed.
+ * @param str the string.
+ * @param what the list of chars which will be tested for.
+ * @return the resulting string with last char removed (if applicable)
+ */
+std::string chomp(const std::string& str, const std::string& what)
+{
+ if (str.empty() || what.empty())
+ {
+ return str;
+ }
+ if (what.find( str.at(str.size()-1) ) != std::string::npos)
+ {
+ return str.substr(0, str.size()-1);
+ }
+ return str;
+} // eo chomp(const std:.string&,const std::string&)
+
+
+/**
+ * @brief returns a lower case version of a given string.
+ * @param str the string
+ * @return the lower case version of the string
+ */
+std::string lower(const std::string& str)
+{
+ std::string result(str);
+ return lowerMod(result);
+} // eo lower(const std::string&)
+
+
+/**
+ * @brief returns a upper case version of a given string.
+ * @param str the string
+ * @return the upper case version of the string
+ */
+std::string upper(const std::string& str)
+{
+ std::string result(str);
+ return upperMod(result);
+} // eo upper(const std::string&)
+
+
+
+/**
+ * @brief removes a given suffix from a string.
+ * @param str the string.
+ * @param suffix the suffix which should be removed if the string ends with it.
+ * @return the string without the suffix.
+ *
+ * If the string ends with the suffix, it is removed. If the the string doesn't end
+ * with the suffix the original string is returned.
+ */
+std::string removeSuffix(const std::string& str, const std::string& suffix)
+{
+ if (hasSuffix(str,suffix))
+ {
+ return str.substr(0, str.size()-suffix.size() );
+ }
+ return str;
+} // eo removeSuffix(const std::string&,const std::string&)
+
+
+
+/**
+ * @brief removes a given prefix from a string.
+ * @param str the string.
+ * @param prefix the prefix which should be removed if the string begins with it.
+ * @return the string without the prefix.
+ *
+ * If the string begins with the prefix, it is removed. If the the string doesn't begin
+ * with the prefix the original string is returned.
+ */
+std::string removePrefix(const std::string& str, const std::string& prefix)
+{
+ if (hasPrefix(str,prefix))
+ {
+ return str.substr( prefix.size() );
+ }
+ return str;
+} // eo removePrefix(const std::string&,const std::string&)
+
+
+/**
+ * split a string to key and value delimited by a given delimiter.
+ * The resulting key and value strings are trimmed (whitespaces removed at beginning and end).
+ * @param str the string which should be splitted.
+ * @param[out] key the resulting key
+ * @param[out] value the resulting value
+ * @param delimiter the delimiter between key and value; default is '='.
+ * @return @a true if the split was successful.
+ */
+bool pairSplit(
+ const std::string& str,
+ std::string& key,
+ std::string& value,
+ char delimiter)
+{
+ std::string::size_type pos = str.find(delimiter);
+ if (pos == std::string::npos) return false;
+ key= str.substr(0,pos);
+ value= str.substr(pos+1);
+ trimMod(key);
+ trimMod(value);
+ return true;
+} // eo pairSplit(const std::string&,std::string&,std::string&,char)
+
+
+/**
+ * splits a string by given delimiter
+ *
+ * @param[in] str the string which should be splitted.
+ * @param[out] result the list resulting from splitting @a str.
+ * @param[in] delimiter the delimiter (word/phrase) at which @a str should be splitted.
+ * @param[in] omit_empty should empty parts not be stored?
+ * @param[in] trim_list list of characters the parts should be trimmed by.
+ * (empty string results in no trim)
+ */
+void splitString(
+ const std::string& str,
+ std::list<std::string>& result,
+ const std::string& delimiter,
+ bool omit_empty,
+ const std::string& trim_list
+)
+{
+ std::string::size_type pos, last_pos=0;
+ bool delimiter_found= false;
+ while ( last_pos < str.size() && last_pos != std::string::npos)
+ {
+ pos= str.find(delimiter, last_pos);
+ std::string part;
+ if (pos == std::string::npos)
+ {
+ part= str.substr(last_pos);
+ delimiter_found= false;
+ }
+ else
+ {
+ part= str.substr(last_pos, pos-last_pos);
+ delimiter_found=true;
+ }
+ if (pos != std::string::npos)
+ {
+ last_pos= pos+ delimiter.size();
+ }
+ else
+ {
+ last_pos= std::string::npos;
+ }
+ if (!trim_list.empty()) trimMod(part, trim_list);
+ if (omit_empty && part.empty()) continue;
+ result.push_back( part );
+ }
+ // if the string ends with a delimiter we need to append an empty string if no omit_empty
+ // was given.
+ // (this way we keep the split result consistent to a join operation)
+ if (delimiter_found && !omit_empty)
+ {
+ result.push_back("");
+ }
+} // eo splitString(const std::string&,std::list< std::string >&,const std::string&,bool,const std::string&)
+
+
+/**
+ * splits a string by a given delimiter
+ * @param str the string which should be splitted.
+ * @param delimiter delimiter the delimiter (word/phrase) at which @a str should be splitted.
+ * @param[in] omit_empty should empty parts not be stored?
+ * @param[in] trim_list list of characters the parts should be trimmed by.
+ * (empty string results in no trim)
+ * @return the list resulting from splitting @a str.
+ */
+std::list<std::string> splitString(
+ const std::string& str,
+ const std::string& delimiter,
+ bool omit_empty,
+ const std::string& trim_list
+)
+{
+ std::list<std::string> result;
+ splitString(str, result, delimiter, omit_empty, trim_list);
+ return result;
+} // eo splitString(const std::string&,const std::string&,bool,const std::string&)
+
+
+/**
+ * @brief joins a list of strings into a single string.
+ *
+ * This funtion is (basically) the reverse operation of @a splitString.
+ *
+ * @param parts the list of strings.
+ * @param delimiter the delimiter which is inserted between the strings.
+ * @return the joined string.
+ */
+std::string joinString(
+ const std::list< std::string >& parts,
+ const std::string& delimiter
+)
+{
+ std::string result;
+ if (! parts.empty())
+ {
+ std::list< std::string >::const_iterator it= parts.begin();
+ result = *it;
+ while( ++it != parts.end() )
+ {
+ result+= delimiter;
+ result+= *it;
+ }
+ }
+ return result;
+} // eo joinString(const std::list< std::string >&,const std::string&)
+
+
+
+/*
+** conversions
+*/
+
+
+/**
+ * @brief returns a hex string from a binary string.
+ * @param str the (binary) string
+ * @param upper_case_digits determine whether to use upper case characters for digits A-F.
+ * @return the string in hex notation.
+ */
+std::string binaryToHex(
+ const std::string& str,
+ bool upper_case_digits
+)
+{
+ std::string result;
+ std::string hexDigits( upper_case_digits ? hexDigitsUpper : hexDigitsLower);
+ for(std::string::const_iterator it= str.begin();
+ it != str.end();
+ ++it)
+ {
+ result.push_back( hexDigits[ ((*it) >> 4) & 0x0f ] );
+ result.push_back( hexDigits[ (*it) & 0x0f ] );
+ }
+ return result;
+} // eo binaryToHex(const std::string&,bool)
+
+
+/**
+ * @brief converts a hex digit string to binary string.
+ * @param str hex digit string
+ * @return the binary string.
+ *
+ * The hex digit string may contains white spaces or colons which are treated
+ * as delimiters between hex digit groups.
+ *
+ * @todo rework the handling of half nibbles (consistency)!
+ */
+std::string hexToBinary(
+ const std::string& str
+)
+throw(std::runtime_error)
+{
+ std::string result;
+ char c= 0;
+ bool hasNibble= false;
+ bool lastWasWS= true;
+ for(std::string::const_iterator it= str.begin();
+ it != str.end();
+ ++it)
+ {
+ std::string::size_type p = hexDigitsLower.find( *it );
+ if (p== std::string::npos)
+ {
+ p= hexDigitsUpper.find( *it );
+ }
+ if (p == std::string::npos)
+ {
+ if ( ( whitespaces.find( *it ) != std::string::npos) // is it a whitespace?
+ or ( *it == ':') // or a colon?
+ )
+ {
+ // we treat that as a valid delimiter:
+ if (hasNibble)
+ {
+ // 1 nibble before WS is treate as lower part:
+ result.push_back(c);
+ // reset state:
+ hasNibble= false;
+ }
+ lastWasWS= true;
+ continue;
+ }
+ }
+ if (p == std::string::npos )
+ {
+ throw runtime_error("illegal character in hex digit string: " + str);
+ }
+ lastWasWS= false;
+ if (hasNibble)
+ {
+ c<<=4;
+ }
+ else
+ {
+ c=0;
+ }
+ c+= (p & 0x0f);
+ if (hasNibble)
+ {
+ //we already had a nibble, so a char is complete now:
+ result.push_back( c );
+ hasNibble=false;
+ }
+ else
+ {
+ // this is the first nibble of a new char:
+ hasNibble=true;
+ }
+ }
+ if (hasNibble)
+ {
+ //well, there is one nibble left
+ // let's do some heuristics:
+ if (lastWasWS)
+ {
+ // if the preceeding character was a white space (or a colon)
+ // we treat the nibble as lower part:
+ //( this is consistent with shortened hex notations where leading zeros are not noted)
+ result.push_back( c );
+ }
+ else
+ {
+ // if it was part of a hex digit chain, we treat it as UPPER part (!!)
+ result.push_back( c << 4 );
+ }
+ }
+ return result;
+} // eo hexToBinary(const std::string&)
+
+
+} // eo namespace i2n
+
std::string iso_to_utf8(const std::string& isostring)
{
string result;
-/***************************************************************************
- * Copyright (C) 2004 by Gerd v. Egidy *
- * info@intra2net.com *
- * *
- ***************************************************************************/
+/** @file
+ * @brief collection of string tools (/ functions).
+ *
+ * contains a collection of miscellaneous functions for dealing with strings.
+ *
+ * some functions (like trim, lower case, upper case ...) are available in two versions:
+ * - a modifying one (suffix "Mod") which modifies the given string argument in place.
+ * - a non modifying one which take a constant string reference and returns a new string.
+ * .
+ *
+ *
+ * (c) Copyright 2007-2008 by Intra2net AG
+ *
+ * info@intra2net.com
+ */
#ifndef __STRINGFUNC_HXX
#define __STRINGFUNC_HXX
+#include <list>
#include <string>
+#include <sstream>
+#include <stdexcept>
+
+namespace i2n {
+
+/*
+** some useful constants:
+*/
+
+extern const std::string whitespaces;
+extern const std::string lineends;
+
+
+/*
+** predicates:
+*/
+
+
+bool hasPrefix(const std::string& str, const std::string& prefix);
+
+bool hasSuffix(const std::string& str, const std::string& suffix);
+
+
+/*
+** tool functions (modifying):
+*/
+
+std::string trimMod(std::string& str, const std::string& charlist = whitespaces);
+
+std::string chompMod(std::string& str, const std::string& what= lineends );
+
+std::string lowerMod(std::string& str);
+
+std::string upperMod(std::string& str);
+
+
+/*
+** tool functions (not modifying):
+*/
+
+std::string trim(const std::string& str, const std::string& charlist = whitespaces);
+
+std::string chomp(const std::string& str, const std::string& what= lineends );
+
+std::string lower(const std::string& str);
+
+std::string upper(const std::string& str);
+
+
+std::string removeSuffix(const std::string& str, const std::string& suffix);
+
+std::string removePrefix(const std::string& str, const std::string& prefix);
+
+
+
+/*
+** split and join:
+*/
+
+
+bool pairSplit(
+ const std::string& str,
+ std::string& key,
+ std::string& value,
+ char delimiter = '=');
+
+
+void splitString(
+ const std::string& str,
+ std::list< std::string >& result,
+ const std::string& delimiter= "\n",
+ bool omit_empty= false,
+ const std::string& trim_list= std::string()
+);
+
+std::list< std::string > splitString(
+ const std::string& str,
+ const std::string& delimiter = "\n",
+ bool omit_empty= false,
+ const std::string& trim_list= std::string()
+);
+
+
+std::string joinString(
+ const std::list< std::string >& parts,
+ const std::string& delimiter = "\n"
+);
+
+
+/*
+** conversions:
+*/
+
+
+std::string binaryToHex(const std::string&str, bool upper_case_digits= false);
+
+std::string hexToBinary(const std::string& str) throw(std::runtime_error);
+
+
+
+/*
+** "type conversions":
+*/
+
+
+/**
+ * convert a datatype @a T to a string via string stream.
+ *
+ * @param s the string which should be converted to @a T.
+ * @return the value of type T.
+ */
+template<
+ class T
+>
+T stringTo(const std::string& s)
+{
+ std::istringstream istr(s);
+ T result;
+ istr >> result;
+ return result;
+} // eo stringTo(const std::string&)
+
+
+/**
+ * convert a datatype @a T to a string via string stream.
+ *
+ * @param s the string which should be converted to @a T.
+ * @param result the resulting value of type @a T.
+ * @return @a true iff the internal string stream was EOF after the conversion.
+ */
+template<
+ class T
+>
+bool stringTo(const std::string& s, T& result)
+{
+ std::istringstream istr(s);
+ istr >> result;
+ return istr.eof();
+} // eo stringTo(const std::string&)
+
+
+/**
+ * convert a string to another datatype @a T via string stream.
+ *
+ * @param v the value (of type @a T) which should be converted to a string.
+ * @return the resulting string.
+ */
+template<
+ class T
+>
+std::string toString(const T& v)
+{
+ std::ostringstream ostr;
+ ostr << v;
+ return ostr.str();
+} // eo toString(const T&)
+
+} // eo namespace i2n
std::string to_lower (const std::string &src);
std::string to_upper (const std::string &src);