jBackup : a very simple backup system

Download jbackup.zip

Synopsis:

jbackupexcludes.txt
jBackup.cfg
jbackup.cpp
App.cpp
App.h
BackupDb.cpp
BackupDb.h
bigint.h
bigint.cpp
Config.cpp
Config.h
DirItem.cpp
DirItem.h
DirScanner.cpp
DirScanner.h
DirTree.cpp
DirTree.h
DirTreeIterator.cpp
DirTreeIterator.h
EMailer.cpp
EMailer.h
FileLineReader.cpp
FileLineReader.h
FileNameMatcher.cpp
FileNameMatcher.h
ItemBackup.cpp
ItemBackup.h
Options.cpp
Options.h
smtp.cpp
Smtp.h
SmtpException.h
SmtpHostInfo.h
SmtpWriter.h
SocketT.h
Statistics.cpp
Statistics.h
Trace.h
WSAInit.h


jbackupexcludes.txt

Synopsis
\cookies\
\history.ie5\
\content.ie5\
\debug\
\release\
\local settings\temp\
\crypto\rsa\machinekeys\
.obj
.res
.pch
.ilk
.sbr
.pdb
.idb
.suo
.ncb
.class
ntuser.dat
ntuser.dat.log
usrclass.dat
usrclass.dat.log


jBackup.cfg

Synopsis
; Note:
;   - do not put a space before or after the '=' sign
;   - do not put leading or trailing blanks
;----------------------------------------------------------
; the root directory where backups are going to be stored
; the structure is
;     \backuproot\
;                \computername1 ; all backups from the computer whose name is 'computername1'
;                              \c    ; all C: drive backups
;                               c\program files ; etc.   
;                              \d    ; all D: drive backups
;     \backuproot\computername2 ; all backups from the computer whose name is 'computername2'
backuproot=c:\backups

; if mirrored is true, then if a file is deleted, it is also deleted from the backup. The backup directory is 
; thus a "mirror" of the original directories
; Only the backuproot directory is mirrored, so if versions > 0, any deleted files will still exist in 
; previous version backup directories (e.g. c:\backups1, c:\backups2, etc.)
mirrored=true

; number of versions
; comment out this line by using a semicolon if you do not want multiple versions of your backed up files
; the backup directories will be a concatenation of the directory named in backuproot and single digits,
; e.g. if backuproot is c:\backups then
;     c:\backups    contains the most recent copy of all files
;     c:\backups1   will contain the previous version of all recently backed up files
;     c:\backups2   will contain two versions back
;
; if the backup database *does not* exist and the backuproot dir *does* exist, then
; the files are not rolled.
versions=2

; the name of the file that holds exclusions
excludefilename=jbackupexcludes.txt

; the database file name, holds all current information about the files
backupdbfilename=jbackup.db

; your SMTP server address
smtpserveraddress=your.smtp.server.address

; the email address you want reports sent to
; note emails are only sent if there is a failed copy!
toaddress=you@your.domain.com
; the email address you want reports to be from
; this can be the same as toaddress
fromaddress=you@your.domain.com

; a list of fully-qualified UNC style directories

;\\yourcomputer\c\some directory
\\yourcomputer\c\Documents and Settings
\\yourcomputer\c\program files

jbackup.cpp

Synopsis
#include "App.h"
#include "Statistics.h"

int main()
  {
  App app;
  gStats.Start();
  app.Run();
  gStats.End();
  gStats.Report();
  return 0;
  }

App.cpp

Synopsis
#include <string>
#include <iostream>
using namespace std;

#include "App.h"
#include "DirItem.h"
#include "DirTree.h"
#include "BackupDb.h"
#include "Config.h"
#include "ItemBackup.h"
#include "FileNameMatcher.h"
#include "Options.h"
#include "Statistics.h"

class App::Private
  {
  public:
    Private()
      : mDb()
      {
      }

    FileNameMatcher mIgnoreList;
    BackupDB mDb;
    ItemBackup mFileBackup;
  } ;

App::App()
: impl(* new App::Private() )
  {
  }
App::~App()
  {
  delete &impl;
  }
void App::Run()
  {
  Config cfg;
  cfg.Load();
  Init();

  gStats.StartScan();
  for(Config::iterator it = cfg.begin(); it != cfg.end(); ++it)
    {
    cout << "Processing directory: " << *it << "\n";
    ScanTree(*it);
    }
  gStats.EndScan();

  Term();
  }
void App::Init()
  {
  impl.mDb.SetDbDir();
  impl.mFileBackup.SetRoot(gOptions.BackupRoot);
  impl.mIgnoreList.AddPatternsFrom(gOptions.ExcludeFileName);
  gStats.StartLoad();
  impl.mDb.Load();
  gStats.EndLoad();
  }
void App::Term()
  {
  gStats.StartSave();
  impl.mDb.Save();
  gStats.EndSave();
  }

class MyDelegate : public BackupDB::DeleteFileDelegate
  {
  public:
    explicit MyDelegate(ItemBackup& itembackup)
      : mFileBackup(itembackup)
      {
      }
    void Handle(const DirItem& item)
      {
      gStats.IncNumDeletedFiles();
      mFileBackup.Delete(item);
      }
  private:
    ItemBackup& mFileBackup;
  };

void App::ScanTree(const string& dir)
  {
  DirTree tree(dir);
  for (DirTreeIterator it = tree.begin(); it != tree.end(); ++it)
    {
    gStats.IncNumFiles();
    BackupDB::EChangeType changetype = impl.mDb.HasChanged(*it);
    if (changetype == BackupDB::eNoChange) 
      gStats.IncNumUnchangedFiles();
    else if (impl.mIgnoreList.IsMatch((*it).Path())) 
      gStats.IncNumIgnoredFiles();
    else
      {
      if (changetype == BackupDB::eNewFile)
        gStats.IncNumNewFiles();
      CopyFile(*it);
      }
    }

  if (gOptions.IsMirroredBackup)
    {
    MyDelegate delegate(impl.mFileBackup);
    impl.mDb.HandleDeletedFiles(delegate);
    }
  }
void App::CopyFile(const DirItem& item)
  {
  cout << item << "\n";
  if (impl.mFileBackup.Copy(item))
    {
    gStats.IncNumCopiedFiles();
    impl.mDb.Add(item);
    }
  else
    {
    gStats.IncNumFailedCopyFiles();
    } 
  }

App.h

Synopsis
#pragma once

#include <string>
using std::string;

class DirItem;
class App
  {
  public:
    App();
    ~App();
    void Run();
    void Init();
    void Term();
    void ScanTree(const string& dir);
    void CopyFile(const DirItem& item);
  private:
    class Private;
    Private& impl;
  } ;

BackupDb.cpp

Synopsis
#include <map>
#include <fstream>
#include <iomanip>
#include <string>
using namespace std;

#include "bigint.h"
#include "BackupDb.h"
#include "DirItem.h"
#include "Options.h"
#include "Statistics.h"

typedef map<string, DirItem> DbType;    
class BackupDB::Private
  {
  public:
    Private()
      : mHasChanged(false)
      {
      }
  DbType mDb;
  string mDir;
  string mPath;
  bool mHasChanged;
  } ;

