summaryrefslogtreecommitdiff
path: root/gdata
diff options
context:
space:
mode:
authorPhilip Withnall <philip@tecnocode.co.uk>2010-08-23 09:55:46 +0100
committerPhilip Withnall <philip@tecnocode.co.uk>2010-08-23 22:08:04 +0100
commit23925976366313b6475801a732b42d293ff4aa7d (patch)
tree992275bac4505d3b2b74009bfcf7eae0a62099f1 /gdata
parent33181607696118df9715f18e633a942dc10f470b (diff)
downloadlibgdata-23925976366313b6475801a732b42d293ff4aa7d.tar.gz
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.
Diffstat (limited to 'gdata')
-rw-r--r--gdata/gdata-private.h2
-rw-r--r--gdata/gdata-service.c81
-rw-r--r--gdata/services/calendar/gdata-calendar-service.c5
-rw-r--r--gdata/services/contacts/gdata-contacts-service.c4
-rw-r--r--gdata/services/documents/gdata-documents-document.c23
-rw-r--r--gdata/services/documents/gdata-documents-service.c16
-rw-r--r--gdata/services/documents/gdata-documents-spreadsheet.c10
-rw-r--r--gdata/services/picasaweb/gdata-picasaweb-service.c4
8 files changed, 103 insertions, 42 deletions
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index 050948ff..786a257e 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 2d8743cb..9c2d38d8 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 d16b9360..3c393f42 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 0f0afb49..7d72a989 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 d0681c8a..a85f8212 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 45e1f5d6..70fa8f75 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 5f66b8e9..6fa9155f 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 d45359fc..159a4da8 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);