f-spot r4357 - in trunk: . extensions extensions/TabbloExport extensions/TabbloExport/Tabblo



Author: lmilesi
Date: Tue Sep 16 09:18:33 2008
New Revision: 4357
URL: http://svn.gnome.org/viewvc/f-spot?rev=4357&view=rev

Log:
2008-09-16  Lorenzo Milesi <maxxer yetopen it>

        * TabbloExport/*: Tabblo.com export extension by Wojciech
        DzierÅanowski. Fix bgo#382658.



Added:
   trunk/extensions/TabbloExport/
   trunk/extensions/TabbloExport/ApplicationCentricCertificatePolicy.cs
   trunk/extensions/TabbloExport/AssemblyInfo.cs
   trunk/extensions/TabbloExport/BlindTrustCertificatePolicy.cs
   trunk/extensions/TabbloExport/FSpotUploadProgress.cs
   trunk/extensions/TabbloExport/Makefile.am
   trunk/extensions/TabbloExport/Preferences.cs
   trunk/extensions/TabbloExport/Tabblo/
   trunk/extensions/TabbloExport/Tabblo/AssemblyInfo.cs
   trunk/extensions/TabbloExport/Tabblo/Connection.cs
   trunk/extensions/TabbloExport/Tabblo/IPreferences.cs
   trunk/extensions/TabbloExport/Tabblo/Makefile.am
   trunk/extensions/TabbloExport/Tabblo/MultipartRequest.cs
   trunk/extensions/TabbloExport/Tabblo/Picture.cs
   trunk/extensions/TabbloExport/Tabblo/TabbloException.cs
   trunk/extensions/TabbloExport/Tabblo/TotalUploadProgress.cs
   trunk/extensions/TabbloExport/Tabblo/UploadProgressEventArgs.cs
   trunk/extensions/TabbloExport/Tabblo/UploadProgressEventHandler.cs
   trunk/extensions/TabbloExport/TabbloExport.addin.xml
   trunk/extensions/TabbloExport/TabbloExport.cs
   trunk/extensions/TabbloExport/TabbloExport.glade
   trunk/extensions/TabbloExport/TrustError.glade
   trunk/extensions/TabbloExport/UserDecisionCertificatePolicy.cs
Modified:
   trunk/configure.in
   trunk/extensions/ChangeLog
   trunk/extensions/Makefile.am

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Tue Sep 16 09:18:33 2008
@@ -368,6 +368,8 @@
 extensions/SmugMugExport/SmugMugNet/Makefile
 extensions/SmugMugExport/Makefile
 extensions/MergeDb/Makefile
+extensions/TabbloExport/Makefile
+extensions/TabbloExport/Tabblo/Makefile
 extensions/PicasaWebExport/Makefile
 extensions/PicasaWebExport/google-sharp/Makefile
 f-spot.pc

Modified: trunk/extensions/Makefile.am
==============================================================================
--- trunk/extensions/Makefile.am	(original)
+++ trunk/extensions/Makefile.am	Tue Sep 16 09:18:33 2008
@@ -8,6 +8,7 @@
 	FolderExport		\
 	MergeDb			\
  	PicasaWebExport		\
+ 	TabbloExport		\
  	SmugMugExport
 
 addinsdir = $(pkglibdir)

Added: trunk/extensions/TabbloExport/ApplicationCentricCertificatePolicy.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/ApplicationCentricCertificatePolicy.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,165 @@
+//
+// FSpotTabbloExport.ApplicationCentricCertificatePolicy
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Net;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Security.Cryptography.X509Certificates;
+
+using FSpot.Utils;
+
+namespace FSpotTabbloExport {
+	
+	class ApplicationCentricCertificatePolicy : ICertificatePolicy {
+
+		protected enum Decision {
+			DontTrust,
+			TrustOnce,
+			TrustAlways
+		};
+		
+		private Dictionary<string, int> cert_hashes;
+		
+		private static readonly IsolatedStorageFile isolated_store =
+				IsolatedStorageFile.GetUserStoreForAssembly ();
+
+		private const string StoreName = "cert_hashes";
+
+		
+		public bool CheckValidationResult (ServicePoint service_point,
+		                                   X509Certificate certificate,
+		                                   WebRequest request,
+						   int problem)
+		{
+			Log.DebugFormat ("Checking validation result for {0}: problem={1}", request.RequestUri, problem);
+			
+			if (0 == problem) {
+				return true;
+			}
+			
+			// Only try to deal with the problem if it is a trust
+			// failure.
+			if (-2146762486 != problem) {
+				return false;
+			}
+			
+			LoadCertificates ();
+			
+			string hash = certificate.GetCertHashString ();
+			Log.DebugFormat ("Certificate hash: " + hash);
+			
+			int stored_problem = 0;
+			if (cert_hashes.TryGetValue (hash, out stored_problem)
+					&& problem == stored_problem) {
+				Log.DebugFormat ("We already trust this site");
+				return true;
+			}
+			
+			Decision decision = GetDecision (certificate, request);
+			Log.DebugFormat ("Decision: " + decision);
+			
+			switch (decision) {
+			case Decision.DontTrust:
+				return false;
+			case Decision.TrustOnce:
+				return true;
+			case Decision.TrustAlways:
+				SaveCertificate (hash, problem);
+				return true;
+			default:
+				Debug.Assert (false, "Unknown decision");
+				return false;
+			}
+		}
+
+		
+		protected virtual Decision GetDecision (
+				X509Certificate certificate,
+				WebRequest request)
+		{
+			Decision decision = Decision.DontTrust;
+			Log.DebugFormat ("Making the default decision: " + decision);
+			return decision;
+		}
+		
+		
+		private void LoadCertificates ()
+		{
+			using (IsolatedStorageFileStream isol_stream =
+					new IsolatedStorageFileStream (
+							StoreName,
+							FileMode.OpenOrCreate,
+							FileAccess.Read,
+			                                isolated_store)) {
+				try {
+					BinaryFormatter formatter =
+							new BinaryFormatter ();
+					cert_hashes = (Dictionary<string, int>) 
+							formatter.Deserialize (
+								isol_stream);
+				} catch (SerializationException e) {
+					// FIXME: handle
+					Log.Exception (e);
+				}
+			}
+			
+			if (null == cert_hashes) {
+				cert_hashes = new Dictionary<string,int> ();
+			}
+		}
+
+		
+		private void SaveCertificate (string hash, int problem)
+		{
+			cert_hashes.Add (hash, problem);
+			
+			using (IsolatedStorageFileStream isolated_stream =
+					new IsolatedStorageFileStream (
+							StoreName,
+							FileMode.OpenOrCreate,
+							FileAccess.Write,
+			                                isolated_store)) {
+				try {
+					BinaryFormatter formatter =
+							new BinaryFormatter ();
+					formatter.Serialize (isolated_stream,
+							cert_hashes);
+				} catch (SerializationException e) {
+					// FIXME: handle
+					Log.Exception (e);
+				}
+			}
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/AssemblyInfo.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/AssemblyInfo.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,32 @@
+#region Using directives
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#endregion
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle ("TabbloExport")]
+[assembly: AssemblyDescription ("TabbloExport is an F-Spot extension "
+	+ "adding support for uploading images to Tabblo.")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("TabbloExport")]
+[assembly: AssemblyCopyright ("")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// This sets the default COM visibility of types in the assembly to invisible.
+// If you need to expose a type to COM, use [ComVisible(true)] on that type.
+[assembly: ComVisible (false)]
+
+// The assembly version has following format :
+//
+// Major.Minor.Build.Revision
+//
+// You can specify all the values or you can use the default the Revision and 
+// Build Numbers by using the '*' as shown below:
+[assembly: AssemblyVersion ("0.1.*")]

Added: trunk/extensions/TabbloExport/BlindTrustCertificatePolicy.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/BlindTrustCertificatePolicy.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,46 @@
+//
+// FSpotTabbloExport.BlindTrustCertificatePolicy
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Net;
+using System.Security.Cryptography.X509Certificates;
+
+using FSpot.Utils;
+
+namespace FSpotTabbloExport {
+	class BlindTrustCertificatePolicy : ICertificatePolicy {
+		public bool CheckValidationResult (ServicePoint service_point,
+		                                   X509Certificate certificate,
+		                                   WebRequest request,
+						   int problem)
+		{
+			Log.DebugFormat ("Blindly trusting " + request.RequestUri);
+			return true;
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/FSpotUploadProgress.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/FSpotUploadProgress.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,64 @@
+//
+// FSpotTabbloExport.FSpotUploadProgress
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using Mono.Tabblo;
+using Mono.Unix;
+using System;
+
+namespace FSpotTabbloExport {
+	
+	class FSpotUploadProgress : TotalUploadProgress	{
+		
+		private FSpot.ThreadProgressDialog progress_dialog;
+		
+		
+		internal FSpotUploadProgress (
+				Picture [] pictures,
+				FSpot.ThreadProgressDialog progress_dialog)
+			: base (pictures)
+		{
+			this.progress_dialog = progress_dialog;
+		}
+		
+		
+		protected override void ShowProgress (string title,
+		                                      long bytes_sent)
+		{
+			progress_dialog.Message = title;
+			progress_dialog.ProgressText = String.Format (
+					Catalog.GetString (
+							"{0} of approx. {1}"),
+					SizeUtil.ToHumanReadable (bytes_sent),
+					SizeUtil.ToHumanReadable (
+							(long) TotalFileSize));
+			progress_dialog.Fraction =
+					(double) bytes_sent / TotalFileSize;
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Makefile.am	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,63 @@
+include $(top_srcdir)/Makefile.include
+
+PLUGIN_NAME = TabbloExport
+
+PLUGIN_MANIFEST = $(PLUGIN_NAME).addin.xml
+
+PLUGIN_ASSEMBLY = $(PLUGIN_NAME).dll
+
+PLUGIN_SOURCES = \
+	$(srcdir)/ApplicationCentricCertificatePolicy.cs \
+	$(srcdir)/AssemblyInfo.cs \
+	$(srcdir)/BlindTrustCertificatePolicy.cs \
+	$(srcdir)/FSpotUploadProgress.cs \
+	$(srcdir)/Preferences.cs \
+	$(srcdir)/TabbloExport.cs \
+	$(srcdir)/UserDecisionCertificatePolicy.cs
+
+
+REFS = \
+	-r:Tabblo/Mono.Tabblo.dll \
+	$(LINK_KEYRING)		\
+	-r:Mono.Posix.dll
+
+PKGS = \
+	-pkg:f-spot \
+	-pkg:gtk-sharp-2.0 \
+	-pkg:glade-sharp-2.0 \
+	-pkg:gnome-vfs-sharp-2.0
+
+SUBDIRS = \
+	Tabblo
+
+RESOURCES = \
+	-resource:$(srcdir)/$(PLUGIN_MANIFEST) \
+	-resource:$(srcdir)/$(PLUGIN_NAME).glade \
+	-resource:$(srcdir)/TrustError.glade
+
+all: $(PLUGIN_ASSEMBLY)
+
+mpack: $(PLUGIN_ASSEMBLY)
+	mautil p $(PLUGIN_ASSEMBLY)
+
+$(PLUGIN_ASSEMBLY): $(PLUGIN_SOURCES) $(PLUGIN_MANIFEST)
+	$(MAKE) -C $(SUBDIRS)
+	$(CSC_LIB) -out:$@ $(PLUGIN_SOURCES) $(REFS) $(PKGS) $(ASSEMBLIES) $(RESOURCES)
+
+plugindir = $(pkglibdir)/extensions
+
+install-data-hook:
+	rm -f $(plugindir)/$(PLUGIN_NAME).addin.xml
+
+plugin_DATA =			\
+	$(PLUGIN_ASSEMBLY)
+
+EXTRA_DIST = 			\
+	$(PLUGIN_SOURCES)	\
+	$(PLUGIN_MANIFEST)	\
+	$(PLUGIN_NAME).glade
+
+CLEANFILES =			\
+	$(PLUGIN_ASSEMBLY)	\
+	$(PLUGIN_ASSEMBLY).mdb	\
+	*.mpack

Added: trunk/extensions/TabbloExport/Preferences.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Preferences.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,63 @@
+//
+// FSpotTabbloExport.Preferences
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace FSpotTabbloExport {
+	
+	class Preferences : Mono.Tabblo.IPreferences {
+		
+		private string username;
+		private string password;
+		
+		public string Username {
+			get {
+				return username;
+			}
+		}
+		public string Password {
+			get {
+				return password;
+			}
+		}
+		public string Privacy {
+			get {
+				return "circle";
+			}
+		}
+		
+		internal void SetUsername (string username) {
+			this.username = username;
+		}
+		
+		internal void SetPassword (string password) {
+			this.password = password;
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/AssemblyInfo.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/AssemblyInfo.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,32 @@
+#region Using directives
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+#endregion
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle ("Mono.Tabblo")]
+[assembly: AssemblyDescription ("A library implementing photo upload to Tabblo"
+	+ " over HTTP")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("Mono.Tabblo")]
+[assembly: AssemblyCopyright ("")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+
+// This sets the default COM visibility of types in the assembly to invisible.
+// If you need to expose a type to COM, use [ComVisible(true)] on that type.
+[assembly: ComVisible (false)]
+
+// The assembly version has following format :
+//
+// Major.Minor.Build.Revision
+//
+// You can specify all the values or you can use the default the Revision and 
+// Build Numbers by using the '*' as shown below:
+[assembly: AssemblyVersion ("0.1.*")]

Added: trunk/extensions/TabbloExport/Tabblo/Connection.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/Connection.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,464 @@
+//
+// Mono.Tabblo.Connection
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using Mono.Unix;
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Text;
+
+using FSpot.Utils;
+
+namespace Mono.Tabblo {
+
+	public class Connection {
+
+		private const string LoginUrl =
+				"https://store.tabblo.com:443/studio/authtoken";;
+		private const string AuthorizeUrl = "https://store.tabblo.com";
+				+ ":443/studio/upload/getposturl";
+		private const string RedirUrl =	"http://www.tabblo.com/studio";
+				+ "/token/{0}/?url=/studio"
+				+ "/report_upload_session";
+		
+		private readonly IPreferences preferences;
+		
+		private string auth_token = null;
+		private string session_upload_url = null;
+		
+		private CookieCollection cookies;
+
+
+		public Connection (IPreferences preferences)
+		{
+			if (null == preferences) {
+				throw new ArgumentNullException ("preferences");
+			}
+			this.preferences = preferences;
+			this.cookies = new CookieCollection ();
+		}
+		
+
+		public void UploadFile (string name, Stream data_stream,
+		                        string mime_type, string [,] arguments)
+		{
+			if (!IsAuthenticated ()) {
+				Login ();
+			}
+
+			Log.DebugFormat ("Uploading " + mime_type + " file " + name);
+			DoUploadFile (name, data_stream, mime_type, arguments);
+		}
+
+		
+		private void DoUploadFile (string name, Stream data_stream,
+		                           string mime_type,
+		                           string [,] arguments)
+		{
+			string upload_url = GetUploadUrl (arguments);
+			HttpWebRequest http_request = CreateHttpRequest (
+					upload_url, "POST", true);
+			MultipartRequest request =
+					new MultipartRequest (http_request);
+			
+			MemoryStream mem_stream = null;
+			if (null != UploadProgressHandler) {
+				// "Manual buffering" using a MemoryStream.
+				request.Request.AllowWriteStreamBuffering =
+						false;
+				mem_stream = new MemoryStream ();
+				request.OutputStream = mem_stream;
+			}
+			
+			request.BeginPart (true);
+			request.AddHeader ("Content-Disposition",
+					"form-data; name=\"filename0\"; "
+							+ "filename=\"" + name
+							+ '"',
+					false);
+			request.AddHeader ("Content-Type", mime_type, true);
+			
+			byte [] data_buffer = new byte [8192];
+			int read_count;
+			while ((read_count = data_stream.Read (
+					data_buffer, 0, data_buffer.Length))
+							> 0) {
+				request.WritePartialContent (
+						data_buffer, 0, read_count);
+			}
+			request.EndPartialContent ();
+			request.EndPart (true);
+			
+			if (null != UploadProgressHandler) {
+				
+				int total = (int) request.OutputStream.Length;
+				request.Request.ContentLength = total;
+
+				string progress_title = String.Format (
+						Catalog.GetString ("Uploading "
+								+ "photo "
+								+ "\"{0}\""),
+						name);
+				
+				using (Stream request_stream = request.Request
+						.GetRequestStream ()) {
+					byte [] buffer =
+							mem_stream.GetBuffer ();
+					int write_count = 0;
+					for (int offset = 0; offset < total;
+							offset += write_count) {
+						FireUploadProgress (
+								progress_title,
+								offset, total);
+						write_count = System.Math.Min (
+								16384,
+								total - offset);
+						request_stream.Write (buffer,
+								offset,
+								write_count);
+					}
+					FireUploadProgress (progress_title,
+							total, total);
+				}
+			}
+			
+			SendRequest ("upload", request.Request, true); 
+		}
+
+
+		public event UploadProgressEventHandler UploadProgressHandler;
+
+		private void FireUploadProgress (string title, int sent,
+		                                 int total)
+		{
+			if (null != UploadProgressHandler) {
+				UploadProgressEventArgs args =
+						new UploadProgressEventArgs (
+								title, sent,
+								total);
+				UploadProgressHandler (this, args);
+			}
+		}
+		
+
+		private bool IsAuthenticated ()
+		{
+			return null != auth_token;
+		}
+
+
+		private void Login ()
+		{
+			FireUploadProgress (Catalog.GetString (
+						"Logging into Tabblo"),
+					0, 0);
+			
+			auth_token = null;
+
+			HttpWebRequest request = CreateHttpRequest (
+					LoginUrl, "POST");
+			request.ContentType =
+					"application/x-www-form-urlencoded";
+			
+			string [,] arguments = {
+				{"username", preferences.Username},
+				{"password", preferences.Password}
+			};
+
+			try {
+				WriteRequestContent (request,
+						FormatRequestArguments (
+								arguments));
+				string response = SendRequest (
+						"login", request);
+				if ("BAD".Equals (response)) {
+					Log.DebugFormat ("Invalid username or password");
+					throw new TabbloException (
+						"Login failed: Invalid username"
+						+ " or password");
+				}
+				
+				auth_token = response;
+				
+			} catch (TabbloException e) {
+				// Here's us trying to produce a more
+				// descriptive message when we have... trust
+				// issues.  This doesn't work, though, at least
+				// as long as Mono bug #346635 is not fixed.
+				//
+				// TODO: When it _starts_ to work, we should
+				// think about doing the same for
+				// `GetUploadUrl()'.
+				WebException we = e.InnerException
+						as WebException;
+				if (null != we) 
+					Log.DebugFormat ("Caught a WebException, status=" + we.Status);
+				if (null != we
+					&& WebExceptionStatus.TrustFailure
+							== we.Status) {
+					throw new TabbloException (
+							"Trust failure", we);
+				}
+				throw;
+			}
+			
+			if  (null != auth_token)
+				Log.DebugFormat  ("Login successful. Token: " + auth_token);
+		}
+		
+		
+		private string GetUploadUrl (string [,] arguments)
+		{
+			FireUploadProgress (Catalog.GetString (
+						"Obtaining URL for upload"),
+					0, 0);
+			
+			if (! IsAuthenticated ())
+				Log.DebugFormat ("Not authenticated");
+			
+			if (null == session_upload_url) {
+				
+				string [,] auth_arguments =
+						{ {"auth_token", auth_token} };
+				string url = AuthorizeUrl + "/?"
+						+ FormatRequestArguments (
+								auth_arguments);
+				
+				HttpWebRequest request =
+						CreateHttpRequest (url, "GET");
+
+				string response = SendRequest (
+						"getposturl", request);
+				
+				if (response.StartsWith ("@")) {
+					session_upload_url =
+							response.Substring (1);
+				} else {
+					throw new TabbloException (
+							"Session upload URL "
+							+ "retrieval failed");
+				}
+			}
+
+			string upload_url = session_upload_url;
+			upload_url += "&redir=" + String.Format (
+					RedirUrl, auth_token);
+			if (null != arguments && arguments.GetLength (0) > 0) {
+				upload_url += '&' + FormatRequestArguments (
+						arguments);
+			}
+			
+			Log.DebugFormat ("Upload URL: " + upload_url);
+			return upload_url;
+		}
+
+
+		private HttpWebRequest CreateHttpRequest (string url,
+		                                          string method)
+		{
+			return CreateHttpRequest (url, method, false);
+		}
+		
+		private HttpWebRequest CreateHttpRequest (string url,
+		                                          string method,
+		                                          bool with_cookies)
+		{
+			HttpWebRequest request = (HttpWebRequest)
+					WebRequest.Create (url);
+			// For some reason, POST requests are _really_ slow with
+			// HTTP 1.1.
+			request.ProtocolVersion = HttpVersion.Version10;
+			request.Method = method;
+			if (with_cookies) {
+				HandleRequestCookies (request);
+			}
+			return request;
+		}
+		
+
+		private void HandleRequestCookies (HttpWebRequest request)
+		{
+			request.CookieContainer = new CookieContainer ();
+			// Instead of just doing a
+			// `request.CookieContainer.Add(cookies)', add cookies
+			// mannually to work around the fact that some cookies
+			// are not properly formatted as they are received from
+			// the server.
+			foreach (Cookie c in cookies) {
+				Cookie new_cookie = new Cookie (c.Name, c.Value,
+						"/", ".tabblo.com");
+				request.CookieContainer.Add (new_cookie);
+			}
+			
+			string cookie_header = request.CookieContainer
+					.GetCookieHeader (request.RequestUri);
+			if (cookie_header.Length > 0)
+				Log.DebugFormat ("Cookie: " + cookie_header);
+		}
+
+		
+		private static void WriteRequestContent (HttpWebRequest request,
+		                                         string content)
+		{
+			byte [] content_bytes =
+					Encoding.UTF8.GetBytes (content);
+
+			request.ContentLength = content_bytes.Length;
+
+			try {
+				using (Stream request_stream =
+						request.GetRequestStream ()) {
+					request_stream.Write (content_bytes, 0,
+							content_bytes.Length);
+				}
+			} catch (WebException e) {
+				Log.Exception (e);
+				throw new TabbloException (
+						"HTTP request failure: "
+								+ e.Message,
+						e);
+			}
+
+			char [] content_chars = new char [content_bytes.Length];
+			content_bytes.CopyTo (content_chars, 0);
+			Log.DebugFormat ("Request content: " + new string (content_chars));
+		}
+
+
+		private static string FormatRequestArguments (
+				string [,] arguments)
+		{
+			StringBuilder content = new StringBuilder ();
+
+			for (int i = 0; i < arguments.GetLength (0); ++i) {
+				content.AppendFormat( "{0}={1}&",
+						arguments [i, 0],
+						arguments [i, 1]);
+			}
+			
+			if (content.Length > 0) {
+				content.Remove (content.Length - 1, 1);
+			}
+				
+			byte [] content_bytes =	Encoding.UTF8.GetBytes (
+					content.ToString ());
+			char [] content_chars = new char [content_bytes.Length];
+			content_bytes.CopyTo (content_chars, 0);
+			
+			return new string (content_chars);
+		}
+
+
+		private string SendRequest (string description,
+		                            HttpWebRequest request)
+		{
+			return SendRequest (description, request, false);
+		}
+		
+		/// <summary>
+		/// Sends an HTTP request.
+		/// </summary>
+		/// <param name="description"></param>
+		/// <param name="request"></param>
+		/// <returns>the HTTP response as string</returns>
+		private string SendRequest (string description,
+		                            HttpWebRequest request,
+		                            bool keep_cookies)
+		{
+			Log.DebugFormat ("Sending " + description + ' ' + request.Method + " request to " + request.Address);
+
+			HttpWebResponse response = null;
+			try {
+				response = (HttpWebResponse)
+						request.GetResponse ();
+				if (keep_cookies) {
+					cookies.Add (response.Cookies);
+					Log.DebugFormat (response.Cookies.Count + " cookie(s)");
+					foreach (Cookie c in response.Cookies) {
+						Log.DebugFormat ("Set-Cookie: " + c.Name + '=' + c.Value + "; Domain=" + c.Domain + "; expires=" + c.Expires);
+					}
+				}
+				return GetResponseAsString (response);
+			} catch (WebException e) {
+				Log.Exception (e);
+				HttpWebResponse error_response =
+						e.Response as HttpWebResponse;
+				string response_string = null != error_response
+						? GetResponseAsString (
+								error_response)
+						: "reason unknown";
+				throw new TabbloException (description
+							+ " failed: "
+							+ response_string,
+						e);
+			} finally {
+				if (null != response) {
+					response.Close ();
+				}
+			}
+		}
+		
+		
+		private static string GetResponseAsString (HttpWebResponse response)
+		{
+			Log.DebugFormat ("Response: ");
+			
+			Encoding encoding = Encoding.UTF8;
+			if (response.ContentEncoding.Length > 0) {
+				try {
+					encoding = Encoding.GetEncoding (
+							response
+							.ContentEncoding);
+				} catch (ArgumentException) {
+					// Swallow invalid encoding exception
+					// and use the default one.
+				}
+			}
+			
+			string response_string = null;
+			
+			using (Stream stream = response.GetResponseStream ()) {
+				StreamReader reader = new StreamReader (
+						stream, encoding);
+				response_string = reader.ReadToEnd ();
+				stream.Close ();
+			}
+
+			if (null != response_string)
+				try {
+					Log.DebugFormat (response_string); 
+				} catch (System.FormatException e) {
+					Log.DebugFormat ("Unable to print respose string: not in correct format");
+				}
+			return response_string;
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/IPreferences.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/IPreferences.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,50 @@
+//
+// Mono.Tabblo.IPreferences
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace Mono.Tabblo {
+
+	/// <summary>
+	/// Interface for a set of Tabblo user preferences.
+	/// </summary>
+	public interface IPreferences {
+		string Username {
+			get;
+		}
+		string Password {
+			get;
+		}
+		// FIXME: It _should_ be possible to set a photo's privacy when
+		// uploading, but that's not implemented yet.
+		string Privacy {
+			get;
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/Makefile.am	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,38 @@
+include $(top_srcdir)/Makefile.include
+
+ASSEMBLY_NAME = Mono.Tabblo
+
+ASSEMBLY_SOURCES = \
+	$(srcdir)/AssemblyInfo.cs \
+	$(srcdir)/Connection.cs \
+	$(srcdir)/IPreferences.cs \
+	$(srcdir)/MultipartRequest.cs \
+	$(srcdir)/Picture.cs \
+	$(srcdir)/TabbloException.cs \
+	$(srcdir)/TotalUploadProgress.cs \
+	$(srcdir)/UploadProgressEventArgs.cs \
+	$(srcdir)/UploadProgressEventHandler.cs
+
+REFS = \
+       -r:Mono.Posix.dll
+
+PKGS = \
+	-pkg:f-spot 
+
+ASSEMBLY = $(ASSEMBLY_NAME).dll
+
+all: $(ASSEMBLY)
+
+$(ASSEMBLY): $(ASSEMBLY_SOURCES)
+	$(CSC_LIB) -out:$@ $(PKGS) $(REFS) $(ASSEMBLY_SOURCES)
+
+assemblydir = $(pkglibdir)
+assembly_DATA =	$(ASSEMBLY)
+
+EXTRA_DIST =			\
+	$(ASSEMBLY_SOURCES)	\
+	License.txt
+
+CLEANFILES =			\
+	$(ASSEMBLY)		\
+	$(ASSEMBLY).mdb

Added: trunk/extensions/TabbloExport/Tabblo/MultipartRequest.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/MultipartRequest.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,223 @@
+//
+// Mono.Tabblo.MultipartRequest
+//
+// Authors:
+//	Gonzalo Paniagua Javier (gonzalo ximian com)
+//	Stephane Delcroix (stephane delcroix org)
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2006 Novell, Inc. (http://www.novell.com)
+// (C) Copyright 2007 S. Delcroix
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+
+using FSpot.Utils;
+
+namespace Mono.Tabblo {
+	
+	class MultipartRequest {
+
+		private const string VerboseSymbol =
+				"FSPOT_TABBLO_EXPORT_VERBOSE";
+
+		private static readonly byte [] CRLF = { 13, 10 };
+		
+		private const string SeparatorString = "PART_SEPARATOR";
+		private const string Separator =
+				"--" + SeparatorString + "\r\n";
+		private const string SeparatorEnd =
+				"--" + SeparatorString + "--\r\n";
+		
+		private static readonly byte [] SeparatorBytes =
+				Encoding.ASCII.GetBytes (Separator);
+		private static readonly byte [] SeparatorEndBytes =
+				Encoding.ASCII.GetBytes (SeparatorEnd);
+		
+		private HttpWebRequest request;
+		private Stream output_stream;
+		
+		bool output_set;
+
+
+		public MultipartRequest (HttpWebRequest http_request)
+		{
+			request = http_request;
+			request.ContentType = "multipart/form-data; boundary="
+					+ SeparatorString;
+		}
+
+		
+		public HttpWebRequest Request {
+			get {
+				return request;
+			}
+		}
+
+		public Stream OutputStream {
+			get {
+				return output_stream;
+			}
+			set {
+				output_set = true;
+				output_stream = value;
+			}
+		}
+
+		
+		public void BeginPart ()
+		{
+			BeginPart (false);
+		}
+
+		public void BeginPart (bool first)
+		{
+			if (!first) {
+				return;
+			}
+
+			LogBeginPart ();
+			InitContent ();
+			AppendContent (SeparatorBytes, 0,
+					SeparatorBytes.Length);
+		}
+
+		
+		public void AddHeader (string name, string val)
+		{
+			AddHeader (name, val, false);
+		}
+
+		public void AddHeader (string name, string val, bool last)
+		{
+			AddHeader (String.Format ("{0}: {1}", name, val), last);
+		}
+
+		public void AddHeader (string header)
+		{
+			AddHeader (header, false);
+		}
+
+		public void AddHeader (string header, bool last)
+		{
+			bool need_crlf = !header.EndsWith ("\r\n");
+			byte [] bytes = Encoding.UTF8.GetBytes (header);
+			AppendContent (bytes, 0, bytes.Length);
+			if (need_crlf) {
+				AppendContent (CRLF, 0, CRLF.Length);
+			}
+			if (last) {
+				AppendContent (CRLF, 0, CRLF.Length);
+			}
+		}
+
+		
+		public void WriteContent (string content)
+		{
+			WriteContent (Encoding.UTF8.GetBytes (content));
+		}
+
+		public void WriteContent (byte [] content)
+		{
+			AppendContent (content, 0, content.Length);
+			AppendContent (CRLF, 0, CRLF.Length);
+		}
+
+		public void WritePartialContent (byte [] content, int offset,
+		                                 int nbytes)
+		{
+			AppendContent (content, offset, nbytes);
+		}
+
+		
+		public void EndPartialContent ()
+		{
+			AppendContent (CRLF, 0, CRLF.Length);
+		}
+
+		public void EndPart (bool last)
+		{
+			if (last) {
+				LogEndPart ();
+				AppendContent (SeparatorEndBytes, 0,
+						SeparatorEndBytes.Length);
+				CloseContent ();
+			} else {
+				AppendContent (SeparatorBytes, 0,
+						SeparatorBytes.Length);
+			}
+		}
+		
+		
+		private void InitContent ()
+		{
+			if (output_stream == null) {
+				output_stream = request.GetRequestStream ();
+			}
+		}
+
+		private void CloseContent ()
+		{
+			if (!output_set) {
+				output_stream.Close ();
+			}
+		}
+		
+		private void AppendContent (byte [] content, int offset,
+		                            int length)
+		{
+			LogContent (content, offset, length);
+			output_stream.Write (content, offset, length);
+		}
+
+
+
+		[Conditional (VerboseSymbol)]
+		private static void LogBeginPart ()
+		{
+			Log.DebugFormat (">>>START MultipartRequest content");
+		}
+
+		[Conditional (VerboseSymbol)]
+		private static void LogEndPart ()
+		{
+			Log.DebugFormat ("<<<END MultipartRequest content");
+		}
+
+		[Conditional (VerboseSymbol)]
+		private static void LogContent (byte [] content, int offset,
+		                                int length)
+		{
+			char [] content_chars = new char [length];
+			Array.Copy (content, offset, content_chars, 0, length);
+			Log.DebugFormat (new string (content_chars));
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/Picture.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/Picture.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,100 @@
+//
+// Mono.Tabblo.Picture
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+
+namespace Mono.Tabblo {
+
+	public class Picture {
+
+		private readonly string name;
+		private readonly Uri uri;
+		private readonly string mime_type;
+		private readonly string privacy;
+
+		
+		public string Name {
+			get {
+				return name;
+			}
+		}
+		public Uri Uri {
+			get {
+				return uri;
+			}
+		}
+		public string MimeType {
+			get {
+				return mime_type;
+			}
+		}
+		public string Privacy {
+			get {
+				return privacy;
+			}
+		}
+		
+		
+		public Picture (string name, Uri uri, string mime_type,
+		                string privacy)
+		{
+			if (null == name) {
+				throw new ArgumentNullException ("name");
+			}
+			if (null == uri) {
+				throw new ArgumentNullException ("uri");
+			}
+			if (null == mime_type) {
+				throw new ArgumentNullException ("mime_type");
+			}
+			if (null == privacy) {
+				throw new ArgumentNullException ("privacy");
+			}
+			this.name = name;
+			this.uri = uri;
+			this.mime_type = mime_type;
+			this.privacy = privacy;
+		}
+		
+		
+		public void Upload (Connection connection)
+		{
+			if (null == connection) {
+				throw new ArgumentNullException ("connection");
+			}
+			
+			using (Stream data_stream =
+					File.OpenRead (Uri.LocalPath)) {
+				connection.UploadFile (Name, data_stream,
+						MimeType, null);
+			}
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/TabbloException.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/TabbloException.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,53 @@
+//
+// Mono.Tabblo.TabbloException
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace Mono.Tabblo {
+
+	public class TabbloException : ApplicationException {
+
+		public TabbloException ()
+		{
+		}
+		
+		public TabbloException (string message) : base (message)
+		{
+		}
+
+		public TabbloException (Exception cause) : base ("", cause)
+		{
+		}
+		
+		public TabbloException (string message, Exception cause)
+			: base (message, cause)
+		{
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/TotalUploadProgress.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/TotalUploadProgress.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,114 @@
+//
+// Mono.Tabblo.TotalUploadProgress
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Diagnostics;
+
+using FSpot.Utils;
+
+namespace Mono.Tabblo {
+
+	public class TotalUploadProgress {
+
+		private long prev_bytes_sent = 0;
+		private long bytes_sent_total = 0;
+		
+		private int total_file_count;
+		private ulong total_file_size;
+		
+		
+		public TotalUploadProgress (Picture [] pictures)
+		{
+			if (null == pictures) Log.DebugFormat ("No pictures!");
+			
+			total_file_count = pictures.Length;
+			total_file_size = GetTotalFileSize (pictures);
+		}
+
+		
+		protected int TotalFileCount {
+			get {
+				return total_file_count;
+			}
+		}
+		protected ulong TotalFileSize {
+			get {
+				return total_file_size;
+			}
+		}
+		
+		
+		public void HandleProgress (object sender,
+		                            UploadProgressEventArgs args)
+		{
+			bytes_sent_total += args.BytesSent - prev_bytes_sent;
+			ShowProgress (args.Title, bytes_sent_total);
+			
+			int percent = (int)
+					((double) bytes_sent_total * 100
+				 			/ TotalFileSize);
+			Log.DebugFormat ("{0}%...", percent);
+
+			if (args.BytesTotal == args.BytesSent) {
+				prev_bytes_sent = 0;
+			} else {
+				prev_bytes_sent = args.BytesSent;
+			}
+		}
+		
+
+		/// <summary>
+		/// Overriden by subclasses to display upload progress.
+		/// </summary>
+		/// <remarks>
+		/// The default implementation does nothing.
+		/// </remarks>
+		protected virtual void ShowProgress (string title,
+		                                     long bytes_sent)
+		{
+		}
+		
+		
+		private static ulong GetTotalFileSize (Picture [] pictures)
+		{
+			if (null == pictures) Log.DebugFormat ("No pictures!");
+			
+			ulong size = 0;
+			
+			foreach (Picture picture in pictures) {
+				FileInfo info = new FileInfo (
+						picture.Uri.LocalPath);
+				size += (ulong) info.Length;
+			}
+			
+			return size;
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/UploadProgressEventArgs.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/UploadProgressEventArgs.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,68 @@
+//
+// Mono.Tabblo.UploadProgressEventArgs
+//
+// Authors:
+//	Gonzalo Paniagua Javier (gonzalo ximian com)
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2006 Novell, Inc. (http://www.novell.com)
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+namespace Mono.Tabblo {
+	
+	public sealed class UploadProgressEventArgs : EventArgs {
+		
+		private readonly string title;
+		private readonly long bytes_sent;
+		private readonly long bytes_total;
+
+		internal UploadProgressEventArgs (string title, long bytes_sent,
+		                                  long bytes_total)
+		{
+			this.title = title;
+			this.bytes_sent = bytes_sent;
+			this.bytes_total = bytes_total;
+		}
+
+		public string Title {
+			get {
+				return title;
+			}
+		}
+
+		public long BytesSent {
+			get {
+				return bytes_sent;
+			}
+		}
+
+		public long BytesTotal {
+			get {
+				return bytes_total;
+			}
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/Tabblo/UploadProgressEventHandler.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/Tabblo/UploadProgressEventHandler.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,37 @@
+//
+// Mono.abblo.UploadProgressEventHandler
+//
+// Authors:
+//	Gonzalo Paniagua Javier (gonzalo ximian com)
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2006 Novell, Inc. (http://www.novell.com)
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace Mono.Tabblo {
+	
+	public delegate void UploadProgressEventHandler (
+			object sender, UploadProgressEventArgs args);
+	
+}

Added: trunk/extensions/TabbloExport/TabbloExport.addin.xml
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/TabbloExport.addin.xml	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,16 @@
+<Addin namespace="FSpot"
+	version="0.4.4.100"
+	name="Tabblo Export"
+	description="This extension allows you to export your photos to Tabblo."
+	author="Wojciech Dzierzanowski"
+	defaultEnabled="false"
+	category="Export">
+
+	<Dependencies>
+		<Addin id="Core" version="0.4.4.100"/>
+	</Dependencies>
+
+	<Extension path = "/FSpot/Menus/Exports">
+		<ExportMenuItem id="Tabblo" _label = "_Tabblo..." class = "FSpotTabbloExport.TabbloExport" />
+	</Extension>
+</Addin>

Added: trunk/extensions/TabbloExport/TabbloExport.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/TabbloExport.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,284 @@
+//
+// FSpotTabbloExport.TabbloExport
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using Mono.Tabblo;
+using Mono.Unix;
+
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Net;
+using System.Threading;
+
+using FSpot.Utils;
+
+namespace FSpotTabbloExport {
+	/// <summary>
+	/// </summary>
+	public class TabbloExport : FSpot.Extensions.IExporter {
+		
+		private readonly Preferences preferences;
+		private readonly Connection connection;
+		
+		private FSpot.IBrowsableCollection photos;
+
+		private const string DialogName = "tabblo_export_dialog";
+		[Glade.Widget] Gtk.Dialog dialog;
+		[Glade.Widget] Gtk.Entry username_entry;
+		[Glade.Widget] Gtk.Entry password_entry;
+		[Glade.Widget] Gtk.Button export_button;
+		[Glade.Widget] Gtk.Button cancel_button;
+		[Glade.Widget] Gtk.ScrolledWindow thumb_scrolled_window;
+		
+		private FSpot.ThreadProgressDialog progress_dialog;
+
+		// Keyring constants.
+		private const string KeyringItemName = "Tabblo Account";
+		private const string KeyringItemApp = "FSpotTabbloExport";
+		private const string KeyringItemNameAttr = "name";
+		private const string KeyringItemUsernameAttr = "username";
+		private const string KeyringItemAppAttr = "application";
+		
+
+		public TabbloExport ()
+		{
+			preferences = new Preferences ();
+			connection = new Connection (preferences);
+		}
+
+		
+		public void Run (FSpot.IBrowsableCollection photos)
+		{
+			if (null == photos) {
+				throw new ArgumentNullException ("photos");
+			}
+			
+			this.photos = photos;
+			
+			Glade.XML glade_xml = new Glade.XML (
+					null, "TabbloExport.glade", DialogName,
+					"f-spot");
+			glade_xml.Autoconnect (this);
+
+			dialog = (Gtk.Dialog) glade_xml.GetWidget (DialogName);
+			
+			FSpot.Widgets.IconView icon_view =
+					new FSpot.Widgets.IconView (photos);
+			icon_view.DisplayDates = false;
+			icon_view.DisplayTags = false;
+
+			username_entry.Changed += HandleAccountDataChanged;
+			password_entry.Changed += HandleAccountDataChanged;
+			ReadAccountData ();
+			HandleAccountDataChanged (null, null);
+			
+			dialog.Modal = false;
+			dialog.TransientFor = null;
+			
+			dialog.Response += HandleResponse;
+			
+			thumb_scrolled_window.Add (icon_view);
+			icon_view.Show ();
+			dialog.Show ();
+		}
+		
+
+		private void HandleAccountDataChanged (object sender,
+		                                       EventArgs args)
+		{
+			preferences.SetUsername (username_entry.Text);
+			preferences.SetPassword (password_entry.Text);
+			
+			export_button.Sensitive =
+					preferences.Username.Length > 0
+					&& preferences.Password.Length > 0;
+		}
+		
+		
+		private void HandleResponse (object sender,
+		                             Gtk.ResponseArgs args)
+		{
+			dialog.Destroy ();
+			
+			if (Gtk.ResponseType.Ok != args.ResponseId) {
+				Log.DebugFormat ("Tabblo export was canceled.");
+				return;
+			}
+			
+			WriteAccountData ();
+			
+			Log.DebugFormat ("Starting Tabblo export");
+			
+			Thread upload_thread =
+					new Thread (new ThreadStart (Upload));
+			progress_dialog = new FSpot.ThreadProgressDialog (
+					upload_thread, photos.Items.Length);
+			progress_dialog.Start ();
+		}
+		
+		
+		private void Upload ()
+		{
+			if (null == connection) Log.DebugFormat ("No connection");
+			
+			Picture [] pictures = GetPicturesForUpload (); 
+				
+			FSpotUploadProgress fup = new FSpotUploadProgress (
+					pictures, progress_dialog);
+			connection.UploadProgressHandler += fup.HandleProgress;
+			
+			ServicePointManager.CertificatePolicy =
+					new UserDecisionCertificatePolicy ();
+			
+			try {
+				foreach (Picture picture in pictures) {
+					picture.Upload (connection);
+				}
+				
+				progress_dialog.Message = Catalog.GetString (
+						"Done sending photos");
+				progress_dialog.ProgressText = Catalog
+						.GetString ("Upload complete");
+				progress_dialog.Fraction = 1;
+				progress_dialog.ButtonLabel = Gtk.Stock.Ok;
+				
+			} catch (TabbloException e) {
+				progress_dialog.Message = Catalog.GetString (
+						"Error uploading to Tabblo: ")
+						+ e.Message;
+				progress_dialog.ProgressText =
+						Catalog.GetString ("Error");
+				// FIXME:  Retry logic? 
+//				  progressDialog.PerformRetrySkip ();
+				Log.DebugFormat ("Error uploading:\n" + e);
+			} finally {
+				connection.UploadProgressHandler -=
+						fup.HandleProgress;
+			}
+		}
+		
+		
+		private Picture [] GetPicturesForUpload ()
+		{
+			if (null == photos) Log.DebugFormat ("No photos");
+			if (null == preferences) Log.DebugFormat ("No preferences");
+			
+			Picture [] pictures = new Picture [photos.Items.Length];
+
+			for (int i = 0; i < pictures.Length; ++i) {
+				FSpot.IBrowsableItem photo = photos.Items [i];
+
+				// FIXME: GnomeVFS is deprecated, we should use
+				// GIO instead.  However, I don't know how to
+				// call `GLib.Content.TypeGuess ()'.
+				string path = photo.DefaultVersionUri.LocalPath;
+				string mime_type = Gnome.Vfs.MimeType
+						.GetMimeTypeForUri (path);
+
+				pictures [i] = new Picture (photo.Name,
+						photo.DefaultVersionUri,
+						mime_type,
+						preferences.Privacy);
+			}
+
+			return pictures;
+		}
+		
+		
+		private void ReadAccountData ()
+		{
+			Hashtable attrs = new Hashtable ();
+			attrs [KeyringItemNameAttr] = KeyringItemName;
+			attrs [KeyringItemAppAttr] = KeyringItemApp;
+			
+			try {
+				Gnome.Keyring.ItemType type = Gnome.Keyring
+						.ItemType.GenericSecret;
+				Gnome.Keyring.ItemData [] items =
+						Gnome.Keyring.Ring.Find (
+								type, attrs);
+				if (items.Length > 1)
+					Log.Warning ("More than one " + KeyringItemName + "found in keyring");
+			
+				if (1 <= items.Length) {
+					Log.DebugFormat (KeyringItemName + " data found in " + "keyring");
+					attrs =	items [0].Attributes;
+					username_entry.Text = (string) attrs [
+						KeyringItemUsernameAttr];
+					password_entry.Text = items [0].Secret;
+				}
+				
+			} catch (Gnome.Keyring.KeyringException e) {
+				Log.DebugFormat ("Error while reading account data:\n" + e);
+			}
+		}
+		
+		
+		private void WriteAccountData ()
+		{
+			try {
+				string keyring = Gnome.Keyring
+						.Ring.GetDefaultKeyring ();
+				
+				Hashtable attrs = new Hashtable ();
+				attrs [KeyringItemNameAttr] = KeyringItemName;
+				attrs [KeyringItemAppAttr] = KeyringItemApp;
+
+				Gnome.Keyring.ItemType type = Gnome.Keyring
+						.ItemType.GenericSecret;
+
+				try {
+					Gnome.Keyring.ItemData [] items = Gnome
+							.Keyring.Ring.Find (
+									type,
+									attrs);
+							
+					foreach (Gnome.Keyring.ItemData item
+							in items) {
+						Gnome.Keyring.Ring.DeleteItem (
+								keyring,
+								item.ItemID);
+					}
+				} catch (Gnome.Keyring.KeyringException e) {
+					Log.DebugFormat ("Error while deleting old account data:\n" + e);
+				}
+				
+				attrs [KeyringItemUsernameAttr] =
+						preferences.Username;
+
+				Gnome.Keyring.Ring.CreateItem (keyring, type,
+						KeyringItemName, attrs,
+						preferences.Password, true);
+				
+			} catch (Gnome.Keyring.KeyringException e) {
+				Log.DebugFormat ("Error while writing account data:\n" + e);
+			}
+		}
+	}
+}

Added: trunk/extensions/TabbloExport/TabbloExport.glade
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/TabbloExport.glade	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--*- mode: xml -*-->
+<glade-interface>
+  <requires lib="canvas"/>
+  <requires lib="gnome"/>
+  <widget class="GtkDialog" id="tabblo_export_dialog">
+    <property name="title" translatable="yes">Export</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox11">
+        <property name="visible">True</property>
+        <child>
+          <widget class="GtkHBox" id="hbox17">
+            <property name="visible">True</property>
+            <property name="border_width">6</property>
+            <child>
+              <widget class="GtkFrame" id="frame8">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">GTK_SHADOW_NONE</property>
+                <child>
+                  <widget class="GtkAlignment" id="alignment12">
+                    <property name="visible">True</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <widget class="GtkScrolledWindow" id="thumb_scrolled_window">
+                        <property name="width_request">180</property>
+                        <property name="height_request">180</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+                        <property name="shadow_type">GTK_SHADOW_IN</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="photo_frame">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Photos&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkVBox" id="vbox11">
+                <property name="visible">True</property>
+                <property name="spacing">6</property>
+                <child>
+                  <widget class="GtkFrame" id="frame9">
+                    <property name="visible">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">GTK_SHADOW_NONE</property>
+                    <child>
+                      <widget class="GtkLabel" id="label44">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Tabblo account&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </widget>
+                      <packing>
+                        <property name="type">label_item</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <widget class="GtkTable" id="table10">
+                        <property name="visible">True</property>
+                        <property name="border_width">12</property>
+                        <property name="n_rows">2</property>
+                        <property name="n_columns">2</property>
+                        <property name="column_spacing">7</property>
+                        <property name="row_spacing">6</property>
+                        <child>
+                          <widget class="GtkLabel" id="password_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">1</property>
+                            <property name="label" translatable="yes">_Password:</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">password_entry</property>
+                          </widget>
+                          <packing>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkEntry" id="password_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="visibility">False</property>
+                            <property name="invisible_char">*</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="bottom_attach">2</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkLabel" id="username_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">1</property>
+                            <property name="label" translatable="yes">_Username:</property>
+                            <property name="use_underline">True</property>
+                            <property name="mnemonic_widget">username_entry</property>
+                          </widget>
+                          <packing>
+                            <property name="x_options">GTK_FILL</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                        <child>
+                          <widget class="GtkEntry" id="username_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">*</property>
+                          </widget>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="right_attach">2</property>
+                            <property name="y_options"></property>
+                          </packing>
+                        </child>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area11">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="cancelButton">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">-6</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkButton" id="export_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="label">_Export</property>
+                <property name="use_underline">True</property>
+                <property name="response_id">-5</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Added: trunk/extensions/TabbloExport/TrustError.glade
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/TrustError.glade	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Sun Sep  7 21:22:13 2008 -->
+<glade-interface>
+  <widget class="GtkDialog" id="trust_error_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Trust Error</property>
+    <property name="resizable">False</property>
+    <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="deletable">False</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <widget class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <widget class="GtkLabel" id="label0">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="xpad">12</property>
+                    <property name="label" translatable="yes">A trust error occured while attempting to access</property>
+                    <property name="wrap">True</property>
+                  </widget>
+                  <packing>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="url_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="xpad">12</property>
+                    <property name="label" translatable="yes">&lt;b&gt;{0}&lt;/b&gt;.</property>
+                    <property name="use_markup">True</property>
+                    <property name="single_line_mode">True</property>
+                  </widget>
+                  <packing>
+                    <property name="fill">False</property>
+                    <property name="position">-1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="xpad">12</property>
+                    <property name="label" translatable="yes">Do you wish to:</property>
+                  </widget>
+                  <packing>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkVButtonBox" id="vbuttonbox1">
+                <property name="visible">True</property>
+                <child>
+                  <widget class="GtkRadioButton" id="abort_radiobutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Abort this session</property>
+                    <property name="response_id">0</property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkRadioButton" id="once_radiobutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Trust the site's certificate this once</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                    <property name="group">abort_radiobutton</property>
+                  </widget>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkRadioButton" id="always_radiobutton">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label" translatable="yes">Always trust this site's certificate</property>
+                    <property name="response_id">0</property>
+                    <property name="draw_indicator">True</property>
+                    <property name="group">once_radiobutton</property>
+                  </widget>
+                  <packing>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">GTK_BUTTONBOX_END</property>
+            <child>
+              <widget class="GtkButton" id="ok_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="label" translatable="yes">gtk-ok</property>
+                <property name="use_stock">True</property>
+                <property name="response_id">0</property>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">GTK_PACK_END</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>

Added: trunk/extensions/TabbloExport/UserDecisionCertificatePolicy.cs
==============================================================================
--- (empty file)
+++ trunk/extensions/TabbloExport/UserDecisionCertificatePolicy.cs	Tue Sep 16 09:18:33 2008
@@ -0,0 +1,112 @@
+//
+// FSpotTabbloExport.UserDecisionCertificatePolicy
+//
+// Authors:
+//	Wojciech Dzierzanowski (wojciech dzierzanowski gmail com)
+//
+// (C) Copyright 2008 Wojciech Dzierzanowski
+//
+
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+
+using FSpot.Utils;
+
+namespace FSpotTabbloExport {
+	
+	class UserDecisionCertificatePolicy
+			: ApplicationCentricCertificatePolicy {
+
+		private const string DialogName = "trust_error_dialog";
+		[Glade.Widget] Gtk.Dialog dialog;
+		[Glade.Widget] Gtk.Label url_label;
+		[Glade.Widget] Gtk.RadioButton abort_radiobutton;
+		[Glade.Widget] Gtk.RadioButton once_radiobutton;
+		[Glade.Widget] Gtk.RadioButton always_radiobutton;
+
+		private X509Certificate certificate;
+		private WebRequest request;
+		private Decision decision;
+
+		private Object decision_lock = new Object ();
+		private ManualResetEvent decision_event;
+		
+		
+		protected override Decision GetDecision (
+				X509Certificate certificate,
+				WebRequest request)
+		{
+			this.certificate = certificate;
+			this.request = request;
+			
+			lock (decision_lock) {
+				GLib.Idle.Add (this.DoGetDecision);
+				decision_event = new ManualResetEvent (false);
+				decision_event.WaitOne ();
+			}
+			
+			return decision;
+		}
+		
+		private bool DoGetDecision ()
+		{
+			Glade.XML glade_xml = new Glade.XML (
+					null, "TrustError.glade", DialogName,
+					"f-spot");
+			glade_xml.Autoconnect (this);
+
+			dialog = (Gtk.Dialog) glade_xml.GetWidget (DialogName);
+
+			url_label.Markup = String.Format (
+					url_label.Text, String.Format (
+							"<b>{0}</b>",
+							request.RequestUri));
+			
+			Gtk.ResponseType response =
+					(Gtk.ResponseType) dialog.Run ();
+			Log.DebugFormat ("Decision dialog response: " + response);
+			
+			dialog.Destroy ();
+			
+			decision = Decision.DontTrust;
+			if (0 == response) {
+				if (abort_radiobutton.Active) {
+					decision = Decision.DontTrust;
+				} else if (once_radiobutton.Active) {
+					decision = Decision.TrustOnce;
+				} else if (always_radiobutton.Active) {
+					decision = Decision.TrustAlways;
+				} else {
+					Debug.Assert (false,
+							"Unhandled decision");
+				}
+			}
+
+			decision_event.Set ();
+			return false;
+		}
+	}
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]