BackupDB::BackupDB()
: impl(* new BackupDB::Private())
  {
  }
void BackupDB::SetDbDir(const string& dir)
  {
  impl.mDir = dir;
  impl.mPath = impl.mDir + "\\" + gOptions.BackupDbFileName;
  }
long BackupDB::size()
  {
  return impl.mDb.size();
  }
void BackupDB::Save()
  {
  if (!impl.mHasChanged)
    return;

  ofstream ofs(impl.mPath.c_str());
  for (DbType::iterator it = impl.mDb.begin(); it != impl.mDb.end(); ++it)
    {
    if (gOptions.IsMirroredBackup && !(*it).second.IsMarked()) continue;
    (*it).second.SaveTo(ofs);
    }
  ofs.close();
  }
void BackupDB::Load()
  {
  ifstream ifs(impl.mPath.c_str());
  while (!ifs.eof() && !ifs.fail())
    {
    DirItem item = DirItem::LoadFrom(ifs);
    if (item == DirItem::Null) continue;
    Load(item);
    }
  ifs.close();
  }
void BackupDB::Load(const DirItem& item)
  {
  string key = item.Path();
  impl.mDb[key] = item;
  impl.mHasChanged = true;
  }
void BackupDB::Add(const DirItem& item)
  {
  Load(item);
  if (gOptions.IsMirroredBackup)
    impl.mDb[item.Path()].MarkIt();
  }
BackupDB::EChangeType BackupDB::HasChanged(const DirItem& item)
  {
  string key = item.Path();
  DbType::iterator it = impl.mDb.find(key);
  if (it == impl.mDb.end()) return eNewFile;
  if (gOptions.IsMirroredBackup)
    (*it).second.MarkIt();
  return (*it).second.HasChanged(item) ? eChangedFile : eNoChange;
  }

void BackupDB::HandleDeletedFiles(BackupDB::DeleteFileDelegate& delegate)
  {
  if (!gOptions.IsMirroredBackup) return;

  for (DbType::iterator it = impl.mDb.begin(); it != impl.mDb.end(); ++it)
    {
    if ((*it).second.IsMarked()) continue;
    impl.mHasChanged = true;
    delegate.Handle((*it).second);
    }
  }

BackupDb.h

Synopsis
#pragma once
using std::string;

class DirItem;
class BackupDB
  {
  public:
    BackupDB();
    void SetDbDir(const string& dir = ".");
    long size();
    void Save();
    void Load();
    void Add(const DirItem& item);
    enum EChangeType {eNewFile, eChangedFile, eNoChange};
    EChangeType HasChanged(const DirItem& item);

    class DeleteFileDelegate
      {
      public:
        virtual void Handle(const DirItem& item) = 0;
        virtual ~DeleteFileDelegate() {}
      } ;
    void HandleDeletedFiles(DeleteFileDelegate& dfd);
  private:
    void Load(const DirItem& item);
    class Private;
    Private& impl;
  } ;

bigint.h

Synopsis
#pragma once
#include <iostream>
using std::ostream;
using std::istream;

union bigint
  {
  struct 
    {
    unsigned long lo;
    unsigned long hi;
    };
  long long all;

  bigint();
  bigint(const long long& i);
  const bigint& operator =(const long long & i);
  } ;

ostream& operator <<(ostream& os, const bigint& i);
istream& operator >>(istream& is, bigint& i);

bigint.cpp

Synopsis
#include <iostream>
using namespace std;

#include "bigint.h"

ostream& operator <<(ostream& os, const bigint& i)
  {
  os << i.hi << ' ' << i.lo;
  return os;
  }

istream& operator >>(istream& is, bigint& i)
  {
  is >> i.hi >> i.lo;
  return is;
  }
bigint::bigint()
: all(0)
  {
  }
bigint::bigint(const long long& i)
: all(i)
  {
  }
const bigint& bigint::operator =(const long long& i)
  {
  all = i;
  return *this;
  }

Config.cpp

Synopsis
#include <list>
#include <string>
#include <algorithm>
using namespace std;

#include "Config.h"
#include "FileLineReader.h"
#include "Options.h"

Config::iterator::iterator()
  {
  }
Config::iterator::iterator(const StringList::iterator& otherit)
: mIt(otherit)
  {
  }
bool Config::iterator::operator !=(const iterator& other) const
  {
  return !operator ==(other);
  }
bool Config::iterator::operator ==(const iterator& other) const
  {
  return mIt == other.mIt;
  }
void Config::iterator::operator ++()
  {
  ++mIt;
  }
string Config::iterator::operator*() const
  {
  return *mIt;
  }
class Config::Private
  {
  public:
    Private(const string& dir)
      :  mLoaded(false)
      {
      mCfgPath = dir + "\\" + gOptions.BackupCfgFileName;
      }
    bool CheckPreviousLoad()
      {
      if (mLoaded) return true;
      mLoaded = true;
      return false;
      }
    void Add(const string& file)
      {
      if (file.empty()) return;
      mList.push_back(file);
      }

    string mCfgPath;
    StringList mList;
  private:
    bool mLoaded;
  } ;

Config::Config(const string& dir)
: impl(* new Config::Private(dir))
  {
  }
Config::~Config()
  {
  delete &impl;
  }
Config::iterator Config::begin()
  {
  return Config::iterator(impl.mList.begin());
  }
Config::iterator Config::end()
  {
  return Config::iterator(impl.mList.end());
  }
void Config::Load()
  {
  if (impl.CheckPreviousLoad()) return;

  const string cBackupRoot = "backuproot=";
  const string cExcludeFileName = "excludefilename=";
  const string cBackupDbFileName = "backupdbfilename=";
  const string cSmtpServerAddress = "smtpserveraddress=";
  const string cToAddress = "toaddress=";
  const string cFromAddress = "fromaddress=";
  const string cIsMirroredBackup = "mirrored=";
  const string cVersions= "versions=";

  FileLineReader reader(impl.mCfgPath);
  while (reader.IsOk())
    {
    string line = reader.GetLine();
    if (line[0] == ';') continue;
    if (line.empty()) continue;

    if (line.find(cBackupRoot) == 0)
      gOptions.BackupRoot = line.substr(cBackupRoot.length());
    else if (line.find(cExcludeFileName) == 0)
      gOptions.ExcludeFileName = line.substr(cExcludeFileName.length());
    else if (line.find(cBackupDbFileName) == 0)
      gOptions.BackupDbFileName = line.substr(cBackupDbFileName.length());
    else if (line.find(cSmtpServerAddress) == 0)
      gOptions.SmtpServerAddress = line.substr(cSmtpServerAddress.length());
    else if (line.find(cToAddress) == 0)
      gOptions.ToAddress = line.substr(cToAddress.length());
    else if (line.find(cFromAddress) == 0)
      gOptions.FromAddress = line.substr(cFromAddress.length());
    else if (line.find(cIsMirroredBackup) == 0)
      {
      string s = line.substr(cIsMirroredBackup.length());
      transform(s.begin(), s.end(), s.begin(), tolower);
      gOptions.IsMirroredBackup = (s == "true");
      }
    else if (line.find(cVersions) == 0)
      {
      string s = line.substr(cVersions.length());
      gOptions.NumVersions = atoi(s.c_str());
      }
    else
      impl.Add(line);
    }
  }

