Index: src/f-spot.glade
===================================================================
--- src/f-spot.glade	(revision 4171)
+++ 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/TagStore.cs
===================================================================
--- src/TagStore.cs	(revision 4171)
+++ src/TagStore.cs	(working copy)
@@ -34,7 +34,8 @@
 	public static byte [] Serialize (Pixbuf pixbuf)
 	{
 		Pixdata pixdata = new Pixdata ();
-		pixdata.FromPixbuf (pixbuf, true); // FIXME GTK# shouldn't this be a constructor or something?
+		IntPtr raw_pixdata = pixdata.FromPixbuf (pixbuf, true); // FIXME GTK# shouldn't this be a constructor or something?
+									//       It's probably because we need the IntPtr to free it afterwards
 
 		uint data_length;
 		IntPtr raw_data = gdk_pixdata_serialize (ref pixdata, out data_length);
@@ -42,8 +43,8 @@
 		byte [] data = new byte [data_length];
 		Marshal.Copy (raw_data, data, 0, (int) data_length);
 		
-		GLib.Marshaller.Free (raw_data);
-
+		GLib.Marshaller.Free (new IntPtr[] { raw_data, raw_pixdata });
+		
 		return data;
 	}
 }
Index: src/PhotoStore.cs
===================================================================
--- src/PhotoStore.cs	(revision 4171)
+++ src/PhotoStore.cs	(working copy)
@@ -144,7 +144,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  			   " +
 			")");
 
 
@@ -163,11 +164,42 @@
 			"       version_id      INTEGER,  	" +
 			"       name            STRING,    	" +
 			"	uri		STRING NOT NULL," +
+			"	md5_sum		STRING NOT NULL," +
 			"	protected	BOOLEAN		" +
 			")");
 	}
 
+	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 = Photo.GenerateMD5 (uri);			
+		Gnome.Vfs.FileInfo info = new Gnome.Vfs.FileInfo (uri.ToString (), FileInfoOptions.GetMimeType);
+
+		Photo[] md5_matches = GetByMD5 (md5);
+
+		foreach (Photo match in md5_matches)
+		{
+		 	Gnome.Vfs.FileInfo match_info = new Gnome.Vfs.FileInfo (match.DefaultVersionUri.ToString (), FileInfoOptions.GetMimeType);
+
+			// same mimetype?
+			if (info.MimeType != match_info.MimeType)
+			 	continue;
+
+			// other comparisons?
+
+			// TODO? load pixbuf and compare sizes?	
+
+			return match;
+		}
+
+		return null;
+	}
+
 	[Obsolete ("Use Create (Uri, uint, out Pixbuf) instead")]
 	public Photo Create (string path, uint roll_id, out Pixbuf thumbnail)
 	{
@@ -191,18 +223,23 @@
 		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 = Photo.GenerateMD5 (new_uri);
+
+	 		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
+				)
+			);
 	
-	 		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"));
-	
-			photo = new Photo (id, unix_time, new_uri);
+			photo = new Photo (id, unix_time, new_uri, md5_sum);
 			AddToCache (photo);
 			photo.Loaded = true;
 	
@@ -215,7 +252,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, md5_sum, protected " + 
+				      "FROM photo_versions " + 
+				      "WHERE photo_id = :id", 
+				      "id", photo.Id
+			)
+		);
 
 		while (reader.Read ()) {
 			uint version_id = Convert.ToUInt32 (reader [0]);
@@ -225,8 +268,9 @@
 #else
 			System.Uri uri = new System.Uri (reader[2].ToString (), true);
 #endif
-			bool is_protected = Convert.ToBoolean (reader[3]);
-			photo.AddVersionUnsafely (version_id, uri, name, is_protected);
+			string md5_sum = reader[3].ToString ();
+			bool is_protected = Convert.ToBoolean (reader[4]);
+			photo.AddVersionUnsafely (version_id, uri, md5_sum, name, is_protected);
 		}
 		reader.Close();
 	}
