Index: src/f-spot.glade
===================================================================
--- src/f-spot.glade	(revision 4052)
+++ src/f-spot.glade	(working copy)
@@ -4869,6 +4869,22 @@
               </packing>
             </child>
             <child>
+              <widget class="GtkCheckButton" id="duplicate_check">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Include Duplicates</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">0</property>
+                <property name="active">False</property>
+                <property name="draw_indicator">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
               <widget class="GtkCheckButton" id="copy_check">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
@@ -4881,7 +4897,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
-                <property name="position">3</property>
+                <property name="position">4</property>
               </packing>
             </child>
             <child>
@@ -4898,7 +4914,7 @@
                 <property name="expand">False</property>
                 <property name="fill">False</property>
                 <property name="pack_type">GTK_PACK_END</property>
-                <property name="position">3</property>
+                <property name="position">5</property>
               </packing>
             </child>
           </widget>
Index: src/PhotoStore.cs
===================================================================
--- src/PhotoStore.cs	(revision 4052)
+++ src/PhotoStore.cs	(working copy)
@@ -56,6 +56,32 @@
 	}
 
 	//
+	// MD5 Calculator
+	//
+
+	private static System.Security.Cryptography.MD5 md5_generator;
+
+	private static System.Security.Cryptography.MD5 MD5Generator {
+		get {
+			if (md5_generator == null)
+			 	md5_generator = new System.Security.Cryptography.MD5CryptoServiceProvider ();
+
+			return md5_generator;
+		} 
+	}
+
+	public static string GenerateMD5 (System.Uri uri)
+	{
+		using (FSpot.ImageFile img = FSpot.ImageFile.Create (uri)) {
+			Pixbuf pixbuf = img.Load (256, 256);
+			byte[] serialized = PixbufSerializer.Serialize (pixbuf);
+			byte[] md5 = MD5Generator.ComputeHash (serialized);
+
+			return Convert.ToBase64String (md5);
+		}
+	}
+
+	//
 	// Generates the thumbnail, returns the Pixbuf, and also stores it as a side effect
 	//
 
@@ -121,7 +147,8 @@
 			"	description        TEXT NOT NULL,	           " +
 			"	roll_id            INTEGER NOT NULL,		   " +
 			"	default_version_id INTEGER NOT NULL,		   " +
-			"	rating		   INTEGER NULL			   " +
+			"	rating		   INTEGER NULL,		   " +
+			"	md5_sum		   TEXT NULL  			   " +
 			")");
 
 
@@ -144,7 +171,30 @@
 			")");
 	}
 