Config.h

Synopsis
#pragma once

#include <list>
#include <string>
using std::string;
using std::list;

typedef list<string> StringList;
class Config
  {
  public:
    class iterator
      {
      public:
        iterator();
        explicit iterator(const StringList::iterator& otherit);
        bool operator !=(const iterator& other) const;
        bool operator ==(const iterator& other) const;
        void operator ++();
        string operator*() const;
      private:
        friend class Config;
        StringList::iterator mIt;
      } ;

    explicit Config(const string& dir = ".");
    ~Config();
    iterator begin();
    iterator end();
    void Load();

  private:
    class Private;
    Private& impl;
  } ;

DirItem.cpp

Synopsis
#include <algorithm>
#include <string>
using std::transform;
using std::getline;
#include "DirItem.h"
#include "bigint.h"

DirItem DirItem::Null = DirItem(string(), string(), false, 0, 0);

ostream& operator<< (ostream& os, const DirItem& item)
  {
  os << item.mPath << " type=" << (item.mIsDir ? "D" : "F") << " size=" << (bigint) item.mFileSize << " date=" << (bigint) item.mFileDate;
  return os;
  }

DirItem DirItem::MakeDir(const string& dir)
  {
  return DirItem(dir, "", true, 0, 0);
  }
DirItem DirItem::MakeFile(const string& dir, const string& name, long long size, long long date)
  {
  return DirItem(dir, name, false, size, date);
  }
DirItem::DirItem()
  {
  Init("", "", false, 0, 0);
  }
DirItem::DirItem(const string& dir, const string& name, bool isdir, long long size, long long date)
  {
  Init(dir, name, isdir, size, date);
  }
DirItem::DirItem(const DirItem& other)
  {
  Init(other.mDir, other.mName, other.mIsDir, other.mFileSize, other.mFileDate);
  }
void DirItem::Init(const string& dir, const string& name, bool isdir, long long size, long long date)
  {
  mIsDir = isdir;
  mFileSize = size;
  mFileDate = date;
  mDir = dir;
  mMarker = false;
  transform(mDir.begin(), mDir.end(), mDir.begin(), tolower);
  mName = isdir ? "" : name;
  transform(mName.begin(), mName.end(), mName.begin(), tolower);
  mPath = mName.empty() ? mDir : mDir + "\\" + mName;
  }
bool DirItem::IsEmpty() const
  {
  return mPath == "";
  }
bool DirItem::HasChanged(const DirItem& other) const
  {
  if (operator !=(other)) return true;
  return ( mFileSize != other.mFileSize || mFileDate != other.mFileDate);
  }
void DirItem::MarkIt()
  {
  mMarker = true;
  }
bool DirItem::IsMarked() const
  {
  return mMarker;
  }
void DirItem::SaveTo(ostream& ofs)
  {
  //assume ofs is open
  ofs << mDir << "\n" 
    << mName << "\n" 
    << (mIsDir ? "D" : "F") << "\n"
    << (bigint) mFileSize << "\n" 
    << (bigint) mFileDate << "\n";
  }
DirItem DirItem::LoadFrom(istream& ifs)
  {
  string dir;
  string file;
  string isdir;
  bigint size;
  bigint  date;
  string newline;
  getline(ifs, dir); //don't skip whitespace
  getline(ifs, file);
  ifs >> isdir
      >> size
      >> date;
  getline(ifs, newline);
  if (ifs.fail()) return Null;
  DirItem item = isdir == "F" ? DirItem::MakeFile(dir, file, size.all, date.all) : DirItem::MakeDir(dir);
  return item;
  }
bool DirItem::operator ==(const DirItem& other) const
  {
  return mIsDir == other.mIsDir && mPath == other.mPath;
  }
bool DirItem::operator !=(const DirItem& other) const
  {
  return !operator==(other);
  }
const string DirItem::Path() const 
  {
  return mPath; 
  }
bool DirItem::IsDir() const
  {
  return mIsDir;
  }

//used only by UTs
long long DirItem::FileDate() const
  { 
  return mFileDate; 
  }
long long DirItem::FileSize() const
  { 
  return mFileSize; 
  }
const string DirItem::Name() const
  { 
  return mName; 
  }

DirItem.h

Synopsis
#pragma once

#include <string>
#include <iostream>
using std::string;
using std::ostream;
using std::istream;

class DirItem
  {
  public:
    static DirItem Null;
    static DirItem MakeDir(const string& dir);
    static DirItem MakeFile(const string& dir, const string& name, long long size, long long date);
    void SaveTo(ostream& ofs);
    static DirItem LoadFrom(istream& ifs);

    DirItem();
    DirItem(const DirItem& other);
    bool IsEmpty() const;
    bool HasChanged(const DirItem& other) const;
    void MarkIt();
    bool IsMarked() const;

    bool operator ==(const DirItem& other) const;
    bool operator !=(const DirItem& other) const;
    const string Path() const;
    bool IsDir() const;
    
  public: //used only by UTs
    const string Name() const;
    long long FileSize() const;
    long long FileDate() const;
  private:
    DirItem(const string& dir, const string& name, bool isdir, long long size, long long date);
    void Init(const string& dir, const string& name, bool isdir, long long size, long long date);
    string mName;
    string mDir;
    string mPath;
    bool mIsDir;
    bool mIsReadOnly;
    long long mFileSize;
    long long mFileDate;
    bool mMarker;

    friend ostream& operator<< (ostream& os, const DirItem& item);
  } ;

ostream& operator<< (ostream& os, const DirItem& item);



DirScanner.cpp

Synopsis
#include "bigint.h"
#include "DirScanner.h"

#ifdef WIN32

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

class DirScanner::Private
  {
  public:
    static DWORD cFileAttribute;
    static DWORD cDirAttribute;
    static string cSeparator;

    Private(const string& path)
      : mPath(path), mHandle(INVALID_HANDLE_VALUE)
      {
      }

    bool isValid()
      { 
      return mHandle != INVALID_HANDLE_VALUE;
      }

    void Close()
      {
      ::FindClose(mHandle);
      }

    void ResetHandle()
      {
      mHandle = INVALID_HANDLE_VALUE;
      }

    string getFileName()
      {    
      return mFileData.cFileName;
      }

    bool IsDir()
      {
      return (mFileData.dwFileAttributes & cDirAttribute) != 0;
      }
    bool IsFile()
      {
      return (mFileData.dwFileAttributes & cFileAttribute) != 0;
      }

    void getFileSize(bigint& size)
      {
      size.lo = mFileData.nFileSizeLow;
      size.hi = mFileData.nFileSizeHigh;
      }
    void getFileDate(bigint& date)
      {
      date.lo = mFileData.ftLastWriteTime.dwLowDateTime;
      date.hi = mFileData.ftLastWriteTime.dwHighDateTime;
      }
    bool getFirstFile()
      {
      string fspec = mPath + "\\*.*";
      mHandle = ::FindFirstFile(fspec.c_str(), &mFileData);
      return isValid();
      }

    bool getNextFile()
      {
      return ::FindNextFile(mHandle, &mFileData) == TRUE;
      }

    string mPath;
    WIN32_FIND_DATA mFileData;
    HANDLE mHandle;
  } ;

