Click here to Skip to main content
Click here to Skip to main content

Backup Project Files to Gmail

, 1 May 2009
Rate this:
Please Sign up or sign in to vote.
A WinForms application with command line execution that scans a folder tree, creates one or more Zip files, and emails them to a Gmail account along with a formatted message.

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";
//string userName = GmailAccount;//sending Id
//string password = GmailPassword;
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.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

About the Author

Mitch Stephens
President Actmate
United States United States
Based in North Carolina, I have been developing software applications for most of my career.
 
Currently marketing an HR performance management application called Talent Management Suite

Comments and Discussions

 
QuestionMy vote of 4 PinmemberReiss26-Jul-11 23:39 
GeneralMy vote of 3 Pinmemberhadre7-Jun-11 21:55 
General.NET 4.0 Framework patch needed for this to work in Visual Studio 2010 with files larger than 3MB PinmemberTed210217-Dec-10 21:42 
GeneralVery cool, but cautionary note: it does not backup any lib and dll files even when they are not in Debug and Release folders PinmemberTed210217-Dec-10 21:19 
GeneralMy Vote of 5 PinmemberRaviRanjankr27-Nov-10 4:43 
GeneralGood job PinmemberDr.Luiji12-Apr-10 1:07 
Generalif you zip with password google can't enter in archive PinmemberDAJG16-Jan-10 3:32 
QuestionIn framework 2.0 ? PinmemberMathiyazhagan29-Jul-09 20:21 
AnswerRe: In framework 2.0 ? PinmemberMitch Stephens30-Jul-09 9:54 
GeneralRe: In framework 2.0 ? PinmemberMathiyazhagan2-Aug-09 21:58 
GeneralPlease include async operation - It's blocking the UI Pinmemberdhaneel13-Jun-09 17:23 
GeneralRe: Please include async operation - It's blocking the UI PinmemberMitch Stephens14-Jun-09 8:04 
GeneralGood work PinmemberDonsw19-May-09 4:52 
QuestionThanks, I've been looking for something simple and easy to use for my mother in law PinmemberLars.C.Hassing6-May-09 9:14 
AnswerRe: Thanks, I've been looking for something simple and easy to use for my mother in law PinmemberMitch Stephens6-May-09 9:26 
GeneralRe: Thanks, I've been looking for something simple and easy to use for my mother in law PinmemberLars.C.Hassing6-May-09 9:38 
GeneralGood work!! Pinmemberm_irfan5-May-09 11:11 
GeneralRe: Good work!! PinmemberMitch Stephens5-May-09 13:29 
Generalwell done Pinmembersnoopy0015-May-09 8:25 
GeneralRe: well done PinmemberMitch Stephens5-May-09 8:37 
GeneralYou mean sending codes to Email PinmemberAbhishek Sur4-May-09 21:51 
GeneralRe: You mean sending codes to Email PinmemberMitch Stephens5-May-09 1:44 
QuestionGetting error while runing exe, can somebody suggest anything?? PinmemberMember 42124104-May-09 20:47 
AnswerRe: Getting error while runing exe, can somebody suggest anything?? PinmemberMitch Stephens5-May-09 1:45 
GeneralRe: Getting error while runing exe, can somebody suggest anything?? PinmemberMember 42124105-May-09 2:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 1 May 2009
Article Copyright 2009 by Mitch Stephens
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid