Introduction
This is a simple application that backs up your code to Gmail. It creates one or more Zip files that contain a filtered list of files, excluding obj, bin, exe, SVN and other non-essential files. It will overflow to multiple zip files as needed, and you can control how large the zip file can be.
Background
Like most developers, I use SVN as my code repository which I try to back-up as often as possible. I liked the idea of an automated, daily, off-site backup that lets me get my code files from anywhere at any time. So, I developed this little tool to ensure that I have a complete, daily off-site backup of my essential files, which would include all .cs, .vb, .proj etc. By backing up to Gmail, I have access to all of my source code even if I am off my network (like at a client). This is not an incremental backup, although you could easily modify the program to make it date sensitive.
By running this on a daily basis, I can also easily restore a snapshot of my development tree for any given date, which has come in handy a few times. (I know you can pull prior versions from SVN, but trust me, this is a lot easier!)
Backing up code to Gmail may seem like a trivial task, but there is a bit more to it than simply Xcopying a bunch of files. The trick is excluding most of the heavy-weight files: I don't need to backup any of the obj files, local reference copies, .svn files, .EXEs, or PDBs. I also happen to have a bunch of third party files in my folders that don't need to be included. So, the program lets you define a list of include and exclude patterns for the files you need and the files you don't need. You can specify folders to be excluded (such as \bin), or specific file patterns (like *.zip). Your settings are saved in an XML file which is just a serialized form of the BackupVars
class in the program.
Gmail Restrictions
Gmail is an amazing email service. I think they now give you about 15 gig of space to use... which makes it ideal for this type of tasks. However, it does come with a few restrictions: you can't include any .exe files in an attachment, even if the files are inside a zip file. The Backup2gmail program will rename a zip file to xxx_yymmdd_hhmmss.ziprenamed so that Gmail sees it only as a binary file.
Gmail limits the total message size to 20 MB, which is pretty large. However, my source code tree, with some test data, goes beyond 20 MB zipped. So, the program will create multiple zip files and send each one as a separate email message. The subject of the email will indicate the message number, and the body of the email will include a nicely formatted HTML table with a list of the files.
The email message looks like this in Gmail:
To restore your files, you need to manually download the attachment, rename it back to a .zip file, and unzip it. The folders are preserved in the zip file, of course.
Command Line Usage
This program can be run from the command line or interactively. The code for the program includes a Command line parsing library. There are two command line formats that can be used: you can pass in the folder name, email account, and password, or you can pass in a Backup2Gmail profile name. A B2G profile is an XML file with the settings and email credentials.
Click on the "Batch Command Line" menu option under the File menu for command line format.
Scheduling the Program in Task Scheduler
You can easily set up a schedule in the Windows Task scheduler to automate your backup on a nightly basis. I usually run the jobs in the early hours of the morning. The scheduling is not included in the program.. you will have to do this manually.
Using the Code
There are a number of useful items in the source code that you may find beneficial:
Dual Mode Operations - Command Line or Interactive
There is a trick to creating a WinForms application that can be run from the command line. By default, a WinForms application will create a main window, so to avoid having a ghost window appear when you use a command line, you can use the following code at start up:
class Backup2Gmail
{
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
[STAThread]
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
frmBackup oBackup = new frmBackup();
oBackup.ShowDialog();
return;
}
AttachConsole(ATTACH_PARENT_PROCESS);
CommandLineParser parser = new CommandLineParser();
Backup2GmailArgs oBackupArgs = new Backup2GmailArgs();
parser.ExtractArgumentAttributes(oBackupArgs);
try
{
parser.ParseCommandLine(args);
}
catch (Exceptions.CommandLineArgumentException exC)
{
Console.Write(exC.ToString());
}
The other challenge for me was creating a single executable without any dependencies. I chose to use the DotNetZip library from CodePlex (see http://dotnetzip.codeplex.com/) over the more commonly used SharpZip library. This library was much easier to incorporate into the project and compiled into the final exe.
Sending the email message is done using the System.Net.Mail.SmtpClient
libraries. Here is the code to actually send the email message:
System.Net.Mail.Attachment mailAttachment =
new System.Net.Mail.Attachment(oBackupVars.Outputfile);
mm.Attachments.Add(mailAttachment);
string smtpHost = "smtp.gmail.com";
System.Net.Mail.SmtpClient mClient = new System.Net.Mail.SmtpClient();
mClient.Port = 587;
mClient.EnableSsl = true;
mClient.UseDefaultCredentials = false;
mClient.Credentials = new System.Net.NetworkCredential(oBackupVars.Userid,
oBackupVars.Password);
mClient.Host = smtpHost;
mClient.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
mClient.Timeout = int.MaxValue;
ShowMessage(oBackupVars, "Sending email to " +
oBackupVars.Userid + " " + mm.Subject);
mClient.Send(mm);
ShowMessage(oBackupVars, "Sent email with backup zip file to " +
oBackupVars.Userid);
Note that Gmail uses a non-standard port number (587).
Program Settings and Runtime Stats
The serializable class BackupVars
includes all run time statistics, email settings, and filters that control which files are included or excluded from the backup. The values in this class are edited using the Windows PropertyGrid
control. Serialization is done using the .NET serializer.
[TypeConverter(typeof(ExpandableObjectConverter))]
public class BackupVars
{
[XmlIgnore()]
public ZipFile oZipFile = null;
[XmlIgnore()]
public int NumEmailMessages = 0;
[XmlIgnore()]
public long UncompressedLength = 0;
[XmlIgnore()]
public long CompressedFileLength = 0;
[XmlIgnore()]
public Label StatusMessage = null;
[XmlIgnore()]
public bool IsRunning = false;
[XmlIgnore()]
public bool IsCancelled = false;
private string _TempFolderForZipFiles = @"C:\Temp\Backup2Gmail\";
public string TempFolderForZipFiles
{
get { return _TempFolderForZipFiles; }
set { _TempFolderForZipFiles = value; }
}
private string _userid;
public string Userid
{
get { return _userid; }
set { _userid = value; }
}
private string _password;
public string Password
{
get { return _password; }
set { _password = value; }
}
private string _outputfile;
public string Outputfile
{
get { return _outputfile; }
set { _outputfile = value; }
}
private string _SourceCodeFolderBaseName;
public string SourceCodeFolderBaseName
{
get { return _SourceCodeFolderBaseName; }
set { _SourceCodeFolderBaseName = value; }
}
private string _RootPath;
public string RootPath
{
get { return _RootPath; }
set { _RootPath = value; }
}
public StringBuilder sb = new StringBuilder();
private string[] _IncludedFilePattern = new string[] { "*.*" };
public string[] IncludeFilePatterns
{
get { return _IncludedFilePattern; }
set { _IncludedFilePattern = value; }
}
private string[] _ExcludedFilePattern = new string[] { "*.msi*" };
public string[] ExcludedFilePattern
{
get { return _ExcludedFilePattern; }
set { _ExcludedFilePattern = value; }
}
private string[] _ExcludedExtensionStartsWith =
new string[] { ".zip", ".msi" };
public string[] ExcludedExtensionStartsWith
{
get { return _ExcludedExtensionStartsWith; }
set { _ExcludedExtensionStartsWith = value; }
}
private string[] _ExcludedExtensionExactMatch =
new string[] { ".exe", ".dll", ".pdb", ".log" };
public string[] ExcludedExtensionExactMatch
{
get { return _ExcludedExtensionExactMatch; }
set { _ExcludedExtensionExactMatch = value; }
}
private string[] _ExcludedFoldersContains = new string[] {
@"\debug\bin",
@"\release",
@"pro\actmatewebsite\actmate\backup",
@"pro\actmatewebsite\actmate\recovery" };
public string[] ExcludedFoldersContains
{
get { return _ExcludedFoldersContains; }
set { _ExcludedFoldersContains = value; }
}
private string[] _ExcludedExactFolderNames = new string[] {
@"bin",
@"obj",
@"References",
@".svn",
@"RadControls",
@"fckeditor"};
public string[] ExcludedExactFolderNames
{
get { return _ExcludedExactFolderNames; }
set { _ExcludedExactFolderNames = value; }
}
private long _ZipFilesize = 19500000;
public long ZipFilesize
{
get { return _ZipFilesize; }
set { _ZipFilesize = value; }
}
public BackupVars()
{
}
public BackupVars Clone()
{
return DeserializeFromXMLString(SerializeToXMLString(this));
}
public static BackupVars DeserializeFromXMLString(string in_Backup2GmailXML)
{
XmlSerializer s = new XmlSerializer(typeof(BackupVars));
return (BackupVars)s.Deserialize(new StringReader(in_Backup2GmailXML));
}
public static string SerializeToXMLString(BackupVars in_Backup2Gmail)
{
try
{
System.Xml.Serialization.XmlSerializer s =
new System.Xml.Serialization.XmlSerializer(typeof(BackupVars));
StringWriter sw = new StringWriter();
s.Serialize(sw, in_Backup2Gmail);
return sw.ToString();
}
catch (Exception ex)
{
throw new Exception("Could not serialize " +
"Backup2Gmail. Error Msg: " + ex.Message, ex);
}
}
}
If you are new to serialization, you may want to take a look at the techniques I use in this class. You will see some properties have the attribute [XMLIgnore()]
- this will instruct the serializer to exclude the property from the XML file.
The key properties in this class are:
- The root folder that you want to backup
- Gmail account
- Password for Gmail
- Include and exclude rules
You may want to hardwire some exclusions and inclusions in this file to handle your situation. The file filtering is done using a combination of include and exclude patterns:
IncludeFilePatterns
ExcludedFilePattern
ExcludedExactFolderNames
ExcludedFoldersContains
ExcludedExtensionStartsWith
ExcludedExtensionExactMatch
Points of Interest
I first tried using the Zip utilities built into .NET, but after spending way too much time trying to decipher what the geeks in Redmond were thinking, I switched to the more logical DotNetZip libraries.
This program is designed mainly for programmers who need to backup code. However, you can also use it for documents and other files that are less than 20 MB when zipped. It is not intended to be a full backup system, and it won't work if you try to backup large files like SQL Server files.
History
- Version 2.0 - CodeProject upload - May 1, 2009.