DWORD DirScanner::Private::cFileAttribute = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE;
DWORD DirScanner::Private::cDirAttribute =  FILE_ATTRIBUTE_DIRECTORY;
string DirScanner::Private::cSeparator = "\\";

#else 

#include <boost/filesystem/operations.hpp>
namespace fs = boost::filesystem;

class DirScanner::Private
  {
  public:
    static string cSeparator;

    Private(const string& path)
      :  mPath(path), itr(path)
      {
      }
    bool isValid()
      { 
      return itr != end_itr;
      }
    void Close()
      {
      }
    void ResetHandle()
      {
      }
    string getFileName()
      {
      return (*itr).leaf();
      }
    bool IsDir()
      {
      return fs::is_directory(*itr);
      }
    bool IsFile()
      {
      return !fs::is_directory(*itr);
      }
    void getFileSize(bigint& size)
      {
	//size.lo = mFileData.nFileSizeLow;
	//size.hi = mFileData.nFileSizeHigh;
      boost::intmax_t sz = fs::file_size(*itr);
      size.all = sz;
      }
    void getFileDate(bigint& date)
      {
	//date.lo = mFileData.ftLastWriteTime.dwLowDateTime;
	//date.hi = mFileData.ftLastWriteTime.dwHighDateTime;
      std::time_t t = fs::last_write_time(*itr);
      date.all = t;
      }
    bool getFirstFile()
      {
      itr = fs::directory_iterator(mPath);
      return isValid();
      }
    bool getNextFile()
      {
      ++itr;
      return isValid();
      }
    string mPath;
    fs::directory_iterator itr;
    fs::directory_iterator end_itr;
  } ;

string DirScanner::Private::cSeparator = "/";

#endif


//------------------------------------------------------
DirScanner::DirScanner(const string& path)
: impl(* new DirScanner::Private(path))
  {
  }
DirScanner::~DirScanner()
  {
  close();
  delete &impl;
  }
bool DirScanner::more()
  {
    return (!impl.isValid()) ? first() : next();
  }
DirItem DirScanner::getItem()
  {
  return getIsDir() ? 
        DirItem::MakeDir(impl.mPath + impl.cSeparator + getName()) 
      : 
        DirItem::MakeFile(impl.mPath, getName(), getFileSize(), getFileDate());
  }
bool DirScanner::isDotFiles()
  {
  return getName() == "." || getName() == "..";
  }
void DirScanner::close()
  {
  if (impl.isValid())
    impl.Close();
  impl.ResetHandle();
  }
string DirScanner::getName()
  {
  return impl.getFileName();
  }
bool DirScanner::getIsDir()
  {
  return impl.IsDir();
  }
bool DirScanner::getIsFile()
  {
  return impl.IsFile();
  }
long long DirScanner::getFileSize()
  {
  bigint size;
  impl.getFileSize(size);
  return size.all;
  }
long long DirScanner::getFileDate()
  {
  bigint date;
  impl.getFileDate(date);
  return date.all;
  }
bool DirScanner::first()
  {
  return impl.getFirstFile();
  }
bool DirScanner::next()
  {
  return impl.getNextFile();
  }

DirScanner.h

Synopsis
#pragma once
#include <string>
using std::string;
#include "DirItem.h"

class DirScanner
  {
  public:
    explicit DirScanner(const string& path);
    ~DirScanner();
    bool more();
    DirItem getItem();
    bool isDotFiles();
    void close();
  private:
    string getName();
    long long getFileSize();
    long long getFileDate();
    bool getIsDir();
    bool getIsFile();
    bool first();
    bool next();
  private:
    class Private;
    Private& impl;
  } ;

DirTree.cpp

Synopsis
#include "DirTree.h"
#include "DirScanner.h"
#include "DirItem.h"

DirTreeIterator DirTreeIterator::End;

DirTree::DirTree(const string& root)
: mIterator(root)
  {
  mRoot = root;
  }
DirTree::~DirTree(void)
  {
  }
DirTreeIterator& DirTree::begin()
  {
  mIterator.Reset();
  return mIterator;
  }
DirTreeIterator& DirTree::end()
  {
  return DirTreeIterator::End;
  }

DirTree.h

Synopsis
#pragma once

#include <string>
using std::string;

#include "DirTreeIterator.h"

class DirTree
  {
  public:
    explicit DirTree(const string& root);
    ~DirTree(void);
    DirTreeIterator& begin();
    DirTreeIterator& end();
  private:
    string mRoot;
    DirTreeIterator mIterator;
  };

DirTreeIterator.cpp

Synopsis
#include "DirTreeIterator.h"
#include "DirScanner.h"

DirTreeIterator::DirTreeIterator()
  {
  }
DirTreeIterator::DirTreeIterator(const string& root)
: mRoot(root)
  {
  }
void DirTreeIterator::Reset()
  {
  ClearStack();
  mCurrentItem = DirItem::MakeDir(mRoot);
  PushCurrentItem();
  }
bool DirTreeIterator::operator ==(const DirTreeIterator& other) const
  {
  if (mCurrentItem.IsEmpty() && (*other).IsEmpty()) return true;
  if (mCurrentItem.IsEmpty() ^ (*other).IsEmpty()) return false;
  return mCurrentItem == other.operator *();
  }
bool DirTreeIterator::operator !=(const DirTreeIterator& other) const
  {
  return !operator==(other);
  }
DirItem DirTreeIterator::operator *() const
  {
  return mCurrentItem;
  }
void DirTreeIterator::operator++()
  {
  mDirStack.pop();
  LoadDirs(mCurrentItem);
  PopCurrentItem();
  }
void DirTreeIterator::ClearStack()
  {
  while(!mDirStack.empty())
    mDirStack.pop();
  }
void DirTreeIterator::PushCurrentItem()
  {
  if (mCurrentItem.IsEmpty()) return;
  mDirStack.push(mCurrentItem);
  }
void DirTreeIterator::PopCurrentItem()
  {
  mCurrentItem = mDirStack.empty() ? DirItem::Null : mDirStack.top();
  }
void DirTreeIterator::LoadDirs(const DirItem& item)
  {
  DirScanner finder(item.Path());
  while(finder.more())
    {
    if (finder.isDotFiles()) continue;
    mDirStack.push(finder.getItem());
    }
  finder.close();
  }

DirTreeIterator.h

Synopsis
#pragma once
#include <stack>
#include <string>
using std::string;
using std::stack;
#include "DirItem.h"

