[libgdata] core: Add a _gdata_service_build_uri() function to escape URI components



commit 23925976366313b6475801a732b42d293ff4aa7d
Author: Philip Withnall <philip tecnocode co uk>
Date:   Mon Aug 23 09:55:46 2010 +0100

    core: Add a _gdata_service_build_uri() function to escape URI components
    
    This allows building URIs in a printf()-style manner, while automatically
    escaping the strings being substituted into the URI. This means that
    malformed input can't cause requests to fail or execute incorrectly by
    producing invalid or incorrect URIs.

 gdata/gdata-private.h                              |    2 +
 gdata/gdata-service.c                              |   81 ++++++++++++++++++++
 gdata/services/calendar/gdata-calendar-service.c   |    5 +-
 gdata/services/contacts/gdata-contacts-service.c   |    4 +-
 .../services/documents/gdata-documents-document.c  |   23 +-----
 gdata/services/documents/gdata-documents-service.c |   16 ++--
 .../documents/gdata-documents-spreadsheet.c        |   10 ++-
 gdata/services/picasaweb/gdata-picasaweb-service.c |    4 +-
 8 files changed, 103 insertions(+), 42 deletions(-)
---
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index 050948f..786a257 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -52,6 +52,8 @@ G_GNUC_INTERNAL guint _gdata_service_send_message (GDataService *self, SoupMessa
 G_GNUC_INTERNAL SoupMessage *_gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *query, GCancellable *cancellable,
                                                    GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 G_GNUC_INTERNAL const gchar *_gdata_service_get_scheme (void) G_GNUC_CONST;
+G_GNUC_INTERNAL gchar *_gdata_service_build_uri (gboolean force_http,
+                                                 const gchar *format, ...) G_GNUC_PRINTF (2, 3) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
 G_GNUC_INTERNAL GDataLogLevel _gdata_service_get_log_level (void) G_GNUC_CONST;
 
 #include "gdata-query.h"
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index 2d8743c..9c2d38d 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -33,6 +33,7 @@
 #include <glib/gi18n-lib.h>
 #include <libsoup/soup.h>
 #include <string.h>
+#include <stdarg.h>
 
 #ifdef HAVE_GNOME
 #include <libsoup/soup-gnome-features.h>
@@ -2108,6 +2109,86 @@ _gdata_service_get_scheme (void)
 }
 
 /*
+ * _gdata_service_build_uri:
+ * @force_http: %TRUE to force the outputted URI to use HTTP, %FALSE to use the scheme returned by _gdata_service_get_scheme()
+ * @format: a standard printf() format string
+ * @...: the arguments to insert in the output
+ *
+ * Builds a URI from the given @format string, replacing each <code class="literal">%%s</code> format placeholder with a URI-escaped version of the
+ * corresponding argument, and each <code class="literal">%%p</code> format placeholder with a non-escaped version of the corresponding argument. No
+ * other printf() format placeholders are supported at the moment except <code class="literal">%%d</code>, which prints a signed integer; and
+ * <code class="literal">%%</code>, which prints a literal percent symbol.
+ *
+ * The returned URI is guaranteed to use the scheme returned by _gdata_service_get_scheme(), unless the @force_http argument is %TRUE; in that case,
+ * the returned URI is guaranteed to use HTTP. The format string, once all the arguments have been inserted into it, must include a service, but it
+ * doesn't matter which one.
+ *
+ * Return value: a newly allocated URI string; free with g_free()
+ */
+gchar *
+_gdata_service_build_uri (gboolean force_http, const gchar *format, ...)
+{
+	const gchar *p, *scheme;
+	gchar *built_uri;
+	GString *uri;
+	va_list args;
+
+	g_return_val_if_fail (format != NULL, NULL);
+
+	/* Allocate a GString to build the URI in with at least as much space as the format string */
+	uri = g_string_sized_new (strlen (format));
+
+	/* Build the URI */
+	va_start (args, format);
+
+	for (p = format; *p != '\0'; p++) {
+		if (*p != '%') {
+			g_string_append_c (uri, *p);
+			continue;
+		}
+
+		switch(*++p) {
+			case 's':
+				g_string_append_uri_escaped (uri, va_arg (args, gchar*), NULL, TRUE);
+				break;
+			case 'p':
+				g_string_append (uri, va_arg (args, gchar*));
+				break;
+			case 'd':
+				g_string_append_printf (uri, "%d", va_arg (args, gint));
+				break;
+			case '%':
+				g_string_append_c (uri, '%');
+				break;
+			default:
+				g_error ("Unrecognized format placeholder '%%%c' in format '%s'. This is a programmer error.", *p, format);
+				break;
+		}
+	}
+
+	va_end (args);
+
+	built_uri = g_string_free (uri, FALSE);
+	scheme = (force_http == FALSE) ? _gdata_service_get_scheme () : "http";
+
+	/* Ensure we're using the correct scheme (HTTP or HTTPS) */
+	if (g_str_has_prefix (built_uri, scheme) == FALSE) {
+		gchar *fixed_uri, **pieces;
+
+		pieces = g_strsplit (built_uri, ":", 2);
+		g_assert (pieces[0] != NULL && pieces[1] != NULL && pieces[2] == NULL);
+
+		fixed_uri = g_strdup_printf ("%s:%s", scheme, pieces[1]);
+
+		g_strfreev (pieces);
+
+		return fixed_uri;
+	}
+
+	return built_uri;
+}
+
+/*
  * debug_handler:
  *
  * GLib debug message handler, which is passed all messages from g_debug() calls, and decides whether to print them.
diff --git a/gdata/services/calendar/gdata-calendar-service.c b/gdata/services/calendar/gdata-calendar-service.c
index d16b936..3c393f4 100644
--- a/gdata/services/calendar/gdata-calendar-service.c
+++ b/gdata/services/calendar/gdata-calendar-service.c
@@ -327,9 +327,8 @@ gdata_calendar_service_insert_event (GDataCalendarService *self, GDataCalendarEv
 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-	uri = g_strdup_printf ("%s://www.google.com/calendar/feeds/%s/private/full",
-	                       _gdata_service_get_scheme (), gdata_service_get_username (GDATA_SERVICE (self)));
-
+	uri = _gdata_service_build_uri (FALSE, "http://www.google.com/calendar/feeds/%s/private/full";,
+		                        gdata_service_get_username (GDATA_SERVICE (self)));
 	entry = gdata_service_insert_entry (GDATA_SERVICE (self), uri, GDATA_ENTRY (event), cancellable, error);
 	g_free (uri);
 
diff --git a/gdata/services/contacts/gdata-contacts-service.c b/gdata/services/contacts/gdata-contacts-service.c
index 0f0afb4..7d72a98 100644
--- a/gdata/services/contacts/gdata-contacts-service.c
+++ b/gdata/services/contacts/gdata-contacts-service.c
@@ -196,9 +196,7 @@ gdata_contacts_service_insert_contact (GDataContactsService *self, GDataContacts
 	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-	uri = g_strdup_printf ("%s://www.google.com/m8/feeds/contacts/%s/full",
-	                       _gdata_service_get_scheme (),
-	                       gdata_service_get_username (GDATA_SERVICE (self)));
+	uri = _gdata_service_build_uri (FALSE, "http://www.google.com/m8/feeds/contacts/%s/full";, gdata_service_get_username (GDATA_SERVICE (self)));
 	entry = gdata_service_insert_entry (GDATA_SERVICE (self), uri, GDATA_ENTRY (contact), cancellable, error);
 	g_free (uri);
 
diff --git a/gdata/services/documents/gdata-documents-document.c b/gdata/services/documents/gdata-documents-document.c
index d0681c8..a85f821 100644
--- a/gdata/services/documents/gdata-documents-document.c
+++ b/gdata/services/documents/gdata-documents-document.c
@@ -185,29 +185,8 @@ gdata_documents_document_download (GDataDocumentsDocument *self, GDataDocumentsS
 gchar *
 gdata_documents_document_get_download_uri (GDataDocumentsDocument *self, const gchar *export_format)
 {
-	const gchar *content_uri, *scheme;
-
 	g_return_val_if_fail (GDATA_IS_DOCUMENTS_DOCUMENT (self), NULL);
 	g_return_val_if_fail (export_format != NULL && *export_format != '\0', NULL);
 
-	content_uri = gdata_entry_get_content_uri (GDATA_ENTRY (self));
-	scheme = _gdata_service_get_scheme ();
-
-	g_assert (content_uri != NULL);
-
-	/* Ensure we're using the correct scheme (HTTP or HTTPS) */
-	if (g_str_has_prefix (content_uri, scheme) == FALSE) {
-		gchar *download_uri, **pieces;
-
-		pieces = g_strsplit (content_uri, ":", 2);
-		g_assert (pieces[0] != NULL && pieces[1] != NULL && pieces[2] == NULL);
-
-		download_uri = g_strdup_printf ("%s:%s&exportFormat=%s", scheme, pieces[1], export_format);
-
-		g_strfreev (pieces);
-
-		return download_uri;
-	}
-
-	return g_strdup_printf ("%s&exportFormat=%s", content_uri, export_format);
+	return _gdata_service_build_uri (FALSE, "%p&exportFormat=%s", gdata_entry_get_content_uri (GDATA_ENTRY (self)), export_format);
 }