+	public Photo CheckForDuplicate (System.Uri uri) {
+		// Here we can go wild in comparing photos,
+		// for now we check on uri and md5
+		Photo found = GetByUri (uri);
+		
+		if (found != null)
+		 	return found;
 
+		string md5 = GenerateMD5 (uri);			
+		Gnome.Vfs.FileInfo info = new Gnome.Vfs.FileInfo (uri.ToString ());
+
+		Photo[] md5_matches = GetByMD5 (md5);
+
+		foreach (Photo match in md5_matches)
+		{
+		 	Gnome.Vfs.FileInfo match_info = new Gnome.Vfs.FileInfo (match.DefaultVersionUri.ToString ());
+
+			if (info.Size == match_info.Size)
+			 	return match;
+		}
+
+		return null;
+	}
+
 	[Obsolete ("Use Create (Uri, uint, out Pixbuf) instead")]
 	public Photo Create (string path, uint roll_id, out Pixbuf thumbnail)
 	{
@@ -168,18 +218,24 @@
 		using (FSpot.ImageFile img = FSpot.ImageFile.Create (orig_uri)) {
 			long unix_time = DbUtils.UnixTimeFromDateTime (img.Date);
 			string description = img.Description != null  ? img.Description.Split ('\0') [0] : String.Empty;
+			string md5_sum = GenerateMD5 (new_uri);
 	
-	 		uint id = (uint) Database.Execute (new DbCommand (
-				"INSERT INTO photos (time, uri, description, roll_id, default_version_id, rating) "	+
-	 			"VALUES (:time, :uri, :description, :roll_id, :default_version_id, :rating)",
-	 			"time", unix_time,
-				"uri", new_uri.OriginalString,
-	 			"description", description,
-				"roll_id", roll_id,
-	 			"default_version_id", Photo.OriginalVersionId,
-				"rating", "0"));
+	 		uint id = (uint) Database.Execute (
+				new DbCommand (
+					"INSERT INTO photos (time, uri, description, roll_id, default_version_id, rating, md5_sum) "	+
+	 				"VALUES (:time, :uri, :description, :roll_id, :default_version_id, :rating, :md5_sum)",
+	 				"time", unix_time,
+					"uri", new_uri.OriginalString,
+	 				"description", description,
+					"roll_id", roll_id,
+	 				"default_version_id", Photo.OriginalVersionId,
+					"rating", "0",
+					"md5_sum", md5_sum
+				)
+			);
 	
 			photo = new Photo (id, unix_time, new_uri);
+			photo.MD5Sum = md5_sum;
 			AddToCache (photo);
 			photo.Loaded = true;
 	
@@ -192,7 +248,13 @@
 
 	private void GetVersions (Photo photo)
 	{
-		SqliteDataReader reader = Database.Query(new DbCommand("SELECT version_id, name, uri, protected FROM photo_versions WHERE photo_id = :id", "id", photo.Id));
+		SqliteDataReader reader = Database.Query(
+			new DbCommand("SELECT version_id, name, uri, protected " + 
+				      "FROM photo_versions " + 
+				      "WHERE photo_id = :id", 
+				      "id", photo.Id
+			)
+		);
 
 		while (reader.Read ()) {
 			uint version_id = Convert.ToUInt32 (reader [0]);
@@ -291,8 +353,12 @@
 		if (photo != null)
 			return photo;
 
-		SqliteDataReader reader = Database.Query(new DbCommand("SELECT time, uri, description, roll_id, default_version_id, rating "
-			+ "FROM photos WHERE id = :id", "id", id));
+		SqliteDataReader reader = Database.Query(
+			new DbCommand("SELECT time, uri, description, roll_id, default_version_id, rating, md5_sum " + 
+				      "FROM photos " + 
+				      "WHERE id = :id", "id", id
+				     )
+		);
 
 		if (reader.Read ()) {
 			photo = new Photo (id,
@@ -307,6 +373,7 @@
 			photo.RollId = Convert.ToUInt32 (reader[3]);
 			photo.DefaultVersionId = Convert.ToUInt32 (reader[4]);
 			photo.Rating = Convert.ToUInt32 (reader [5]);
+			photo.MD5Sum = reader[6].ToString ();
 			AddToCache (photo);
 		}
 		reader.Close();
@@ -330,8 +397,12 @@
 	{
 		Photo photo = null;
 
-		SqliteDataReader reader = Database.Query (new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating FROM photos "
-                + "WHERE uri = :uri", "uri", uri.ToString ()));
+		SqliteDataReader reader = Database.Query (
+			new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating, md5_sum " + 
+				       "FROM photos " + 
+				       "WHERE uri = :uri", "uri", uri.ToString ()
+				      )
+		);
 
 		if (reader.Read ()) {
 			photo = new Photo (Convert.ToUInt32 (reader [0]),
@@ -342,15 +413,18 @@
 			photo.RollId = Convert.ToUInt32 (reader[3]);
 			photo.DefaultVersionId = Convert.ToUInt32 (reader[4]);
 			photo.Rating = Convert.ToUInt32 (reader [5]);
+			photo.MD5Sum = reader[6].ToString ();
 		}
 	        reader.Close();
 
 		if (photo == null)
 			return null;
 
-		if (LookupInCache (photo.Id) as Photo != null)
-			return LookupInCache (photo.Id) as Photo;
+		Photo cached = LookupInCache (photo.Id) as Photo;
 
+		if (cached != null)
+			return cached;
+
 		AddToCache (photo);
 	
 		GetTags (photo);
@@ -359,6 +433,57 @@
 		return photo;
 	}
 
+	public Photo[] GetByMD5 (string md5_sum)
+	{
+		List<Photo> photos = new List<Photo> ();
+		
+		SqliteDataReader reader = Database.Query (
+			new DbCommand ("SELECT id, time, uri, description, roll_id, default_version_id, rating " + 
+				       "FROM photos " + 
+				       "WHERE md5_sum = :md5_sum ", "md5_sum", md5_sum
+				      )
+		);
+
+		while (reader.Read ()) {
+			Photo photo = new Photo (Convert.ToUInt32 (reader [0]),
+					   Convert.ToInt64 (reader [1]),
+#if MONO_2_0
+				new System.Uri (reader [2].ToString ())
+#else
+				new System.Uri (reader [2].ToString (), true)
+#endif
+					   );
+
+			photo.Description = reader[3].ToString ();
+			photo.RollId = Convert.ToUInt32 (reader[4]);
+			photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);
+			photo.Rating = Convert.ToUInt32 (reader [6]);
+			photo.MD5Sum = md5_sum;
+
+			// get cached if possible
+			Photo cached = LookupInCache (photo.Id) as Photo;
+
+			if (cached != null)
+			{
+				photos.Add (cached);
+				continue;
+			}
+
+			// Add to cache and fully load if not found in cache
+			AddToCache (photo);
+	
+			GetTags (photo);
+			GetVersions (photo);
+
+			// add to collection
+			photos.Add (photo);
+		}
+
+	        reader.Close();
+
+		return photos.ToArray ();
+	}
+
 	public void Remove (Tag []tags)
 	{
 		Photo [] photos = Query (tags, String.Empty, null, null);	
@@ -418,28 +543,37 @@
 	private void Update (Photo photo) {
 		// Update photo.
 
-		Database.ExecuteNonQuery (new DbCommand (
-			"UPDATE photos SET description = :description, " + 
-			"default_version_id = :default_version_id, " + 
-			"time = :time, " + 
-			"uri = :uri, " +
-			"rating = :rating " +
-			"WHERE id = :id ",
-			"description", photo.Description,
-			"default_version_id", photo.DefaultVersionId,
-			"time", DbUtils.UnixTimeFromDateTime (photo.Time),
-			"uri", photo.VersionUri (Photo.OriginalVersionId).OriginalString,
-			"rating", String.Format ("{0}", photo.Rating),
-			"id", photo.Id));
+		Database.ExecuteNonQuery (
+			new DbCommand (
+				"UPDATE photos " + 
+				"SET description = :description, " + 
+				"    default_version_id = :default_version_id, " + 
+				"    time = :time, " + 
+				"    uri = :uri, " +
+				"    rating = :rating, " +
+				"    md5_sum = :md5_sum	" +
+				"WHERE id = :id ",
+				"description", photo.Description,
+				"default_version_id", photo.DefaultVersionId,
+				"time", DbUtils.UnixTimeFromDateTime (photo.Time),
+				"uri", photo.VersionUri (Photo.OriginalVersionId).OriginalString,
+				"rating", String.Format ("{0}", photo.Rating),
+				"md5_sum", photo.MD5Sum,
+				"id", photo.Id
+			)
+		);
 
 		// Update tags.
 
 		Database.ExecuteNonQuery (new DbCommand ("DELETE FROM photo_tags WHERE photo_id = :id", "id", photo.Id));
 
 		foreach (Tag tag in photo.Tags) {
-			Database.ExecuteNonQuery (new DbCommand ("INSERT INTO photo_tags (photo_id, tag_id) " +
-                                         " VALUES (:photo_id, :tag_id)",
-                                         "photo_id", photo.Id, "tag_id", tag.Id));
+			Database.ExecuteNonQuery (
+				new DbCommand ("INSERT INTO photo_tags (photo_id, tag_id) " +
+                                               "VALUES (:photo_id, :tag_id)",
+                                               "photo_id", photo.Id, "tag_id", tag.Id
+				)
+			);
 		}
 
 		// Update versions.
@@ -451,10 +585,13 @@
 				continue;
 			PhotoVersion version = photo.GetVersion (version_id) as PhotoVersion;
 
-			Database.ExecuteNonQuery(new DbCommand ("INSERT INTO photo_versions (photo_id, version_id, name, uri, protected) " +
-							     "       VALUES (:photo_id, :version_id, :name, :uri, :is_protected)",
-							     "photo_id", photo.Id, "version_id", version.VersionId,
-							     "name", version.Name, "uri", version.Uri.ToString (), "is_protected", version.IsProtected));
+			Database.ExecuteNonQuery(
+				new DbCommand ("INSERT INTO photo_versions (photo_id, version_id, name, uri, protected) " +
+					       "VALUES (:photo_id, :version_id, :name, :uri, :is_protected)",
+					       "photo_id", photo.Id, "version_id", version.VersionId,
+					       "name", version.Name, "uri", version.Uri.ToString (), "is_protected", version.IsProtected
+				)
+			);
 		}
 	}
 	
@@ -539,6 +676,7 @@
 				photo.RollId = Convert.ToUInt32 (reader[4]);
 				photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);
 				photo.Rating = Convert.ToUInt32 (reader [6]);
+				photo.MD5Sum = reader [6].ToString ();
 				
 				version_list.Add (photo);
 			}
@@ -577,7 +715,8 @@
 				"photos.description, "		+
 				"photos.roll_id, "		+
 				"photos.default_version_id, "	+
-				"photos.rating "		+
+				"photos.rating, "		+
+				"photos.md5_sum "		+
 			"FROM photos " 				+
 			"WHERE uri LIKE \"file://{0}%\" "	+
 			"AND uri NOT LIKE \"file://{0}/%/%\"" , dir.FullName);