class DirTreeIterator
  {
  public:
    static DirTreeIterator End;
    DirTreeIterator();
    DirTreeIterator(const string& root);
    void Reset();
    bool operator ==(const DirTreeIterator& other) const;
    bool operator !=(const DirTreeIterator& other) const;
    DirItem operator *() const;
    void operator++();
  private:
    void ClearStack();
    void PushCurrentItem();
    void PopCurrentItem();
    void LoadDirs(const DirItem& item);

  private:
    stack<DirItem> mDirStack;
    DirItem mCurrentItem;
    string mRoot;
  } ;

EMailer.cpp

Synopsis
#include "EMailer.h"
#include "Smtp.h"

class EMailer::Private
  {
  public:
    Smtp smtp;
  };
EMailer::EMailer(const string& smtpserver)
: impl(* new EMailer::Private)
  {
  impl.smtp.SmtpServer = smtpserver;
  }
EMailer::~EMailer(void)
  {
  delete &impl;
  }
void EMailer::Subject(const string& sub)
  {
  impl.smtp.Subject = sub;
  }
void EMailer::ToAddress(const string& addr)
  {
  impl.smtp.To = addr;
  }
void EMailer::FromAddress(const string& addr)
  {
  impl.smtp.From = addr;
  }
void EMailer::Body(const string& text)
  {
  mBody += text;
  }
void EMailer::Send()
  {
  impl.smtp.Content = mBody;
  try
    {
    impl.smtp.Send();
    }
  catch(...)
    {
    }
  impl.smtp.Close();
  }


EMailer.h

Synopsis
#pragma once

#include <string>
using std::string;

class EMailer
  {
  public:
    explicit EMailer(const string& smtpserver);
    ~EMailer();
    void Subject(const string& sub);
    void ToAddress(const string& addr);
    void FromAddress(const string& addr);
    void Body(const string& text);
    void Send();

  private:
    string mBody;
    class Private;
    Private& impl;
  };

FileLineReader.cpp

Synopsis
#include <fstream>
using namespace std;

#include "FileLineReader.h"

class FileLineReader::Private
  {
  public:
    ifstream ifs;
  } ;

FileLineReader::FileLineReader(const string& path)
: impl(* new FileLineReader::Private())
  {
  impl.ifs.open(path.c_str());
  }
FileLineReader::~FileLineReader()
  {
  impl.ifs.close();
  delete &impl;
  }
bool FileLineReader::IsOk()
  {
  return !impl.ifs.fail() && !impl.ifs.eof();
  }
string FileLineReader::GetLine()
  {
  string line;
  getline(impl.ifs, line); //don't skip whitespace
  return line;
  }

FileLineReader.h

Synopsis
#pragma once

#include <string>
using std::string;

class FileLineReader
  {
  public:
    FileLineReader(const string& path);
    ~FileLineReader();
    bool IsOk();
    string GetLine();
  private:
    class Private;
    Private& impl;
  } ;

FileNameMatcher.cpp

Synopsis
#include <string>
#include <list>
#include <algorithm>
#include <iostream>
using std::string;
using std::list;
using std::transform;
using std::cout;

#include "FileNameMatcher.h"
#include "FileLineReader.h"

typedef list<string> PatternList;

class FileNameMatcher::Private
  {
  public:
    PatternList mPatterns;
  } ;
FileNameMatcher::FileNameMatcher()
: impl(* new FileNameMatcher::Private)
  {
  }
FileNameMatcher::~FileNameMatcher()
  {
  delete &impl;
  }
bool FileNameMatcher::IsMatch(const string& f)
  {
  string s = f;
  transform(s.begin(), s.end(), s.begin(), tolower);
  for (PatternList::iterator it = impl.mPatterns.begin(); it != impl.mPatterns.end(); ++it)
    {
    if (IsMatch(s, *it))
      return true;
    }
  return false;
  }
void FileNameMatcher::AddPatternsFrom(const string& path)
  {
  FileLineReader reader(path);
  while (reader.IsOk())
    {
    string pattern = reader.GetLine();
    transform(pattern.begin(), pattern.end(), pattern.begin(), tolower);
    if (pattern[0] == ';') continue;
    if (pattern.empty()) continue;
    AddPattern(pattern);
    }
  }
void FileNameMatcher::AddPattern(const string& p)
  {
  if (p[0] == ';') return;
  impl.mPatterns.push_back(p);
  }
bool FileNameMatcher::IsMatch(const string& f, const string& p)
  {
  bool rc = f.find(p) != string::npos;
  //cout << "  ismatch: " << rc << " '" << p << "' in '" << f << "'\n";
  return rc;
  }

FileNameMatcher.h

Synopsis
#pragma once

#include <string>
#include <list>
using std::string;
using std::list;

class FileNameMatcher
  {
  public:
    FileNameMatcher();
    ~FileNameMatcher();
    bool IsMatch(const string& f);
    void AddPatternsFrom(const string& path);
    void AddPattern(const string& p);
  private:
    bool IsMatch(const string& f, const string& p);
    class Private;
    Private& impl;
  };

ItemBackup.cpp

Synopsis
#include <algorithm>
#include <iostream>
using namespace std;

#include "ItemBackup.h"
#include "DirItem.h"
#include "Options.h"

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
static DWORD cFileAttribute = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE;
static DWORD cDirAttribute =  FILE_ATTRIBUTE_DIRECTORY;

class ItemBackup::Private
{
public:
  bool DelFile(const string& dest)
    {
    return ::DeleteFile(dest.c_str());
    }
  bool CopyFile(const string& src, const string& dest)
    {
    return ::CopyFile(src.c_str(), dest.c_str(), FALSE) ? true : false;
    }
  bool CreateDirectory(const string& path)
    {
    return ::CreateDirectory(path.c_str(), 0);
    }
  bool Exists(const string& path, unsigned long typeflag)
    {
    DWORD res = ::GetFileAttributes(path.c_str());
    if (res == 0xFFFFFFFF) return false;
    if (res & typeflag) return true;
    return false;
    }
  bool FileExists(const string& path)
    {
    return Exists(path, cFileAttribute);
    }
  bool DirExists(const string& path)
    {
    return Exists(path, cDirAttribute);
    }
  void ClearReadonlyOn(const string& path)
    {
    DWORD attr = ::GetFileAttributes(path.c_str());
    attr &= ~FILE_ATTRIBUTE_READONLY;
    ::SetFileAttributes(path.c_str(), attr);
    }
  unsigned long GetLastError()
    {
    return ::GetLastError();
    } 
  bool IsReadOnly(unsigned long gle)
    {
    return gle == ERROR_ACCESS_DENIED;
    }
} ;


#else

#include <boost/filesystem/operations.hpp>
namespace fs = boost::filesystem;

class ItemBackup::Private
{
public:
  Private() : mLastError(0)
    {
    }
  bool CopyFile(const string& src, const string& dest)
    {
    fs::copy_file(src, dest); //todo: trap failures
    return true;
    }
  bool DelFile(const string& dest)
    {
    return fs::remove(dest);
    }
  bool CreateDirectory(const string& path)
    {
    return fs::create_directory(path);
    }
  bool FileExists(const string& path)
    {
    return fs::exists(path);
    }
  bool DirExists(const string& path)
    {
    return fs::exists(path);
    }
  void ClearReadonlyOn(const string& path)
    {
      //todo: clear read only flag
    }
  unsigned long GetLastError()
    {
    return mLastError;
    } 
  bool IsReadOnly(unsigned long gle)
    {
      return mLastError == (unsigned long) -1; //EPERM; todo: use correct enum
    }
  unsigned long mLastError;
};
#endif