diff --git a/gdata/services/documents/gdata-documents-service.c b/gdata/services/documents/gdata-documents-service.c
index 45e1f5d..70fa8f7 100644
--- a/gdata/services/documents/gdata-documents-service.c
+++ b/gdata/services/documents/gdata-documents-service.c
@@ -619,17 +619,17 @@ gdata_documents_service_remove_document_from_folder (GDataDocumentsService *self
 	g_assert (document_id != NULL);
 
 	if (GDATA_IS_DOCUMENTS_PRESENTATION (document)) {
-		uri = g_strdup_printf ("%s://docs.google.com/feeds/folders/private/full/folder%%3A%s/presentation%%3A%s",
-		                       _gdata_service_get_scheme (), folder_id, document_id);
+		uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/presentation%%3A%s";,
+		                                folder_id, document_id);
 	} else if (GDATA_IS_DOCUMENTS_SPREADSHEET (document)) {
-		uri = g_strdup_printf ("%s://docs.google.com/feeds/folders/private/full/folder%%3A%s/spreadsheet%%3A%s",
-		                       _gdata_service_get_scheme (), folder_id, document_id);
+		uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/spreadsheet%%3A%s";,
+		                                folder_id, document_id);
 	} else if (GDATA_IS_DOCUMENTS_TEXT (document)) {
-		uri = g_strdup_printf ("%s://docs.google.com/feeds/folders/private/full/folder%%3A%s/document%%3A%s",
-		                       _gdata_service_get_scheme (), folder_id, document_id);
+		uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/document%%3A%s";,
+		                                folder_id, document_id);
 	} else if (GDATA_IS_DOCUMENTS_FOLDER (document)) {
-		uri = g_strdup_printf ("%s://docs.google.com/feeds/folders/private/full/folder%%3A%s/folder%%3A%s",
-		                       _gdata_service_get_scheme (), folder_id, document_id);
+		uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/folder%%3A%s";,
+		                                folder_id, document_id);
 	} else {
 		g_assert_not_reached ();
 	}