@@ -627,7 +766,8 @@
 					     "photos.description, "		+
 				      	     "photos.roll_id, "   		+
 					     "photos.default_version_id, "	+
-					     "photos.rating "			+
+					     "photos.rating, "			+
+					     "photos.md5_sum "			+
 				      "FROM photos ");
 		
 		if (range != null) {
Index: src/ImportCommand.cs
===================================================================
--- src/ImportCommand.cs	(revision 4052)
+++ src/ImportCommand.cs	(working copy)
@@ -321,6 +321,7 @@
 	[Glade.Widget] Gtk.OptionMenu source_option_menu;
 	[Glade.Widget] Gtk.ScrolledWindow icon_scrolled;
 	[Glade.Widget] Gtk.ScrolledWindow photo_scrolled;
+	[Glade.Widget] Gtk.CheckButton duplicate_check;
 	[Glade.Widget] Gtk.CheckButton recurse_check;
 	[Glade.Widget] Gtk.CheckButton copy_check;
 	[Glade.Widget] Gtk.Button ok_button;
@@ -431,10 +432,8 @@
 	}
 
 	private bool Step ()
-	{			
-		Photo photo;
-		Pixbuf thumbnail;
-		int count;
+	{	
+	 	StepStatusInfo status_info;		
 		bool ongoing = true;
 
 		if (importer == null)
@@ -443,27 +442,27 @@
 		try {
 			// FIXME this is really just an incredibly ugly way of dealing
 			// with the recursive DoImport loops we sometimes get into
-			ongoing = importer.Step (out photo, out thumbnail, out count);
+			ongoing = importer.Step (out status_info);
 		} catch (ImportException e){
 			System.Console.WriteLine (e);
 			return false;
 		}
 
-		if (photo == null || thumbnail == null) {
+		if (!status_info.IsDuplicate && (status_info.Photo == null || status_info.Thumbnail == null)) {
 			Console.WriteLine ("Could not import file");
 		} else {
 			//icon_scrolled.Visible = true;
-			collection.Add (photo);
-		
+		 	if (!status_info.IsDuplicate)
+				collection.Add (status_info.Photo);
 			//grid.AddThumbnail (thumbnail);
 
 		}
 
-		if (thumbnail != null)
-			thumbnail.Dispose ();
+		if (status_info.Thumbnail != null)
+			status_info.Thumbnail.Dispose ();
 		
-		if (count < total)
-			UpdateProgressBar (count + 1, total);
+		if (status_info.Count < total)
+			UpdateProgressBar (status_info.Count + 1, total);
 
 		if (ongoing && total > 0)
 			return true;
@@ -600,6 +599,7 @@
 		this.Dialog.DefaultResponse = ResponseType.Ok;
 		
 		//import_folder_entry.Activated += HandleEntryActivate;
+		duplicate_check.Toggled += HandleRecurseToggled;
 		recurse_check.Toggled += HandleRecurseToggled;
 		copy_check.Toggled += HandleRecurseToggled;
 
@@ -776,9 +776,13 @@
 		bool recurse = true;
 		if (recurse_check != null)
 			recurse = recurse_check.Active;
+
+		bool include_duplicates = false;
+		if (include_duplicates != null)
+		 	include_duplicates = duplicate_check.Active;
 		
 //		importer = new FileImportBackend (store, pathimport, copy, recurse, null);
-		importer = new FileImportBackend (store, pathimport, copy, recurse, null, Dialog);
+		importer = new FileImportBackend (store, pathimport, copy, recurse, include_duplicates, null, Dialog);
 		AllowFinish = false;
 		
 		total = importer.Prepare ();
@@ -848,3 +852,48 @@
 
 #endif
 }
+
+public class StepStatusInfo {
+	private Photo photo;
+	private Pixbuf thumbnail;
+	private int count;
+	private bool is_duplicate;
+
+	public Photo Photo {
+		get  {
+			return photo; 
+		} 
+	}
+
+	public Pixbuf Thumbnail {
+		get {
+			return thumbnail; 
+		} 
+	}
+
+	public int Count {
+		get {
+			return count; 
+		} 
+	}
+
+	public bool IsDuplicate {
+		get {
+			return is_duplicate; 
+		} 
+	}
+
+	public StepStatusInfo (Photo photo, Pixbuf thumbnail, int count, bool is_duplicate)
+	{
+		this.photo = photo;
+		this.thumbnail = thumbnail;
+		this.count = count;
+		this.is_duplicate = is_duplicate;
+	}
+ 
+	public StepStatusInfo (Photo photo, Pixbuf thumbnail, int count)
+		: this (photo, thumbnail, count, false)
+	{ }
+}
+
+
Index: src/ImportBackend.cs
===================================================================
--- src/ImportBackend.cs	(revision 4052)
+++ src/ImportBackend.cs	(working copy)
@@ -7,7 +7,7 @@
 	public abstract int Prepare ();
 
 	// Import one picture.  Returns false when done; then you have to call Finish().
-	public abstract bool Step (out Photo photo, out Pixbuf thumbnail, out int count);
+	public abstract bool Step (out StepStatusInfo import_info);
 
 	// Cancel importing.
 	public abstract void Cancel ();
Index: src/FileImportBackend.cs
===================================================================
--- src/FileImportBackend.cs	(revision 4052)
+++ src/FileImportBackend.cs	(working copy)
@@ -23,6 +23,7 @@
 	TagStore tag_store = FSpot.Core.Database.Tags;
 	bool recurse;
 	bool copy;
+	bool include_duplicates;
 	string [] base_paths;
 	Tag [] tags;
 	Gtk.Window parent;
@@ -194,9 +195,11 @@
 		return dest;
 	}
 
-	public override bool Step (out Photo photo, out Pixbuf thumbnail, out int count)
+	public override bool Step (out StepStatusInfo status_info)
 	{
-		thumbnail = null;
+		Photo photo = null;
+		Pixbuf thumbnail = null;
+		bool is_duplicate = false;
 
 		if (import_info == null)
 			throw new ImportException ("Prepare() was not called");
@@ -212,41 +215,62 @@
 			string destination = info.OriginalPath;
 			if (copy)
 				destination = ChooseLocation (info.OriginalPath, directories);
-			
+
 			// Don't copy if we are already home
 			if (info.OriginalPath == destination) {
 				info.DestinationPath = destination;
-				photo = store.Create (info.DestinationPath, roll.Id, out thumbnail);
+
+				if (!include_duplicates)
+					photo = store.CheckForDuplicate (UriUtils.PathToFileUri (destination));
+
+				if (photo == null)
+					photo = store.Create (info.DestinationPath, roll.Id, out thumbnail);
+				else
+				 	is_duplicate = true;
 			} else {
 				System.IO.File.Copy (info.OriginalPath, destination);
 				info.DestinationPath = destination;
 
-				photo = store.Create (info.DestinationPath, info.OriginalPath, roll.Id, out thumbnail);
+				if (!include_duplicates)
+				 	photo = store.CheckForDuplicate (UriUtils.PathToFileUri (destination));
 
-				try {
-					File.SetAttributes (destination, File.GetAttributes (info.DestinationPath) & ~FileAttributes.ReadOnly);
-					DateTime create = File.GetCreationTime (info.OriginalPath);
-					File.SetCreationTime (info.DestinationPath, create);
-					DateTime mod = File.GetLastWriteTime (info.OriginalPath);
-					File.SetLastWriteTime (info.DestinationPath, mod);
-				} catch (IOException) {
-					// we don't want an exception here to be fatal.
+				if (photo == null)
+				{
+					photo = store.Create (info.DestinationPath, info.OriginalPath, roll.Id, out thumbnail);
+				 	
+
+					try {
+						File.SetAttributes (destination, File.GetAttributes (info.DestinationPath) & ~FileAttributes.ReadOnly);
+						DateTime create = File.GetCreationTime (info.OriginalPath);
+						File.SetCreationTime (info.DestinationPath, create);
+						DateTime mod = File.GetLastWriteTime (info.OriginalPath);
+						File.SetLastWriteTime (info.DestinationPath, mod);
+					} catch (IOException) {
+						// we don't want an exception here to be fatal.
+					}
 				}
+				else
+				{
+					is_duplicate = true; 
+				}
 			} 
 
-			if (tags != null) {
-				foreach (Tag t in tags) {
-					photo.AddTag (t);
+			if (!is_duplicate)
+			{
+				if (tags != null) {
+					foreach (Tag t in tags) {
+						photo.AddTag (t);
+					}
+					needs_commit = true;
 				}
-				needs_commit = true;
-			}
 
-			needs_commit |= xmptags.Import (photo, info.DestinationPath, info.OriginalPath);
+				needs_commit |= xmptags.Import (photo, info.DestinationPath, info.OriginalPath);
 
-			if (needs_commit)
-				store.Commit(photo);
-			
-			info.Photo = photo;
+				if (needs_commit)
+					store.Commit(photo);
+
+				info.Photo = photo;
+			}
 		} catch (System.Exception e) {
 			System.Console.WriteLine ("Error importing {0}{2}{1}", info.OriginalPath, e.ToString (), Environment.NewLine);
 			if (thumbnail != null)
@@ -269,7 +293,7 @@
 		}
 
 		this.count ++;
-		count = this.count;
+		status_info = new StepStatusInfo (photo, thumbnail, this.count, is_duplicate);
 
 		return (!abort && count != import_info.Count);
 	}
@@ -329,14 +353,17 @@
 	//rolls.EndImport();    // Clean up the imported session.
 	}
 
-	public FileImportBackend (PhotoStore store, string [] base_paths, bool recurse, Gtk.Window parent) : this (store, base_paths, false, recurse, null, parent) {}
+	public FileImportBackend (PhotoStore store, string [] base_paths, bool recurse, Gtk.Window parent) : this (store, base_paths, false, recurse, false, null, parent) {}
 
-	public FileImportBackend (PhotoStore store, string [] base_paths, bool copy, bool recurse, Tag [] tags, Gtk.Window parent)
+	public FileImportBackend (PhotoStore store, string [] base_paths, bool copy, bool recurse, Tag [] tags, Gtk.Window parent) : this (store, base_paths, copy, recurse, false, null, parent) {}
+
+	public FileImportBackend (PhotoStore store, string [] base_paths, bool copy, bool recurse, bool include_duplicates, Tag [] tags, Gtk.Window parent)
 	{
 		this.store = store;
 		this.copy = copy;
 		this.base_paths = base_paths;
 		this.recurse = recurse;
+		this.include_duplicates = include_duplicates;
 		this.tags = tags;
 		this.parent = parent;
 	}
Index: src/Core/Photo.cs
===================================================================
--- src/Core/Photo.cs	(revision 4052)
+++ src/Core/Photo.cs	(working copy)
@@ -174,6 +174,12 @@
 					rating = value;
 			}
 		}