//-------------------------------
ItemBackup::ItemBackup()
  : impl(* new ItemBackup::Private())
  {
  }
ItemBackup::~ItemBackup()
  {
  delete &impl;
  }
void ItemBackup::SetRoot(const string& destroot)
  {
  mDestRoot = destroot;
  }
void ItemBackup::Delete(const DirItem& item)
  {
  //don't roll versions!
  string dest = ConvertPath(item, mDestRoot);
  cout << "Deleted: '" << dest << "'\n";
  impl.DelFile(dest);
  }
bool ItemBackup::Copy(const DirItem& item)
  {
  RollVersions(item);
  string dest = ConvertPath(item, mDestRoot);
  MakePath(dest);
  if (item.IsDir()) return true;

  if (impl.CopyFile(item.Path(), dest))
    return true;

  return HandleError(item.Path(), dest);
  }
void ItemBackup::RollVersions(const DirItem& item)
  {
  for (int vers = gOptions.NumVersions; vers > 0; vers--)
    {
    string src = vers - 1 == 0 ? ConvertPath(item, mDestRoot) : ConvertPath(item, mDestRoot, vers - 1);
    if (!impl.FileExists(src))
      continue;

    string dest = ConvertPath(item, mDestRoot, vers);
    MakePath(dest);
    if (item.IsDir()) continue;
    impl.CopyFile(src, dest);
    }
  }
string ItemBackup::ConvertPath(const DirItem& item, const string& destroot, int version)
  {
  char buf[20];
  sprintf(buf, "%d", version);
  return ConvertPath(item, mDestRoot + buf);
  }
bool ItemBackup::HandleError(const string& src, const string& dest)
  {
  unsigned long gle = impl.GetLastError();
  cout << "ERROR: CopyFile failed. gle=" << gle << " src='" << src << "' dest='"<< dest << "'\n";

  if (impl.IsReadOnly(gle))
    return HandleReadonlyFile(src, dest);

  return false;
  }
bool ItemBackup::HandleReadonlyFile(const string& src, const string& dest)
  {
  cout << "   -- clearing readonly flag on dest='"<< dest << "'\n";
  impl.ClearReadonlyOn(dest);

  cout << "   -- recopying file\n";
  bool ok = impl.CopyFile(src, dest);
  if (ok)
    cout << "   -- second copy is successful\n";
  else
    cout << "   -- second copy failed:  gle=" << impl.GetLastError() << "\n";
  return ok;
  }

string ItemBackup::ConvertPath(const DirItem& item, const string& destroot)
  {
  string srcpath = item.Path();
  replace(srcpath.begin(), srcpath.end(), '/', '\\');
  if (IsUncPath(srcpath))
    {
    if (item.IsDir())
      return AddTrailingSlash(ConvertUncPath(destroot, srcpath));
    return ConvertUncPath(destroot, srcpath);
    }

  if (item.IsDir())
    return AddTrailingSlash(ConvertLocalPath(destroot, srcpath));
  return ConvertLocalPath(destroot, srcpath);
  }
bool ItemBackup::IsUncPath(const string& s)
  {
  return s[0] == '\\' && s[1] == '\\';
  }
string ItemBackup::ConvertUncPath(const string& destroot, const string& srcpath)
  {
  return destroot + &srcpath[1];
  }
string ItemBackup::ConvertLocalPath(const string& destroot, const string& srcpath)
  {
  return destroot + "\\local\\" + srcpath[0] + &srcpath[2];
  }
string ItemBackup::AddTrailingSlash(const string& s)
  {
  return (s.find_last_of('\\') == s.length() ? s : s + "\\" );
  }
void ItemBackup::MakePath(const string& dest)
  {
  string path = dest;
  for (string::size_type i = path.find("\\", 0); i != string::npos; i = path.find("\\", i + 1))
    CreateDir(path.substr(0, i));
  }
void ItemBackup::CreateDir(const string& path)
  {
  if (impl.DirExists(path)) return;
  bool rc = impl.CreateDirectory(path);
  if (!rc)
    {
    cout << "ERROR: CreateDir failed. gle=" << impl.GetLastError() << " dir='"<< path << "'\n";
    }
  }

ItemBackup.h

Synopsis
#pragma once

#include <string>
using std::string;
class DirItem;

class ItemBackup
  {
  public:
    ItemBackup();
    ~ItemBackup();
    bool Copy(const DirItem& item);
    void Delete(const DirItem& item);
    void SetRoot(const string& destroot);
  private:
    string ConvertPath(const DirItem& item, const string& destroot);
    string ConvertPath(const DirItem& item, const string& destroot, int version);
    bool IsUncPath(const string& s);
    string ConvertUncPath(const string& destroot, const string& srcpath);
    string ConvertLocalPath(const string& destroot, const string& srcpath);
    string AddTrailingSlash(const string& s);
    void MakePath(const string& dest);
    void CreateDir(const string& path);
    bool HandleReadonlyFile(const string& item, const string& dest);
    bool HandleError(const string& src, const string& dest);
    void RollVersions(const DirItem& item); 

  private:
    class Private;
    Private& impl;
    string mDestRoot;
  } ;

Options.cpp

Synopsis
#include "Options.h"

Options gOptions;

Options::Options()
  {
  BackupRoot = "c:\\backups";
  ExcludeFileName = "jbackupexcludes.txt";
#ifdef _DEBUG
  BackupCfgFileName = "jbackup.test.cfg";
#else
  BackupCfgFileName = "jbackup.cfg";
#endif
  BackupDbFileName = "jbackup.db";
  //SmtpServerAddress = mail.xx.com;
  //ToAddress = you@xx.com;
  //FromAddress = you@xx.com;
  IsMirroredBackup = false;
  NumVersions = 0;
  } 

Options.h

Synopsis
#pragma once

#include <string>
using std::string;
class Options
  {
  public:
    string BackupRoot;
    string ExcludeFileName;
    string BackupCfgFileName;
    string BackupDbFileName;
    string SmtpServerAddress;
    string ToAddress;
    string FromAddress;
    bool IsMirroredBackup;
    int  NumVersions;
    Options();
  } ;

extern Options gOptions;

smtp.cpp

Synopsis
#ifdef WIN32
#include <winsock2.h>
#define socklen_t int
#else
#include <resolv.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SOCKET int
#define INVALID_SOCKET (-1)
#define closesocket(s) close(s)
//#define inet_ntoa loc_ntoa
//#define inet_aton loc_aton
#endif

#include <string>
using namespace std;

#include "Smtp.h"

#ifdef WIN32
#include "WSAInit.h"
WSAInit instance;
#endif