diff --git a/gdata/services/documents/gdata-documents-spreadsheet.c b/gdata/services/documents/gdata-documents-spreadsheet.c
index 5f66b8e..6fa9155 100644
--- a/gdata/services/documents/gdata-documents-spreadsheet.c
+++ b/gdata/services/documents/gdata-documents-spreadsheet.c
@@ -122,10 +122,12 @@ gdata_documents_spreadsheet_get_download_uri (GDataDocumentsSpreadsheet *self, c
 	g_assert (document_id != NULL);
 
 	if (gid != -1) {
-		return g_strdup_printf ("%s://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=%s&exportFormat=%s&gid=%d",
-		                        _gdata_service_get_scheme (), document_id, export_format, gid);
+		return _gdata_service_build_uri (FALSE,
+		                                 "http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=%s&exportFormat=%s&gid=%d";,
+		                                 document_id, export_format, gid);
 	} else {
-		return g_strdup_printf ("%s://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=%s&exportFormat=%s",
-		                        _gdata_service_get_scheme (), document_id, export_format);
+		return _gdata_service_build_uri (FALSE,
+		                                 "http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=%s&exportFormat=%s";,
+		                                 document_id, export_format);
 	}
 }
diff --git a/gdata/services/picasaweb/gdata-picasaweb-service.c b/gdata/services/picasaweb/gdata-picasaweb-service.c
index d45359f..159a4da 100644
--- a/gdata/services/picasaweb/gdata-picasaweb-service.c
+++ b/gdata/services/picasaweb/gdata-picasaweb-service.c
@@ -153,7 +153,7 @@ create_uri (GDataPicasaWebService *self, const gchar *username, const gchar *typ
 		username = "default";
 	}
 
-	return g_strdup_printf ("http://picasaweb.google.com/data/%s/api/user/%s";, type, username);
+	return _gdata_service_build_uri (TRUE, "http://picasaweb.google.com/data/%s/api/user/%s";, type, username);
 }
 
 /**
@@ -381,7 +381,7 @@ get_file_output_stream (GDataPicasaWebService *self, GDataPicasaWebAlbum *album,
 	content_type = g_file_info_get_content_type (file_info);
 
 	/* Build the upload URI and upload stream */
-	upload_uri = g_strdup_printf ("http://picasaweb.google.com/data/feed/api/user/%s/albumid/%s";, user_id, album_id);
+	upload_uri = _gdata_service_build_uri (TRUE, "http://picasaweb.google.com/data/feed/api/user/%s/albumid/%s";, user_id, album_id);
 	output_stream = gdata_upload_stream_new (GDATA_SERVICE (self), SOUP_METHOD_POST, upload_uri, GDATA_ENTRY (file_entry), slug, content_type);
 	g_free (upload_uri);
 	g_object_unref (file_info);



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