+
+		private string md5_sum;
+		public string MD5Sum {
+			get { return md5_sum; }
+			set { md5_sum = value; } 
+		}
 	
 		// Version management
 		public const int OriginalVersionId = 1;
Index: src/Updater.cs
===================================================================
--- src/Updater.cs	(revision 4052)
+++ src/Updater.cs	(working copy)
@@ -246,8 +246,57 @@
 				Execute ("UPDATE photos SET rating = 0 WHERE rating IS NULL");
 			});
 
-			// Update to version 14.0
-			//AddUpdate (new Version (14,0),delegate () {
+			// Update to version 15.0
+			AddUpdate (new Version (15,0), delegate () {
+			 	string temp_table = MoveTableToTemp ("photos");
+ 
+ 				Execute ("CREATE TABLE photos ( " +
+					 "	id                 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,   " +
+					 "	time               INTEGER NOT NULL,	   	   " +
+					 "	uri		   STRING NOT NULL,		   " +
+					 "	description        TEXT NOT NULL,	           " +
+					 "	roll_id            INTEGER NOT NULL,		   " +
+					 "	default_version_id INTEGER NOT NULL,		   " +
+					 "	rating		   INTEGER NULL,		   " +
+					 "	md5_sum		   TEXT NULL  			   " +
+ 					 ")"
+				);
+ 
+ 				Execute (string.Format ("INSERT INTO photos (id, time, uri, description, roll_id, " + 
+							"default_version_id, rating, md5_sum) " + 
+ 							"SELECT id, time, uri, description, roll_id, " +
+ 							"       default_version_id, rating, '' " +
+ 							"FROM   {0} ", 
+ 							temp_table
+						       )
+				);
+ 
+ 				SqliteDataReader reader = ExecuteReader ("SELECT id, uri FROM photos");
+     
+ 				while (reader.Read ()) {
+ 					uint photo_id = Convert.ToUInt32 (reader [0]);
+#if MONO_2_0
+					System.Uri uri = new System.Uri (reader[1].ToString ());
+#else
+					System.Uri uri = new System.Uri (reader[1].ToString (), true);
+#endif
+ 
+ 					string md5_sum = PhotoStore.GenerateMD5 (uri);
+ 
+ 					if (md5_sum == null)
+ 					 	continue;
+ 
+ 					ExecuteScalar (string.Format (  "UPDATE photos " + 
+ 									"SET md5_sum = '{0}' " +
+ 									"WHERE id = {1}",
+ 									md5_sum,
+ 									photo_id));
+ 				}
+			}, true);
+
+			// Update to version 16.0
+			// Update to version 16.0
+			//AddUpdate (new Version (16,0),delegate () {
 			//	do update here
 			//});
 		}