#include "SmtpException.h"
#include "SmtpHostInfo.h"
#include "SocketT.h"
typedef SocketT<SmtpHostInfo, SmtpException> SmartSocket;
#include "SmtpWriter.h"

class Smtp::Private
  {
  public:
    SmartSocket mSocket;
  } ;

Smtp::Smtp()
: impl(* new Smtp::Private)
  {
  }
Smtp::~Smtp()
  {
  impl.mSocket.Close();
  delete &impl;
  }
void Smtp::Send()
  {
  Create();
  Connect();
  WriteHello();
  WriteFrom();
  WriteTo();
  WriteStartData();
  WriteBody();
  WriteQuit();
  }
void Smtp::Close()
  {
  impl.mSocket.Close();
  }
void Smtp::Create()
  {
  impl.mSocket.SetServer(SmtpServer);
  impl.mSocket.Create();
  }
void Smtp::Connect()
  {
  impl.mSocket.Connect();
  CheckResponse("220");
  }
void Smtp::WriteHello()
  {
  SmtpWriter writer(impl.mSocket);
  writer.Line("HELO " + impl.mSocket.GetLocalAddr());
  writer.Flush();
  CheckResponse("250");
  }
void Smtp::WriteFrom()
  {
  SmtpWriter writer(impl.mSocket);
  writer.HeaderLine("MAIL FROM", From);
  writer.Flush();
  CheckResponse("250");
  }
void Smtp::WriteTo()
  {
  SmtpWriter writer(impl.mSocket);
  writer.HeaderLine("RCPT TO", To);
  writer.Flush();
  CheckResponse("250");
  }
void Smtp::WriteStartData()
  {
  SmtpWriter writer(impl.mSocket);
  writer.Line("DATA");
  writer.Flush();
  CheckResponse("354");
  }
void Smtp::WriteBody()
  {
  SmtpWriter writer(impl.mSocket);
  writer.Body(Subject, To, From, Content);
  writer.Flush();
  CheckResponse("250");
  }
void Smtp::WriteQuit()
  {
  SmtpWriter writer(impl.mSocket);
  writer.Line("QUIT");
  writer.Flush();
  CheckResponse("221");
  }
void Smtp::CheckResponse(const string& s)
  {
  string response = impl.mSocket.Response();
  if (response.find(s) == response.npos)
    throw SmtpException("Expected: '" + s + "' actual: '" + response + "'");
  }

Smtp.h

Synopsis
#pragma once
#include <string>
using std::string;

class Smtp
  {
  public:
    string Content;
    string From;
    string To;
    string SmtpServer;
    string Subject;

    Smtp();
    ~Smtp();
    void Send();
    void Close();
  
  private:
    void Create();
    void Connect();
    void WriteHello();
    void WriteFrom();
    void WriteTo();
    void WriteStartData();
    void WriteBody();
    void WriteQuit();
    void CheckResponse(const string& s);
  
    class Private;
    Private& impl;
  };

SmtpException.h

Synopsis
#pragma once
#include <exception>

class SmtpException : public exception
  {
  string m_str;
  public:
    SmtpException() throw()
      :m_str("SMTP exception")
      {
      }
    SmtpException(const string& str)
      :m_str("SMTP exception: " + str)
      {
      }
    ~SmtpException() throw()
      {
      }
    virtual const char* what() const throw()
      {
      return m_str.c_str();
      }
  };

SmtpHostInfo.h

Synopsis
#pragma once
#include "SmtpException.h"

class SmtpHostInfo
  {
  public:
    void SetServer(const string& server)
      {
      mServer = server;
      }
    unsigned long GetAddr()
      {
      struct hostent* host;
      struct in_addr inaddr;
      unsigned int rc;
#ifdef WIN32      
      inaddr.s_addr = ::inet_addr(mServer.c_str());
      rc = (unsigned int) (void*)  inaddr.s_addr;
#else
      rc = inet_aton(mServer.c_str(), &inaddr);
#endif
      if (rc == INADDR_NONE)
        host = ::gethostbyname(mServer.c_str());
      else
        host = ::gethostbyaddr((const char *)&inaddr, sizeof(inaddr), AF_INET);
      if (host == NULL)
        throw SmtpException("GetAddr: Invalid SMTP server");
      return *((u_long*)host->h_addr_list[0]);
      }
    short GetPort()
      {
      struct servent* server = ::getservbyname("smtp", "tcp");
      if (server == NULL)
        throw SmtpException("GetPort: SMTP is an unknown TCP service");
      return server->s_port;
      }
  private:
    string mServer;
  } ;


SmtpWriter.h

Synopsis
#pragma once
#include <sstream>
using std::stringstream;

class SmtpWriter
  {
  public:
    SmtpWriter(SmartSocket& s)
      : mSocket(s)
      {
      }
    void Line(const string& s)
      {
      mOut << s << "\r\n";
      }
    void Flush()
      {
      mSocket.Write(mOut.str());
      }
    void HeaderLine(const string& hdr, const string& val)
      {
      NameValueLine(hdr, "<" + val + ">");
      }
    void Body(const string& subject, const string& to, const string& from, const string& content)
      {
      NameValueLine("Subject", subject);
      NameValueLine("To", to);
      NameValueLine("From", from);
      StartOfContent();
      Line(content);
      EndOfContent();
      }
  private:
    void NameValueLine(const string& name, const string& value)
      {
      Line(name + ": " + value);
      }
    void StartOfContent()
      {
      Line("");
      }
    void EndOfContent()
      {
      Line(".");
      }
  private:
    stringstream mOut;
    SmartSocket& mSocket;
  } ;

SocketT.h

Synopsis
#pragma once

#include "Trace.h"

template <class HostInfoProvider, class SocketException>
class SocketT : public HostInfoProvider
  {
  public:
    SocketT(SOCKET& s)
      :mSocket(s)
      {
      }
    SocketT()
      : mSocket(INVALID_SOCKET)
      {
      }
    ~SocketT()
      {
      Close();
      }
    void Close()
      {
      if (mSocket == INVALID_SOCKET) return;
      ::closesocket(mSocket);
      mSocket = INVALID_SOCKET;
      }
    void Write(const string& str)
      {
      TRACE("Write: " << str);
      int bytestosend = str.length();
      int bytessent;
      for (const char* s = str.c_str(); bytestosend; s += bytessent)
        {
        bytessent = ::send(mSocket, s, bytestosend, 0);
        if (bytessent <= 0)
          throw SocketException("Write: failed");
        bytestosend -= bytessent;
        }
      };
    string GetLocalAddr()
      {
      struct sockaddr_in local;
      socklen_t n = sizeof(local);
      ::getsockname(mSocket, (struct sockaddr *)&local, &n);
      struct hostent* h = ::gethostbyaddr((char*)&local.sin_addr, sizeof(local.sin_addr), AF_INET);
      return h ? h->h_name : ::inet_ntoa(local.sin_addr);
      }
    void Create()
      {
      Close();
      mSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (mSocket == INVALID_SOCKET)
        throw SocketException("Create: returned invalid socket");
      }
    void Connect()
      {
      sockaddr_in sa;
      sa.sin_family = AF_INET;
      sa.sin_port = HostInfoProvider::GetPort();
      sa.sin_addr.s_addr = HostInfoProvider::GetAddr();
      if (::connect(mSocket, (sockaddr *)&sa, sizeof(sa)) < 0)
        throw SocketException("Connect: connection to host failed");
      }
    string Response()
      {
      string response;
      int roundtrips = 0;
      for(;;)
        {
        char buffer[1024];
        int n = ::recv(mSocket, buffer, sizeof(buffer), 0);
        if (n == -1)
          throw SocketException("Response: socket read failed");
        response += string(buffer, n);
        if (response.find("\n") != response.npos)
          {
          TRACE(response);
          return response;
          }
        roundtrips++;
        if (roundtrips > 1000)
          throw SocketException("socket read timeout");
        }
      }
  private:
    SOCKET mSocket;
  };