@@ -244,7 +288,7 @@
 	}		
 	
 	private void GetAllVersions  () {
-		SqliteDataReader reader = Database.Query("SELECT photo_id, version_id, name, uri, protected FROM photo_versions");
+		SqliteDataReader reader = Database.Query("SELECT photo_id, version_id, name, uri, md5_sum, protected FROM photo_versions");
 		
 		while (reader.Read ()) {
 			uint id = Convert.ToUInt32 (reader [0]);
@@ -268,8 +312,9 @@
 #else
 				System.Uri uri = new System.Uri (reader[3].ToString (), true);
 #endif
-				bool is_protected = Convert.ToBoolean (reader[4]);	
-				photo.AddVersionUnsafely (version_id, uri, name, is_protected);
+				string md5_sum = reader[4].ToString ();
+				bool is_protected = Convert.ToBoolean (reader[5]);	
+				photo.AddVersionUnsafely (version_id, uri, md5_sum, name, is_protected);
 			}
 
 			/*
@@ -314,17 +359,23 @@
 		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,
 				Convert.ToInt64 (reader [0]),
 #if MONO_2_0
-				new System.Uri (reader [1].ToString ()));
+				new System.Uri (reader [1].ToString ()),
 #else
-				new System.Uri (reader [1].ToString (), true));
+				new System.Uri (reader [1].ToString (), true),
 #endif
+				reader[6].ToString ()
+			);
 
 			photo.Description = reader[2].ToString ();
 			photo.RollId = Convert.ToUInt32 (reader[3]);
@@ -354,15 +405,19 @@
 		Photo photo = null;
 
 		uint timer = Log.DebugTimerStart ();
-		SqliteDataReader reader = Database.Query (new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating " + 
-									 " FROM photos " +
-									 " JOIN photo_versions AS pv ON photos.id = pv.photo_id" +
-							                 " WHERE photos.uri = :uri OR pv.uri = :uri", "uri", uri.ToString ()));
+		SqliteDataReader reader = Database.Query (
+			new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating " + 
+				       " FROM photos " +
+				       " JOIN photo_versions AS pv ON photos.id = pv.photo_id" +
+				       " WHERE photos.uri = :uri OR pv.uri = :uri", "uri", uri.ToString ()
+			)
+		);
 
 		if (reader.Read ()) {
 			photo = new Photo (Convert.ToUInt32 (reader [0]),
 					   Convert.ToInt64 (reader [1]),
-					   uri);
+					   uri,
+					   reader[6].ToString ());
 
 			photo.Description = reader[2].ToString ();
 			photo.RollId = Convert.ToUInt32 (reader[3]);
@@ -375,9 +430,11 @@
 		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);
@@ -386,6 +443,63 @@
 		return photo;
 	}
 
+	public Photo[] GetByMD5 (string md5_sum)
+	{
+		List<Photo> photos = new List<Photo> ();
+		
+		SqliteDataReader reader = Database.Query (
+			new DbCommand ("SELECT DISTINCT " + 
+				       "id, time, photos.uri, description, roll_id, default_version_id, rating " + 
+				       "FROM photos " + 
+				       "LEFT JOIN photo_versions " + 
+				       "ON   photos.id = photo_versions.photo_id " +
+				       "WHERE photos.md5_sum = :md5_sum " +
+				       "OR photo_versions.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
+				md5_sum
+			);
+
+			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);	
@@ -468,28 +582,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.
@@ -501,12 +624,47 @@
 				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, md5_sum, protected) " +
+					       "VALUES (:photo_id, :version_id, :name, :uri, :md5_sum, :is_protected)",
+					       "photo_id", photo.Id, "version_id", version.VersionId,
+					       "name", version.Name, "uri", version.Uri.ToString (), "md5_sum", version.MD5Sum, "is_protected", version.IsProtected
+				)
+			);
 		}
 	}
+
+	public void UpdateMD5Sum (Photo photo) {
+		string md5_sum = Photo.GenerateMD5 (photo.VersionUri (Photo.OriginalVersionId)); 
+		photo.MD5Sum = md5_sum;
+
+		Database.ExecuteNonQuery (
+			new DbCommand (
+				"UPDATE photos " +
+				"SET    md5_sum = :md5_sum " +
+				"WHERE  ID = :id",
+				"md5_sum", md5_sum,
+				"id", photo.Id
+			)
+		);
+
+		bool needs_commit = false;
+
+		foreach (uint version_id in photo.VersionIds) {
+			if (version_id == Photo.OriginalVersionId)
+			 	continue;
+
+			PhotoVersion version = photo.GetVersion (version_id) as PhotoVersion;
+
+			string version_md5_sum = Photo.GenerateMD5 (version.Uri);
+			version.MD5Sum = version_md5_sum; 
+
+			needs_commit = true;
+		}
+
+		if (needs_commit)
+			Commit (photo, true, false);
+	}
 	
 	// Dbus
 	public event ItemsAddedHandler ItemsAddedOverDBus;
@@ -585,11 +743,12 @@
 				photo = new Photo (id,
 						   Convert.ToInt64 (reader [1]),
 #if MONO_2_0
-						   new System.Uri (reader [2].ToString ()));
+						   new System.Uri (reader [2].ToString ()),
 #else
-						   new System.Uri (reader [2].ToString (), true));
+						   new System.Uri (reader [2].ToString (), true),
 #endif
-				
+						   reader [6].ToString ()
+				);
 				photo.Description = reader[3].ToString ();
 				photo.RollId = Convert.ToUInt32 (reader[4]);
 				photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);
@@ -632,7 +791,8 @@
 				"photos.description, "		+
 				"photos.roll_id, "		+
 				"photos.default_version_id, "	+
-				"photos.rating "		+
+				"photos.rating, "		+
+				"photos.md5_sum "		+
 			"FROM photos " 				+
 			"WHERE uri LIKE \"file://:dir%\" "	+
 			"AND uri NOT LIKE \"file://:dir/%/%\"",
@@ -681,7 +841,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 4171)
+++ 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 4171)
+++ 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 4171)
+++ src/FileImportBackend.cs	(working copy)
@@ -23,11 +23,13 @@
 	TagStore tag_store = FSpot.Core.Database.Tags;
 	bool recurse;
 	bool copy;
+	bool include_duplicates;
 	string [] base_paths;
 	Tag [] tags;
 	Gtk.Window parent;
 
 	int count;
+	int duplicate_count;
 	XmpTagsImporter xmptags;
 
 	ArrayList import_info;
@@ -194,9 +196,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 +216,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, true, true);
-			
-			info.Photo = photo;
+				if (needs_commit)
+					store.Commit(photo, true, true);
+
+				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,8 +294,12 @@
 		}
 
 		this.count ++;
-		count = this.count;
 
+		if (is_duplicate)
+		 	this.duplicate_count ++;
+
+		status_info = new StepStatusInfo (photo, thumbnail, this.count, is_duplicate);
+
 		return (!abort && count != import_info.Count);
 	}
 
@@ -325,18 +354,25 @@
 
 		import_info = null;
 		xmptags.Finish();
-		count = 0;
+
+		if (count == duplicate_count)
+		 	rolls.Remove (roll);
+
+		count = duplicate_count = 0;
 	//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 4171)
+++ 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;
@@ -216,9 +222,9 @@
 	
 		// This doesn't check if a version of that name already exists, 
 		// it's supposed to be used only within the Photo and PhotoStore classes.
-		internal void AddVersionUnsafely (uint version_id, System.Uri uri, string name, bool is_protected)
+		internal void AddVersionUnsafely (uint version_id, System.Uri uri, string md5_sum, string name, bool is_protected)
 		{
-			Versions [version_id] = new PhotoVersion (this, version_id, uri, name, is_protected);
+			Versions [version_id] = new PhotoVersion (this, version_id, uri, md5_sum, name, is_protected);
 	
 			highest_version_id = Math.Max (version_id, highest_version_id);
 		}
@@ -233,7 +239,9 @@
 			if (VersionNameExists (name))
 				throw new ApplicationException ("A version with that name already exists");
 			highest_version_id ++;
-			Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, uri, name, is_protected);
+			string md5_sum = GenerateMD5 (uri);
+
+			Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, uri, name, md5_sum, is_protected);
 			return highest_version_id;
 		}
 	
@@ -373,6 +381,7 @@
 		{
 			System.Uri new_uri = GetUriForVersionName (name, System.IO.Path.GetExtension (VersionUri (base_version_id).AbsolutePath));
 			System.Uri original_uri = VersionUri (base_version_id);
+			string md5_sum = MD5Sum;
 	
 			if (VersionNameExists (name))
 				throw new Exception ("This version name already exists");
@@ -397,9 +406,13 @@
 	//				try {
 	//					Mono.Unix.Native.Syscall.chown(new_path, Mono.Unix.Native.Syscall.getuid (), stat.st_gid);
 	//				} catch (Exception) {}
+	//
+			} else {
+				md5_sum = Photo.GenerateMD5 (new_uri);
 			}
 			highest_version_id ++;
-			Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, new_uri, name, is_protected);
+
+			Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, new_uri, md5_sum, name, is_protected);
 	
 			return highest_version_id;
 		}
@@ -421,7 +434,7 @@
 					continue;
 	
 				highest_version_id ++;
-				Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, version.Uri, name, is_protected);
+				Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, version.Uri, version.MD5Sum, name, is_protected);
 	
 				return highest_version_id;
 			}
@@ -544,9 +557,39 @@
 			return tags.Contains (tag);
 		}
 	
+		//
+		// 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)
+		{
+		 	try {
+				Gdk.Pixbuf pixbuf = ThumbnailGenerator.Create (uri);
+				byte[] serialized = PixbufSerializer.Serialize (pixbuf);
+				byte[] md5 = MD5Generator.ComputeHash (serialized);
+				pixbuf.Dispose ();
+
+				return Convert.ToBase64String (md5);
+			} catch (Exception e) {
+			 	Log.DebugFormat("Failed to create MD5Sum for Uri {0}; {1}", uri, e.Message);
+			}
+
+			return string.Empty; 
+		}
+
+
 		// Constructor
-		public Photo (uint id, long unix_time, System.Uri uri)
+		public Photo (uint id, long unix_time, System.Uri uri, string md5_sum)
 			: base (id)
 		{
 			if (uri == null)
@@ -556,10 +599,11 @@
 	
 			description = String.Empty;
 			rating = 0;
+			this.md5_sum = md5_sum;
 	
 			// Note that the original version is never stored in the photo_versions table in the
 			// database.
-			AddVersionUnsafely (OriginalVersionId, uri, Catalog.GetString ("Original"), true);
+			AddVersionUnsafely (OriginalVersionId, uri, md5_sum, Catalog.GetString ("Original"), true);
 		}
 	}
 }
Index: src/Core/PhotoVersion.cs
===================================================================
--- src/Core/PhotoVersion.cs	(revision 4171)
+++ src/Core/PhotoVersion.cs	(working copy)
@@ -5,6 +5,7 @@
  *	Ettore Perazzoli <ettore@perazzoli.org>
  *	Larry Ewing <lewing@gnome.org>
  *	Stephane Delcroix <stephane@delcroix.org>
+ *	Thomas Van Machelen <thomas.vanmachelen@gmail.com>
  * 
  * This is free software. See COPYING for details.
  */
@@ -16,6 +17,7 @@
 		Photo photo;
 		uint version_id;
 		System.Uri uri;
+		string md5_sum;
 		string name;
 		bool is_protected;
 	
@@ -52,6 +54,11 @@
 				uri = value;
 			}
 		}
