[libgdata] Bug 582026 – Access Control List functionality
- From: Philip Withnall <pwithnall src gnome org>
- To: svn-commits-list gnome org
- Subject: [libgdata] Bug 582026 – Access Control List functionality
- Date: Sun, 17 May 2009 07:54:29 -0400 (EDT)
commit 5d853ec5706129ce8af1bc0a2baae3dfed52d8d0
Author: Philip Withnall <philip tecnocode co uk>
Date: Sun May 17 12:50:57 2009 +0100
Bug 582026 â?? Access Control List functionality
This adds a framework for controlling access control lists (ACLs). It
includes GDataAccessHandler, an interface to be implemented by any GDataEntry
which has an ACL. It also includes GDataAccessRule, a GDataEntry representing
a single access control rule, initially written by Thibault Saunier
<saunierthibault gmail com>.
Full documentation is included, and test cases will be added per-service.
---
docs/reference/gdata-docs.xml | 6 +
docs/reference/gdata-sections.txt | 41 ++++
gdata/Makefile.am | 6 +-
gdata/gdata-access-handler.c | 415 +++++++++++++++++++++++++++++++++++++
gdata/gdata-access-handler.h | 76 +++++++
gdata/gdata-access-rule.c | 397 +++++++++++++++++++++++++++++++++++
gdata/gdata-access-rule.h | 77 +++++++
gdata/gdata-service.h | 4 +-
gdata/gdata.h | 2 +
gdata/gdata.symbols | 12 +
po/POTFILES.in | 1 +
11 files changed, 1035 insertions(+), 2 deletions(-)
diff --git a/docs/reference/gdata-docs.xml b/docs/reference/gdata-docs.xml
index 4bb621e..f14206f 100644
--- a/docs/reference/gdata-docs.xml
+++ b/docs/reference/gdata-docs.xml
@@ -26,6 +26,12 @@
</chapter>
<chapter>
+ <title>Access Control List API</title>
+ <xi:include href="xml/gdata-access-handler.xml"/>
+ <xi:include href="xml/gdata-access-rule.xml"/>
+ </chapter>
+
+ <chapter>
<title>Namespace API</title>
<xi:include href="xml/gdata-atom.xml"/>
<xi:include href="xml/gdata-gdata.xml"/>
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index 79a1915..c49ecc4 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -628,3 +628,44 @@ gdata_youtube_uploader_get_type
<SUBSECTION Private>
GDataYouTubeQueryPrivate
</SECTION>
+
+<SECTION>
+<FILE>gdata-access-handler</FILE>
+<TITLE>GDataAccessHandler</TITLE>
+GDataAccessHandler
+GDataAccessHandlerIface
+gdata_access_handler_get_rules
+gdata_access_handler_insert_rule
+gdata_access_handler_update_rule
+gdata_access_handler_delete_rule
+<SUBSECTION Standard>
+gdata_access_handler_get_type
+GDATA_ACCESS_HANDLER
+GDATA_ACCESS_HANDLER_CLASS
+GDATA_ACCESS_HANDLER_GET_IFACE
+GDATA_IS_ACCESS_HANDLER
+GDATA_TYPE_ACCESS_HANDLER
+</SECTION>
+
+<SECTION>
+<FILE>gdata-access-rule</FILE>
+<TITLE>GDataAccessRule</TITLE>
+GDataAccessRule
+GDataAccessRuleClass
+gdata_access_rule_new
+gdata_access_rule_new_from_xml
+gdata_access_rule_get_role
+gdata_access_rule_set_role
+gdata_access_rule_get_scope
+gdata_access_rule_set_scope
+<SUBSECTION Standard>
+gdata_access_rule_get_type
+GDATA_ACCESS_RULE
+GDATA_ACCESS_RULE_CLASS
+GDATA_ACCESS_RULE_GET_CLASS
+GDATA_IS_ACCESS_RULE
+GDATA_IS_ACCESS_RULE_CLASS
+GDATA_TYPE_ACCESS_RULE
+<SUBSECTION Private>
+GDataAccessRulePrivate
+</SECTION>
diff --git a/gdata/Makefile.am b/gdata/Makefile.am
index 8551fdc..03f0660 100644
--- a/gdata/Makefile.am
+++ b/gdata/Makefile.am
@@ -50,7 +50,9 @@ gdata_headers = \
gdata-atom.h \
gdata-media-rss.h \
gdata-gdata.h \
- gdata-parser.h
+ gdata-parser.h \
+ gdata-access-handler.h \
+ gdata-access-rule.h
gdataincludedir = $(pkgincludedir)/gdata
gdatainclude_HEADERS = \
@@ -70,6 +72,8 @@ libgdata_la_SOURCES = \
gdata-media-rss.c \
gdata-gdata.c \
gdata-parser.c \
+ gdata-access-handler.c \
+ gdata-access-rule.c \
gdata-private.h
libgdata_la_CPPFLAGS = \
diff --git a/gdata/gdata-access-handler.c b/gdata/gdata-access-handler.c
new file mode 100644
index 0000000..6587220
--- /dev/null
+++ b/gdata/gdata-access-handler.c
@@ -0,0 +1,415 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2009 <philip tecnocode co uk>
+ *
+ * GData Client is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-access-handler
+ * @short_description: GData access handler interface
+ * @stability: Unstable
+ * @include: gdata/gdata-access-handler.h
+ *
+ * #GDataAccessHandler is an interface which can be implemented by #GDataEntry<!-- -->s which can have their permissions controlled by an
+ * access control list (ACL). It has a set of methods which allow the #GDataAccessRule<!-- -->s for the access handler/entry to be retrieved,
+ * added, modified and deleted, with immediate effect.
+ *
+ * When implementing the interface, classes must implement an <function>is_owner_rule</function> function.
+ **/
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+
+#include "gdata-access-handler.h"
+#include "gdata-private.h"
+#include "gdata-access-rule.h"
+
+GType
+gdata_access_handler_get_type (void)
+{
+ static GType access_handler_type = 0;
+
+ if (!access_handler_type) {
+ access_handler_type = g_type_register_static_simple (G_TYPE_INTERFACE, "GDataAccessHandler",
+ sizeof (GDataAccessHandlerIface),
+ NULL, 0, NULL, 0);
+ }
+
+ return access_handler_type;
+}
+
+/**
+ * gdata_access_handler_get_rules:
+ * @self: a #GDataAccessHandler
+ * @service: a #GDataService
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @progress_callback: a #GDataQueryProgressCallback to call when a rule is loaded, or %NULL
+ * @progress_user_data: data to pass to the @progress_callback function
+ * @error: a #GError, or %NULL
+ *
+ * Retrieves a #GDataFeed containing all the access rules which apply to the given #GDataAccessHandler. Only the owner of a #GDataAccessHandler may
+ * view its rule feed.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
+ * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * A %GDATA_SERVICE_ERROR_WITH_QUERY will be returned if the server indicates there is a problem with the query.
+ *
+ * For each rule in the response feed, @progress_callback will be called in the main thread. If there was an error parsing the XML response,
+ * a #GDataParserError will be returned.
+ *
+ * Return value: a #GDataFeed of access control rules, or %NULL; unref with g_object_unref()
+ *
+ * Since: 0.3.0
+ **/
+GDataFeed *
+gdata_access_handler_get_rules (GDataAccessHandler *self, GDataService *service, GCancellable *cancellable, GDataQueryProgressCallback progress_callback,
+ gpointer progress_user_data, GError **error)
+{
+ GDataServiceClass *klass;
+ GDataFeed *feed;
+ GDataLink *link;
+ SoupMessage *message;
+ guint status;
+
+ /* TODO: async version */
+ g_return_val_if_fail (GDATA_IS_ENTRY (self), NULL);
+ g_return_val_if_fail (GDATA_IS_SERVICE (service), NULL);
+
+ /* Get the ACL URI */
+ link = gdata_entry_look_up_link (GDATA_ENTRY (self), "http://schemas.google.com/acl/2007#accessControlList");
+ g_assert (link != NULL);
+ message = soup_message_new (SOUP_METHOD_GET, link->href);
+
+ /* Make sure subclasses set their headers */
+ klass = GDATA_SERVICE_GET_CLASS (service);
+ if (klass->append_query_headers != NULL)
+ klass->append_query_headers (service, message);
+
+ /* Send the message */
+ status = _gdata_service_send_message (service, message, error);
+ if (status == SOUP_STATUS_NONE) {
+ g_object_unref (message);
+ return NULL;
+ }
+
+ /* Check for cancellation */
+ if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
+ g_object_unref (message);
+ return NULL;
+ }
+
+ if (status != 200) {
+ /* Error */
+ g_assert (klass->parse_error_response != NULL);
+ klass->parse_error_response (service, GDATA_SERVICE_ERROR_WITH_QUERY, status, message->reason_phrase, message->response_body->data,
+ message->response_body->length, error);
+ g_object_unref (message);
+ return NULL;
+ }
+
+ g_assert (message->response_body->data != NULL);
+
+ feed = _gdata_feed_new_from_xml (message->response_body->data, message->response_body->length, GDATA_TYPE_ACCESS_RULE,
+ progress_callback, progress_user_data, error);
+ g_object_unref (message);
+
+ return feed;
+}
+
+/**
+ * gdata_access_handler_insert_rule:
+ * @self: a #GDataAccessHandler
+ * @service: a #GDataService
+ * @rule: the #GDataAccessRule to insert
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Inserts @rule in the access control list of the #GDataAccessHandler.
+ *
+ * The service will return an updated version of the rule, which is the return value of this function on success.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
+ * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * If the rule is marked as already having been inserted a %GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED error will be returned immediately
+ * (there will be no network requests).
+ *
+ * If there is an error inserting the rule, a %GDATA_SERVICE_ERROR_WITH_INSERTION error will be returned.
+ *
+ * Return value: an updated #GDataAccessRule, or %NULL
+ *
+ * Since: 0.3.0
+ **/
+GDataAccessRule *
+gdata_access_handler_insert_rule (GDataAccessHandler *self, GDataService *service, GDataAccessRule *rule, GCancellable *cancellable, GError **error)
+{
+ GDataServiceClass *klass;
+ GDataAccessRule *updated_rule;
+ GDataLink *link;
+ SoupMessage *message;
+ gchar *upload_data;
+ guint status;
+
+ g_return_val_if_fail (GDATA_IS_ENTRY (self), NULL);
+ g_return_val_if_fail (GDATA_IS_SERVICE (service), NULL);
+ g_return_val_if_fail (GDATA_IS_ACCESS_RULE (rule), NULL);
+
+ if (gdata_entry_is_inserted (GDATA_ENTRY (rule)) == TRUE) {
+ g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_ENTRY_ALREADY_INSERTED,
+ _("The rule has already been inserted."));
+ return NULL;
+ }
+
+ /* Get the ACL URI */
+ link = gdata_entry_look_up_link (GDATA_ENTRY (self), "http://schemas.google.com/acl/2007#accessControlList");
+ g_assert (link != NULL);
+ message = soup_message_new (SOUP_METHOD_POST, link->href);
+
+ /* Make sure subclasses set their headers */
+ klass = GDATA_SERVICE_GET_CLASS (service);
+ if (klass->append_query_headers != NULL)
+ klass->append_query_headers (service, message);
+
+ /* Append the data */
+ upload_data = gdata_entry_get_xml (GDATA_ENTRY (rule));
+ soup_message_set_request (message, "application/atom+xml", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
+
+ /* Send the message */
+ status = _gdata_service_send_message (service, message, error);
+ if (status == SOUP_STATUS_NONE) {
+ g_object_unref (message);
+ return NULL;
+ }
+
+ /* Check for cancellation */
+ if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
+ g_object_unref (message);
+ return NULL;
+ }
+
+ if (status != 201) {
+ /* Error */
+ g_assert (klass->parse_error_response != NULL);
+ klass->parse_error_response (service, GDATA_SERVICE_ERROR_WITH_INSERTION, status, message->reason_phrase, message->response_body->data,
+ message->response_body->length, error);
+ g_object_unref (message);
+ return NULL;
+ }
+
+ /* Build the updated entry */
+ g_assert (message->response_body->data != NULL);
+
+ /* Parse the XML; create and return a new GDataEntry of the same type as @entry */
+ updated_rule = GDATA_ACCESS_RULE (_gdata_entry_new_from_xml (G_OBJECT_TYPE (rule), message->response_body->data,
+ message->response_body->length, error));
+ g_object_unref (message);
+
+ return updated_rule;
+}
+
+static SoupMessage *
+get_soup_message (GDataAccessHandler *access_handler, GDataAccessRule *rule, const gchar *method)
+{
+ GDataLink *link;
+ SoupMessage *message;
+ GString *uri_string;
+ gchar *uri;
+ const gchar *scope_type, *scope_value;
+
+ /* Get the edit URI */
+ link = gdata_entry_look_up_link (GDATA_ENTRY (rule), "edit");
+ if (link != NULL)
+ return soup_message_new (method, link->href);
+
+ /* Try building the URI instead */
+ link = gdata_entry_look_up_link (GDATA_ENTRY (access_handler), "http://schemas.google.com/acl/2007#accessControlList");
+ g_assert (link != NULL);
+ gdata_access_rule_get_scope (rule, &scope_type, &scope_value);
+
+ uri_string = g_string_sized_new (strlen (link->href) + 30);
+ g_string_append_printf (uri_string, "%s/", link->href);
+ g_string_append_uri_escaped (uri_string, scope_type, NULL, TRUE);
+ if (scope_value != NULL) {
+ g_string_append (uri_string, "%3A");
+ g_string_append_uri_escaped (uri_string, scope_value, NULL, TRUE);
+ }
+
+ uri = g_string_free (uri_string, FALSE);
+ message = soup_message_new (method, uri);
+ g_free (uri);
+
+ return message;
+}
+
+/**
+ * gdata_access_handler_update_rule:
+ * @self: a #GDataAccessHandler
+ * @service: a #GDataService
+ * @rule: the #GDataAccessRule to update
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Updates @rule in the access control list of the #GDataAccessHandler.
+ *
+ * The service will return an updated version of the rule, which is the return value of this function on success.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
+ * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * If there is an error updating the rule, a %GDATA_SERVICE_ERROR_WITH_UPDATE error will be returned.
+ *
+ * Return value: an updated #GDataAccessRule, or %NULL
+ *
+ * Since: 0.3.0
+ **/
+GDataAccessRule *
+gdata_access_handler_update_rule (GDataAccessHandler *self, GDataService *service, GDataAccessRule *rule, GCancellable *cancellable, GError **error)
+{
+ GDataServiceClass *klass;
+ GDataAccessRule *updated_rule;
+ SoupMessage *message;
+ gchar *upload_data;
+ guint status;
+
+ g_return_val_if_fail (GDATA_IS_ENTRY (self), NULL);
+ g_return_val_if_fail (GDATA_IS_SERVICE (service), NULL);
+ g_return_val_if_fail (GDATA_IS_ACCESS_RULE (rule), NULL);
+
+ message = get_soup_message (self, rule, SOUP_METHOD_PUT);
+
+ /* Make sure subclasses set their headers */
+ klass = GDATA_SERVICE_GET_CLASS (service);
+ if (klass->append_query_headers != NULL)
+ klass->append_query_headers (service, message);
+
+ /* Looks like ACLs don't support ETags */
+
+ /* Append the data */
+ upload_data = gdata_entry_get_xml (GDATA_ENTRY (rule));
+ soup_message_set_request (message, "application/atom+xml", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
+
+ /* Send the message */
+ status = _gdata_service_send_message (service, message, error);
+ if (status == SOUP_STATUS_NONE) {
+ g_object_unref (message);
+ return NULL;
+ }
+
+ /* Check for cancellation */
+ if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
+ g_object_unref (message);
+ return NULL;
+ }
+
+ if (status != 200) {
+ /* Error */
+ g_assert (klass->parse_error_response != NULL);
+ klass->parse_error_response (service, GDATA_SERVICE_ERROR_WITH_UPDATE, status, message->reason_phrase, message->response_body->data,
+ message->response_body->length, error);
+ g_object_unref (message);
+ return NULL;
+ }
+
+ /* Build the updated entry */
+ g_assert (message->response_body->data != NULL);
+
+ /* Parse the XML; create and return a new GDataEntry of the same type as @entry */
+ updated_rule = GDATA_ACCESS_RULE (_gdata_entry_new_from_xml (G_OBJECT_TYPE (rule), message->response_body->data,
+ message->response_body->length, error));
+ g_object_unref (message);
+
+ return updated_rule;
+}
+
+/**
+ * gdata_access_handler_delete_rule:
+ * @self: a #GDataAccessHandler
+ * @service: a #GDataService
+ * @rule: the #GDataAccessRule to delete
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Deletes @rule from the access control list of the #GDataAccessHandler.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
+ * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * If there is an error deleting the rule, a %GDATA_SERVICE_ERROR_WITH_DELETION error will be returned, unless the @rule was the owner's
+ * rule; in which case, %GDATA_SERVICE_ERROR_FORBIDDEN will be returned without any network activity.
+ *
+ * Return value: %TRUE on success, %FALSE otherwise
+ *
+ * Since: 0.3.0
+ **/
+gboolean
+gdata_access_handler_delete_rule (GDataAccessHandler *self, GDataService *service, GDataAccessRule *rule, GCancellable *cancellable, GError **error)
+{
+ GDataServiceClass *klass;
+ GDataAccessHandlerIface *iface;
+ SoupMessage *message;
+ guint status;
+
+ g_return_val_if_fail (GDATA_IS_ENTRY (self), FALSE);
+ g_return_val_if_fail (GDATA_IS_SERVICE (service), FALSE);
+ g_return_val_if_fail (GDATA_IS_ACCESS_RULE (rule), FALSE);
+
+ /* The owner of the access handler can't be deleted */
+ iface = GDATA_ACCESS_HANDLER_GET_IFACE (self);
+ g_assert (iface->is_owner_rule != NULL);
+ if (iface->is_owner_rule (rule) == TRUE) {
+ g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_FORBIDDEN,
+ _("The owner's rule may not be deleted."));
+ return FALSE;
+ }
+
+ message = get_soup_message (self, rule, SOUP_METHOD_DELETE);
+
+ /* Make sure subclasses set their headers */
+ klass = GDATA_SERVICE_GET_CLASS (service);
+ if (klass->append_query_headers != NULL)
+ klass->append_query_headers (service, message);
+
+ /* Looks like ACLs don't support ETags */
+
+ /* Send the message */
+ status = _gdata_service_send_message (service, message, error);
+ if (status == SOUP_STATUS_NONE) {
+ g_object_unref (message);
+ return FALSE;
+ }
+
+ /* Check for cancellation */
+ if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
+ g_object_unref (message);
+ return FALSE;
+ }
+
+ if (status != 200) {
+ /* Error */
+ g_assert (klass->parse_error_response != NULL);
+ klass->parse_error_response (service, GDATA_SERVICE_ERROR_WITH_DELETION, status, message->reason_phrase, message->response_body->data,
+ message->response_body->length, error);
+ g_object_unref (message);
+ return FALSE;
+ }
+
+ g_object_unref (message);
+
+ return TRUE;
+}
diff --git a/gdata/gdata-access-handler.h b/gdata/gdata-access-handler.h
new file mode 100644
index 0000000..ceb8790
--- /dev/null
+++ b/gdata/gdata-access-handler.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Philip Withnall 2009 <philip tecnocode co uk>
+ *
+ * GData Client is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_ACCESS_HANDLER_H
+#define GDATA_ACCESS_HANDLER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-feed.h>
+#include <gdata/gdata-service.h>
+#include <gdata/gdata-access-rule.h>
+
+G_BEGIN_DECLS
+
+#define GDATA_TYPE_ACCESS_HANDLER (gdata_access_handler_get_type ())
+#define GDATA_ACCESS_HANDLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_ACCESS_HANDLER, GDataAccessHandler))
+#define GDATA_ACCESS_HANDLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_ACCESS_HANDLER, GDataAccessHandlerIface))
+#define GDATA_IS_ACCESS_HANDLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_ACCESS_HANDLER))
+#define GDATA_ACCESS_HANDLER_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDATA_TYPE_ACCESS_HANDLER, GDataAccessHandlerIface))
+
+/**
+ * GDataAccessHandler:
+ *
+ * All the fields in the #GDataAccessHandler structure are private and should never be accessed directly.
+ *
+ * Since: 0.3.0
+ **/
+typedef struct _GDataAccessHandler GDataAccessHandler; /* dummy typedef */
+
+/**
+ * GDataAccessHandlerIface:
+ * @parent: the parent type
+ * @is_owner_rule: a function to return whether the given #GDataAccessRule has the role of an owner (of a #GDataAccessHandler).
+ *
+ * The class structure for the #GDataAccessHandler interface.
+ *
+ * Since: 0.3.0
+ **/
+typedef struct {
+ GTypeInterface parent;
+
+ gboolean (*is_owner_rule) (GDataAccessRule *rule);
+} GDataAccessHandlerIface;
+
+GType gdata_access_handler_get_type (void) G_GNUC_CONST;
+
+GDataFeed *gdata_access_handler_get_rules (GDataAccessHandler *self, GDataService *service, GCancellable *cancellable,
+ GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
+ GError **error) G_GNUC_WARN_UNUSED_RESULT;
+GDataAccessRule *gdata_access_handler_insert_rule (GDataAccessHandler *self, GDataService *service, GDataAccessRule *rule,
+ GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+GDataAccessRule *gdata_access_handler_update_rule (GDataAccessHandler *self, GDataService *service, GDataAccessRule *rule,
+ GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+gboolean gdata_access_handler_delete_rule (GDataAccessHandler *self, GDataService *service, GDataAccessRule *rule,
+ GCancellable *cancellable, GError **error);
+
+G_END_DECLS
+
+#endif /* !GDATA_ACCESS_HANDLER_H */
diff --git a/gdata/gdata-access-rule.c b/gdata/gdata-access-rule.c
new file mode 100644
index 0000000..cd2a50f
--- /dev/null
+++ b/gdata/gdata-access-rule.c
@@ -0,0 +1,397 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Thibault Saunier 2009 <saunierthibault gmail com>
+ * Copyright (C) Philip Withnall 2009 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:gdata-access-rule
+ * @short_description: GData access rule object
+ * @stability: Unstable
+ * @include: gdata/gdata-access-rule.h
+ *
+ * #GDataAccessRule is a subclass of #GDataEntry to represent a generic access rule from an access control list (ACL).
+ * It is returned by the ACL methods implemented in the #GDataAccessHandler interface.
+ **/
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <libxml/parser.h>
+#include <string.h>
+
+#include "gdata-access-rule.h"
+#include "gdata-gdata.h"
+#include "gdata-parser.h"
+#include "gdata-types.h"
+#include "gdata-private.h"
+
+static void gdata_access_rule_finalize (GObject *object);
+static void get_namespaces (GDataEntry *entry, GHashTable *namespaces);
+static void get_xml (GDataEntry *entry, GString *xml_string);
+static void gdata_access_rule_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static void gdata_access_rule_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static gboolean parse_xml (GDataEntry *entry, xmlDoc *doc, xmlNode *node, GError **error);
+
+struct _GDataAccessRulePrivate {
+ gchar *role;
+ gchar *scope_type;
+ gchar *scope_value;
+};
+
+enum {
+ PROP_ROLE = 1,
+ PROP_SCOPE_TYPE,
+ PROP_SCOPE_VALUE,
+};
+
+G_DEFINE_TYPE (GDataAccessRule, gdata_access_rule, GDATA_TYPE_ENTRY)
+#define GDATA_ACCESS_RULE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDATA_TYPE_ACCESS_RULE, GDataAccessRulePrivate))
+
+static void
+gdata_access_rule_class_init (GDataAccessRuleClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GDataEntryClass *entry_class = GDATA_ENTRY_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (GDataAccessRulePrivate));
+
+ gobject_class->finalize = gdata_access_rule_finalize;
+ gobject_class->set_property = gdata_access_rule_set_property;
+ gobject_class->get_property = gdata_access_rule_get_property;
+
+ entry_class->get_xml = get_xml;
+ entry_class->parse_xml = parse_xml;
+ entry_class->get_namespaces = get_namespaces;
+
+ /**
+ * GDataAccessRule:role:
+ *
+ * The role of the person concerned by this ACL.
+ *
+ * Since: 0.3.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_ROLE,
+ g_param_spec_string ("role",
+ "Role", "The role of the person concerned by this ACL.",
+ NULL,
+ G_PARAM_READWRITE ));
+
+ /**
+ * GDataAccessRule:scope-type:
+ *
+ * Specifies to whom this access rule applies.
+ *
+ * Since: 0.3.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_SCOPE_TYPE,
+ g_param_spec_string ("scope-type",
+ "Scope type", "Specifies to whom this access rule applies.",
+ NULL,
+ G_PARAM_READWRITE ));
+
+ /**
+ * GDataAccessRule:scope-value:
+ *
+ * A value representing the user who is represented by the access rule, such as an
+ * e-mail address for users, or a domain name for domains.
+ *
+ * Since: 0.3.0
+ **/
+ g_object_class_install_property (gobject_class, PROP_SCOPE_VALUE,
+ g_param_spec_string ("scope-value",
+ "Scope value", "The scope value for this access rule.",
+ NULL,
+ G_PARAM_READWRITE ));
+}
+
+/**
+ * gdata_access_rule_new:
+ * @id: the access rule's ID, or %NULL
+ *
+ * Creates a new #GDataAccessRule with the given ID and default properties.
+ *
+ * Return value: a new #GDataAccessRule; unref with g_object_unref()
+ *
+ * Since: 0.3.0
+ **/
+GDataAccessRule *
+gdata_access_rule_new (const gchar *id)
+{
+ return g_object_new (GDATA_TYPE_ACCESS_RULE, "id", id, NULL);
+}
+
+/**
+ * gdata_access_rule_new_from_xml:
+ * @xml: an XML string
+ * @length: the length in characters of @xml, or %-1
+ * @error: a #GError, or %NULL
+ *
+ * Creates a new #GDataAccessRule from an XML string. If @length is %-1, the length of
+ * the string will be calculated.
+ *
+ * Errors from #GDataParserError can be returned if problems are found in the XML.
+ *
+ * Return value: a new #GDataAccessRule, or %NULL; unref with g_object_unref()
+ *
+ * Since: 0.3.0
+ **/
+GDataAccessRule *
+gdata_access_rule_new_from_xml (const gchar *xml, gint length, GError **error)
+{
+ return GDATA_ACCESS_RULE (_gdata_entry_new_from_xml (GDATA_TYPE_ACCESS_RULE, xml, length, error));
+}
+
+static void
+gdata_access_rule_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+ GDataAccessRule *self = GDATA_ACCESS_RULE (object);
+
+ switch (property_id) {
+ case PROP_ROLE:
+ gdata_access_rule_set_role (self, g_value_get_string (value));
+ break;
+ case PROP_SCOPE_TYPE:
+ g_free (self->priv->scope_type);
+ self->priv->scope_type = g_value_dup_string (value);
+ g_object_notify (object, "scope-type");
+ break;
+ case PROP_SCOPE_VALUE:
+ g_free (self->priv->scope_value);
+ self->priv->scope_value = g_value_dup_string (value);
+ g_object_notify (object, "scope-value");
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_access_rule_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+ GDataAccessRulePrivate *priv = GDATA_ACCESS_RULE_GET_PRIVATE (object);
+
+ switch (property_id) {
+ case PROP_ROLE:
+ g_value_set_string (value, priv->role);
+ break;
+ case PROP_SCOPE_TYPE:
+ g_value_set_string (value, priv->scope_type);
+ break;
+ case PROP_SCOPE_VALUE:
+ g_value_set_string (value, priv->scope_value);
+ break;
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gdata_access_rule_init (GDataAccessRule *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_ACCESS_RULE, GDataAccessRulePrivate);
+}
+
+static void
+gdata_access_rule_finalize (GObject *object)
+{
+ GDataAccessRulePrivate *priv = GDATA_ACCESS_RULE_GET_PRIVATE (object);
+
+ g_free (priv->role);
+ g_free (priv->scope_type);
+ g_free (priv->scope_value);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (gdata_access_rule_parent_class)->finalize (object);
+}
+
+static gboolean
+parse_xml (GDataEntry *entry, xmlDoc *doc, xmlNode *node, GError **error)
+{
+ GDataAccessRule *self = GDATA_ACCESS_RULE (entry);
+
+ g_return_val_if_fail (GDATA_IS_ACCESS_RULE (self), FALSE);
+ g_return_val_if_fail (doc != NULL, FALSE);
+ g_return_val_if_fail (node != NULL, FALSE);
+
+ if (xmlStrcmp (node->name, (xmlChar*) "role") == 0) {
+ /* gAcl:role */
+ xmlChar *role = xmlGetProp (node, (xmlChar*) "value");
+ if (role == NULL)
+ return gdata_parser_error_required_property_missing ("gAcl:role", "value", error);
+ gdata_access_rule_set_role (self, (gchar*) role);
+ xmlFree (role);
+ } else if (xmlStrcmp (node->name, (xmlChar*) "scope") == 0) {
+ /* gAcl:scope */
+ xmlChar *scope_type, *scope_value;
+
+ scope_type = xmlGetProp (node, (xmlChar*) "type");
+ if (scope_type == NULL)
+ return gdata_parser_error_required_property_missing ("gAcl:scope", "type", error);
+
+ scope_value = xmlGetProp (node, (xmlChar*) "value");
+ gdata_access_rule_set_scope (self, (gchar*) scope_type, (gchar*) scope_value);
+ xmlFree (scope_type);
+ xmlFree (scope_value);
+ } else if (GDATA_ENTRY_CLASS (gdata_access_rule_parent_class)->parse_xml (entry, doc, node, error) == FALSE) {
+ /* Error! */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+get_xml (GDataEntry *entry, GString *xml_string)
+{
+ GDataCategory *category;
+ GDataAccessRulePrivate *priv = GDATA_ACCESS_RULE (entry)->priv;
+
+ /* Ensure we have the correct category/kind */
+ category = gdata_category_new ("http://schemas.google.com/acl/2007#accessRule", "http://schemas.google.com/g/2005#kind", NULL);
+ gdata_entry_add_category (entry, category);
+
+ /* So it's valid Atom, set the title if one doesn't already exist */
+ if (gdata_entry_get_title (entry) == NULL)
+ gdata_entry_set_title (entry, priv->role);
+
+ /* Chain up to the parent class */
+ GDATA_ENTRY_CLASS (gdata_access_rule_parent_class)->get_xml (entry, xml_string);
+
+ if (priv->role != NULL)
+ /* gAcl:role */
+ g_string_append_printf (xml_string, "<gAcl:role value='%s'/>", priv->role);
+
+ if (priv->scope_value != NULL){
+ /* gAcl:scope */
+ if (priv->scope_type != NULL)
+ g_string_append_printf (xml_string, "<gAcl:scope type='%s' value='%s'/>", priv->scope_type, priv->scope_value);
+ else
+ g_string_append_printf (xml_string, "<gAcl:scope value='%s'/>", priv->scope_value);
+ }
+}
+
+static void
+get_namespaces (GDataEntry *entry, GHashTable *namespaces)
+{
+ /* Chain up to the parent class */
+ GDATA_ENTRY_CLASS (gdata_access_rule_parent_class)->get_namespaces (entry, namespaces);
+
+ g_hash_table_insert (namespaces, (gchar*) "gAcl", (gchar*) "http://schemas.google.com/acl/2007");
+}
+
+/**
+ * gdata_access_rule_set_role:
+ * @self: a #GDataAccessRule
+ * @role: a new role, or %NULL
+ *
+ * Sets the #GDataAccessRule:role property to @role.
+ *
+ * Set @role to %NULL to unset the property in the access rule.
+ *
+ * Since: 0.3.0
+ **/
+void
+gdata_access_rule_set_role (GDataAccessRule *self, const gchar *role)
+{
+ g_return_if_fail (GDATA_IS_ACCESS_RULE (self));
+ g_free (self->priv->role);
+ self->priv->role = g_strdup (role);
+ g_object_notify (G_OBJECT (self), "role");
+}
+
+/**
+ * gdata_access_rule_get_role:
+ * @self: a #GDataAccessRule
+ *
+ * Gets the #GDataAccessRule:role property.
+ *
+ * Return value: the access rule's role, or %NULL
+ *
+ * Since: 0.3.0
+ **/
+const gchar *
+gdata_access_rule_get_role (GDataAccessRule *self)
+{
+ g_return_val_if_fail (GDATA_IS_ACCESS_RULE (self), NULL);
+ return self->priv->role;
+}
+
+
+/**
+ * gdata_access_rule_set_scope:
+ * @self: a #GDataAccessRule
+ * @type: a new scope type
+ * @value: a new scope value, or %NULL
+ *
+ * Sets the #GDataAccessRule:scope-type property to @type and the #GDataAccessRule:scope-value property to @value.
+ *
+ * Set @scope_value to %NULL to unset the #GDataAccessRule:scope-value property in the access rule. @type cannot
+ * be %NULL. @scope_value must be %NULL if @type is <literal>default</literal>, and non-%NULL otherwise.
+ *
+ * See the <ulink type="http" url="http://code.google.com/apis/calendar/docs/2.0/reference.html#gacl_reference">online documentation</ulink>
+ * for more information.
+ *
+ * Since: 0.3.0
+ **/
+void
+gdata_access_rule_set_scope (GDataAccessRule *self, const gchar *type, const gchar *value)
+{
+ g_return_if_fail (GDATA_IS_ACCESS_RULE (self));
+ g_return_if_fail (type != NULL);
+
+ /* Validate stuff first */
+ if (strcmp (type, "default") == 0)
+ g_return_if_fail (value == NULL);
+ else
+ g_return_if_fail (value != NULL);
+
+ g_free (self->priv->scope_type);
+ self->priv->scope_type = g_strdup (type);
+
+ g_free (self->priv->scope_value);
+ self->priv->scope_value = g_strdup (value);
+
+ g_object_freeze_notify (G_OBJECT (self));
+ g_object_notify (G_OBJECT (self), "scope-type");
+ g_object_notify (G_OBJECT (self), "scope-value");
+ g_object_thaw_notify (G_OBJECT (self));
+}
+
+/**
+ * gdata_access_rule_get_scope:
+ * @self: a #GDataAccessRule
+ * @type: return location for the scope type, or %NULL
+ * @value: return location for the scope value, or %NULL
+ *
+ * Gets the #GDataAccessRule:scope-type and #GDataAccessRule:scope-value properties.
+ *
+ * Since: 0.3.0
+ **/
+void
+gdata_access_rule_get_scope (GDataAccessRule *self, const gchar **type, const gchar **value)
+{
+ g_return_if_fail (GDATA_IS_ACCESS_RULE (self));
+ if (type != NULL)
+ *type = self->priv->scope_type;
+ if (value != NULL)
+ *value = self->priv->scope_value;
+}
diff --git a/gdata/gdata-access-rule.h b/gdata/gdata-access-rule.h
new file mode 100644
index 0000000..c11d240
--- /dev/null
+++ b/gdata/gdata-access-rule.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * GData Client
+ * Copyright (C) Thibault Saunier 2009 <saunierthibault gmail com>
+ * Copyright (C) Philip Withnall 2009 <philip tecnocode co uk>
+ *
+ * GData Client is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GData Client is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_ACCESS_RULE_H
+#define GDATA_ACCESS_RULE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gdata/gdata-entry.h>
+#include <gdata/gdata-gdata.h>
+
+G_BEGIN_DECLS
+
+#define GDATA_TYPE_ACCESS_RULE (gdata_access_rule_get_type ())
+#define GDATA_ACCESS_RULE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_ACCESS_RULE, GDataAccessRule))
+#define GDATA_ACCESS_RULE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_ACCESS_RULE, GDataAccessRuleClass))
+#define GDATA_IS_ACCESS_RULE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_ACCESS_RULE))
+#define GDATA_IS_ACCESS_RULE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_ACCESS_RULE))
+#define GDATA_ACCESS_RULE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_ACCESS_RULE, GDataAccessRuleClass))
+
+typedef struct _GDataAccessRulePrivate GDataAccessRulePrivate;
+
+/**
+ * GDataAccessRule:
+ *
+ * All the fields in the #GDataAccessRule structure are private and should never be accessed directly.
+ *
+ * Since: 0.3.0
+ **/
+typedef struct {
+ GDataEntry parent;
+ GDataAccessRulePrivate *priv;
+} GDataAccessRule;
+
+/**
+ * GDataAccessRuleClass:
+ *
+ * All the fields in the #GDataAccessRuleClass structure are private and should never be accessed directly.
+ *
+ * Since: 0.3.0
+ **/
+typedef struct {
+ /*< private >*/
+ GDataEntryClass parent;
+} GDataAccessRuleClass;
+
+GType gdata_access_rule_get_type (void) G_GNUC_CONST;
+
+GDataAccessRule *gdata_access_rule_new (const gchar *id) G_GNUC_WARN_UNUSED_RESULT;
+GDataAccessRule *gdata_access_rule_new_from_xml (const gchar *xml, gint length, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+
+const gchar *gdata_access_rule_get_role (GDataAccessRule *self);
+void gdata_access_rule_set_role (GDataAccessRule *self, const gchar *role);
+void gdata_access_rule_get_scope (GDataAccessRule *self, const gchar **type, const gchar **value);
+void gdata_access_rule_set_scope (GDataAccessRule *self, const gchar *type, const gchar *value);
+
+G_END_DECLS
+
+#endif /* !GDATA_ACCESS_RULE_H */
diff --git a/gdata/gdata-service.h b/gdata/gdata-service.h
index 81c51cb..7f75df5 100644
--- a/gdata/gdata-service.h
+++ b/gdata/gdata-service.h
@@ -42,6 +42,7 @@ G_BEGIN_DECLS
* @GDATA_SERVICE_ERROR_NOT_FOUND: A requested resource (feed or entry) was not found on the server
* @GDATA_SERVICE_ERROR_CONFLICT: There was a conflict when updating an entry on the server; the server-side copy was modified inbetween downloading
* and uploading the modified entry
+ * @GDATA_SERVICE_ERROR_FORBIDDEN: Generic error for a forbidden action (not due to having insufficient permissions)
*
* Error codes for #GDataService operations.
**/
@@ -55,7 +56,8 @@ typedef enum {
GDATA_SERVICE_ERROR_WITH_UPDATE,
GDATA_SERVICE_ERROR_WITH_DELETION,
GDATA_SERVICE_ERROR_NOT_FOUND,
- GDATA_SERVICE_ERROR_CONFLICT
+ GDATA_SERVICE_ERROR_CONFLICT,
+ GDATA_SERVICE_ERROR_FORBIDDEN
} GDataServiceError;
/**
diff --git a/gdata/gdata.h b/gdata/gdata.h
index 20fde3c..b51d3c5 100644
--- a/gdata/gdata.h
+++ b/gdata/gdata.h
@@ -28,6 +28,8 @@
#include <gdata/gdata-parser.h>
#include <gdata/gdata-query.h>
#include <gdata/gdata-enums.h>
+#include <gdata/gdata-access-handler.h>
+#include <gdata/gdata-access-rule.h>
/* Namespaces */
#include <gdata/gdata-atom.h>
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index d5353f4..30ccb08 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -335,3 +335,15 @@ gdata_contacts_contact_remove_group
gdata_contacts_contact_is_group_deleted
gdata_contacts_contact_get_groups
gdata_contacts_contact_is_deleted
+gdata_access_handler_get_type
+gdata_access_handler_get_rules
+gdata_access_handler_insert_rule
+gdata_access_handler_update_rule
+gdata_access_handler_delete_rule
+gdata_access_rule_get_type
+gdata_access_rule_new
+gdata_access_rule_new_from_xml
+gdata_access_rule_get_role
+gdata_access_rule_set_role
+gdata_access_rule_get_scope
+gdata_access_rule_set_scope
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 1f25452..180770d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,6 +1,7 @@
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
[encoding: UTF-8]
+gdata/gdata-access-handler.c
gdata/gdata-entry.c
gdata/gdata-feed.c
gdata/gdata-parser.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]