|
|
\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
|
|
|
; 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
|
|
|
#include "App.h"
#include "Statistics.h"
int main()
{
App app;
gStats.Start();
app.Run();
gStats.End();
gStats.Report();
return 0;
}
|
|
|
#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();
}
}
|
|
|
#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;
} ;
|
|
|
#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);
}
}
|
|
|
#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;
} ;
|
|
|
#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);
|
|
|
#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;
}
|
|
|
#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);
}
}
|
|
|
#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;
} ;
|
|
|
#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;
}
|
|
|
#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);
|
|
|
#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();
}
|
|
|
#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;
} ;
|
|
|
#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;
}
|
|
|
#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;
};
|
|
|
#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();
}
|
|
|
#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;
} ;
|
|
|
#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();
}
|
|
|
#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;
};
|
|
|
#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;
}
|
|
|
#pragma once
#include <string>
using std::string;
class FileLineReader
{
public:
FileLineReader(const string& path);
~FileLineReader();
bool IsOk();
string GetLine();
private:
class Private;
Private& impl;
} ;
|
|
|
#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;
}
|
|
|
#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;
};
|
|
|
#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";
}
}
|
|
|
#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;
} ;
|
|
|
#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;
}
|
|
|
#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;
|
|
|
#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 + "'");
}
|
|
|
#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;
};
|
|
|
#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();
}
};
|
|
|
#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;
} ;
|
|
|
#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;
} ;
|
|
|
#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;
};
|
|
|
#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();
}
}
|
|
|
#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;
|
|
|
#pragma once
#ifdef _DEBUG
#include <iostream>
using std::cout;
using std::endl;
#define TRACE(s) cout << s << endl;
#else
#define TRACE(s)
#endif
|
|
|
#pragma once
class WSAInit
{
public:
WSAInit()
{
WORD w = MAKEWORD(1,1);
WSADATA wsadata;
::WSAStartup(w, &wsadata);
};
~WSAInit()
{
::WSACleanup();
};
} ;
|