+
+		public string MD5Sum {
+			get { return md5_sum; } 
+			internal set { md5_sum = value; }
+		}
 	
 		public uint VersionId {
 			get { return version_id; }
@@ -65,11 +72,12 @@
 			get { return photo.Rating; }
 		}
 	
-		public PhotoVersion (Photo photo, uint version_id, System.Uri uri, string name, bool is_protected)
+		public PhotoVersion (Photo photo, uint version_id, System.Uri uri, string md5_sum, string name, bool is_protected)
 		{
 			this.photo = photo;
 			this.version_id = version_id;
 			this.uri = uri;
+			this.md5_sum = md5_sum;
 			this.name = name;
 			this.is_protected = is_protected;
 		}
Index: src/QueuedSqliteDatabase.cs
===================================================================
--- src/QueuedSqliteDatabase.cs	(revision 4171)
+++ src/QueuedSqliteDatabase.cs	(working copy)
@@ -353,7 +353,7 @@
             if(result != null) {
                 throw new ApplicationException("Command has alread been executed");
             }
-        
+
             try {
                 switch(command_type) {
                     case Banshee.Database.CommandType.Reader:
Index: src/PhotoView.cs
===================================================================
--- src/PhotoView.cs	(revision 4171)
+++ src/PhotoView.cs	(working copy)
@@ -19,6 +19,7 @@
 using System.Xml.Serialization;
 using Mono.Unix;
 
+using FSpot.Jobs;
 using FSpot.Xmp;
 using FSpot.Widgets;
 using FSpot.Utils;
@@ -280,6 +281,7 @@
 				sepia.Image = View.CompletePixbuf ();
 				sepia.Adjust ();
 				Core.Database.Photos.Commit (photo, true, true);
+				CalculateHashJob.Create (Core.Database.Jobs, photo.Id);
 			} catch (System.Exception e) {
 				ShowError (e, photo); 
 			}
@@ -294,6 +296,7 @@
 				desaturate.Image = View.CompletePixbuf ();
 				desaturate.Adjust ();
 				Core.Database.Photos.Commit (photo, true, true);
+				CalculateHashJob.Create (Core.Database.Jobs, photo.Id);
 			} catch (System.Exception e) {
 				ShowError (e, photo);
 			}