Statistics.cpp

Synopsis
#include <time.h>
#include <iostream>
#include <ostream>
#include <sstream>
using namespace std;

#include "Statistics.h"
#include "EMailer.h"
#include "Options.h"

Statistics gStats;

class Statistics::Private
  {
  public:
    clock_t mStart;
    clock_t mAfter;
    clock_t mStartLoad;
    clock_t mAfterLoad;
    clock_t mStartScan;
    clock_t mAfterScan;
    clock_t mStartSave;
    clock_t mAfterSave;
    long mNumFiles;
    long mNumIgnoredFiles;
    long mNumCopiedFiles;
    long mNumUnchangedFiles;
    long mNumFailedCopyFiles;
    long mNumNewFiles;
    long mNumDeletedFiles;
  } ;
Statistics::Statistics()
: impl(* new Statistics::Private)
  {
  impl.mNumFiles = 0;
  impl.mNumIgnoredFiles = 0;
  impl.mNumCopiedFiles = 0;
  impl.mNumUnchangedFiles = 0;
  impl.mNumFailedCopyFiles = 0;
  impl.mNumNewFiles = 0;
  impl.mNumDeletedFiles = 0;
  }
Statistics::~Statistics()
  {
  delete &impl;
  }
void Statistics::Start()
  {
  impl.mStart = clock();
  }
void Statistics::End()
  {
  impl.mAfter = clock();
  }
void Statistics::StartLoad()
  {
  impl.mStartLoad = clock();
  }
void Statistics::EndLoad()
  {
  impl.mAfterLoad = clock();
  }
void Statistics::StartSave()
  {
  impl.mStartSave = clock();
  }
void Statistics::EndSave()
  {
  impl.mAfterSave = clock();
  }
void Statistics::StartScan()
  {
  impl.mStartScan = clock();
  }
void Statistics::EndScan()
  {
  impl.mAfterScan = clock();
  }
void Statistics::IncNumFiles()
  {
  impl.mNumFiles++;
  }
void Statistics::IncNumIgnoredFiles()
  {
  impl.mNumIgnoredFiles++;
  }
void Statistics::IncNumCopiedFiles()
  {
  impl.mNumCopiedFiles++;
  }
void Statistics::IncNumUnchangedFiles()
  {
  impl.mNumUnchangedFiles++;
  }
void Statistics::IncNumFailedCopyFiles()
  {
  impl.mNumFailedCopyFiles++;
  }
void Statistics::IncNumNewFiles()
  {
  impl.mNumNewFiles++;
  }
void Statistics::IncNumDeletedFiles()
  {
  impl.mNumDeletedFiles++;
  }
string ToString(time_t& t)
  {
  static char buf[28];
  strftime(buf, sizeof(buf), "%b %d/%Y %H:%M:%S", localtime(&t));
  return buf;
  }

string Interval(clock_t start, clock_t end)
  {
  stringstream os;
  unsigned long sec = (end - start) / 1000;
  unsigned long min = 0;
  unsigned long hour = 0;
  if (sec > 60)
    {
    min = sec / 60;
    sec %= 60;
    }
  if (min > 60)
    {
    hour = min / 60;
    min %= 60;
    }
  if (hour == 1)
    os << hour << " hour ";
  else if (hour > 1)
    os << hour << " hours ";
  if (min == 1)
    os << min << " minute ";
  else if (min > 1)
    os << min << " minutes ";
  if (sec == 1)
    os << sec << " second";
  else
    os << sec << " seconds";
  os << ends;
  return os.str();
  }
void Statistics::Report()
  {
  stringstream out;

  time_t now = time(0);
  out << "Date: " << ToString(now) << "\r\n";
  out << "Load time : " << Interval(impl.mStartLoad, impl.mAfterLoad) << "\r\n";
  out << "Scan time : " << Interval(impl.mStartScan, impl.mAfterScan) << "\r\n";
  out << "Save time : " << Interval(impl.mStartSave, impl.mAfterSave) << "\r\n";
  out << "Total time: " << Interval(impl.mStart, impl.mAfter) << "\r\n";
  out << "\r\n";
  out << "Num Ignored Files    : " << impl.mNumIgnoredFiles << "\r\n";
  out << "Num Unchanged Files  : " << impl.mNumUnchangedFiles << "\r\n";
  out << "Num New Files        : " << impl.mNumNewFiles << "\r\n";
  out << "Num Deleted Files    : " << impl.mNumDeletedFiles << "\r\n";
  out << "Num Failed Copy Files: " << impl.mNumFailedCopyFiles << "\r\n";
  out << "Num Copied Files     : " << impl.mNumCopiedFiles << "\r\n";
  out << "Num Files            : " << impl.mNumFiles << "\r\n";
  out << ends;
  cout << out.str();

  if (impl.mNumFailedCopyFiles > 0)
    {
    EMailer email(gOptions.SmtpServerAddress);
    email.Subject(string("Backup results on: ") + ToString(now));
    email.ToAddress(gOptions.ToAddress);
    email.FromAddress(gOptions.FromAddress);
    email.Body(out.str());
    email.Send();
    }
  }

Statistics.h

Synopsis
#pragma once

class Statistics
  {
  public:
    Statistics();
    ~Statistics();
    void Start();
    void End();
    void StartLoad();
    void EndLoad();
    void StartSave();
    void EndSave();
    void StartScan();
    void EndScan();
    void IncNumFiles();
    void IncNumIgnoredFiles();
    void IncNumCopiedFiles();
    void IncNumUnchangedFiles();
    void IncNumFailedCopyFiles();
    void IncNumNewFiles();
    void IncNumDeletedFiles();
    void Report();
  private:
    class Private;
    Private& impl;
  } ;

extern Statistics gStats;


Trace.h

Synopsis
#pragma once

#ifdef _DEBUG
#include <iostream>
using std::cout;
using std::endl;
#define TRACE(s) cout << s << endl;
#else
#define TRACE(s)
#endif

WSAInit.h

Synopsis
#pragma once

class WSAInit
  {
  public:
    WSAInit()
      {
      WORD w = MAKEWORD(1,1);
      WSADATA wsadata;
      ::WSAStartup(w, &wsadata);
      };
    ~WSAInit()
      {
      ::WSACleanup();
      };
  } ;






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010