@@ -343,6 +346,8 @@
 				photo.SaveVersion (edited, create_version);
 				((PhotoQuery)query).Commit (Item.Index, true, true);
 	
+				CalculateHashJob.Create (Core.Database.Jobs, photo.Id);
+
 				// FIXME the fact that the selection doesn't go away is a bug in ImageView, it should
 				// be fixed there.
 				photo_view.Pixbuf = edited;
Index: src/Db.cs
===================================================================
--- src/Db.cs	(revision 4171)
+++ src/Db.cs	(working copy)
@@ -163,6 +163,10 @@
 	bool empty;
 	string path;
 
+	public delegate void InitializedHandler ();
+
+	public event InitializedHandler Initialized;
+
 	public TagStore Tags {
 		get { return tag_store; }
 	}
@@ -251,6 +255,10 @@
 		Database.CommitTransaction ();
 
 		empty = new_db;
+
+		if (Initialized != null)
+		 	Initialized ();
+
 		Log.DebugTimerPrint (timer, "Db Initialization took {0}");
 	}
 
Index: src/Jobs/CalculateHashJob.cs
===================================================================
--- src/Jobs/CalculateHashJob.cs	(revision 0)
+++ src/Jobs/CalculateHashJob.cs	(revision 0)
@@ -0,0 +1,50 @@
+/*
+ * Jobs/CalculateHashJob.cs
+ *
+ * Author(s)
+ *   Thomas Van Machelen <thomas.vanmachelen@gmail.com>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+using System;
+using Banshee.Kernel;
+using FSpot.Utils;
+
+namespace FSpot.Jobs {
+	public class CalculateHashJob : Job
+	{
+		public CalculateHashJob (uint id, string job_options, int run_at, JobPriority job_priority, bool persistent) 
+			: this (id, job_options, DbUtils.DateTimeFromUnixTime (run_at), job_priority, persistent)
+		{
+		}
+
+		public CalculateHashJob (uint id, string job_options, DateTime run_at, JobPriority job_priority, bool persistent) 
+			: base (id, job_options, job_priority, run_at, persistent)
+		{
+		}
+
+		public static CalculateHashJob Create (JobStore job_store, uint photo_id)
+		{
+			return (CalculateHashJob) job_store.CreatePersistent (typeof(FSpot.Jobs.CalculateHashJob), photo_id.ToString ()); 
+		}
+
+		protected override bool Execute ()
+		{
+			//this will add some more reactivity to the system
+			System.Threading.Thread.Sleep (200);
+
+			uint photo_id = Convert.ToUInt32 (JobOptions);
+			Log.DebugFormat ("Calculating Hash {0}...", photo_id);
+
+			try {
+			 	Photo photo = FSpot.Core.Database.Photos.Get (Convert.ToUInt32 (photo_id)) as Photo;
+				FSpot.Core.Database.Photos.UpdateMD5Sum (photo);
+			} catch (System.Exception e) {
+			 	Log.DebugFormat ("Error Calculating Hash for photo {0}: {1}", JobOptions, e.Message);
+			}
+			return false;
+		}
+	} 
+}
+
Index: src/Updater.cs
===================================================================
--- src/Updater.cs	(revision 4171)
+++ src/Updater.cs	(working copy)
@@ -246,8 +246,67 @@
 				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
+						       )
+				);
+
+
+				string temp_versions_table = MoveTableToTemp ("photo_versions");
+
+				Console.WriteLine("{0} - {1}", temp_table, temp_versions_table);
+
+				Execute ("CREATE TABLE photo_versions (    	" +
+					 "      photo_id        INTEGER,  	" +
+					 "      version_id      INTEGER,  	" +
+					 "      name            STRING,    	" +
+					 "	uri		STRING NOT NULL," +
+					 "	md5_sum		STRING NOT NULL," +
+					 "	protected	BOOLEAN		" +
+					 ")");
+
+				Execute (string.Format ("INSERT INTO photo_versions (photo_id, version_id, name, uri, md5_sum, protected) " + 
+ 							"SELECT photo_id, version_id, name, uri, '', protected " +
+ 							"FROM   {0} ", 
+ 							temp_versions_table
+						       )
+				);
+
+				// This is kind of hacky but should be a lot faster on
+				// large photo databases
+				Execute (string.Format ("INSERT INTO jobs (job_type, job_options, run_at, job_priority) " +
+							"SELECT '{0}', id, {1}, {2} " +
+							"FROM   photos ",
+							typeof(Jobs.CalculateHashJob).ToString (),
+							FSpot.Utils.DbUtils.UnixTimeFromDateTime (DateTime.Now),
+							0
+						       )
+				);
+ 			}, true);
+
+			// Update to version 16.0
+			// Update to version 16.0
+			//AddUpdate (new Version (16,0),delegate () {
 			//	do update here
 			//});
 		}
Index: src/Makefile.am
===================================================================
--- src/Makefile.am	(revision 4171)
+++ src/Makefile.am	(working copy)
@@ -158,6 +158,7 @@
 	$(srcdir)/Imaging/Tiff.cs		\
 	$(srcdir)/JobStore.cs			\
 	$(srcdir)/Jobs/SyncMetadataJob.cs	\
+	$(srcdir)/Jobs/CalculateHashJob.cs	\
 	$(srcdir)/Loupe.cs			\
 	$(srcdir)/MainWindow.cs			\
 	$(srcdir)/MemorySurface.cs		\
