1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * GData Client
4 * Copyright (C) Philip Withnall 2009, 2014, 2015 <philip@tecnocode.co.uk>
5 *
6 * GData Client is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * GData Client is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with GData Client. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /**
21 * SECTION:gdata-calendar-service
22 * @short_description: GData Calendar service object
23 * @stability: Stable
24 * @include: gdata/services/calendar/gdata-calendar-service.h
25 *
26 * #GDataCalendarService is a subclass of #GDataService for communicating with the GData API of Google Calendar. It supports querying
27 * for, inserting, editing and deleting events from calendars, as well as operations on the calendars themselves.
28 *
29 * For more details of Google Calendar's GData API, see the
30 * <ulink type="http" url="https://developers.google.com/google-apps/calendar/v3/reference/">
31 * online documentation</ulink>.
32 *
33 * Each calendar accessible through the service has an access control list (ACL) which defines the level of access to the calendar to each user, and
34 * which users the calendar is shared with. For more information about ACLs for calendars, see the
35 * <ulink type="http" url="https://developers.google.com/google-apps/calendar/v3/reference/acl">online documentation on
36 * sharing calendars</ulink>.
37 *
38 * <example>
39 * <title>Retrieving the Access Control List for a Calendar</title>
40 * <programlisting>
41 * GDataCalendarService *service;
42 * GDataCalendarCalendar *calendar;
43 * GDataFeed *acl_feed;
44 * GDataAccessRule *rule, *new_rule;
45 * GDataLink *acl_link;
46 * GList *i;
47 * GError *error = NULL;
48 *
49 * /<!-- -->* Create a service and retrieve a calendar to work on *<!-- -->/
50 * service = create_calendar_service ();
51 * calendar = get_calendar (service);
52 *
53 * /<!-- -->* Query the service for the ACL for the given calendar *<!-- -->/
54 * acl_feed = gdata_access_handler_get_rules (GDATA_ACCESS_HANDLER (calendar), GDATA_SERVICE (service), NULL, NULL, NULL, &error);
55 *
56 * if (error != NULL) {
57 * g_error ("Error getting ACL feed for calendar: %s", error->message);
58 * g_error_free (error);
59 * g_object_unref (calendar);
60 * g_object_unref (service);
61 * return;
62 * }
63 *
64 * /<!-- -->* Iterate through the ACL *<!-- -->/
65 * for (i = gdata_feed_get_entries (acl_feed); i != NULL; i = i->next) {
66 * const gchar *scope_value;
67 *
68 * rule = GDATA_ACCESS_RULE (i->data);
69 *
70 * /<!-- -->* Do something with the access rule here. As an example, we update the rule applying to test@gmail.com and delete all
71 * * the other rules. We then insert another rule for example@gmail.com below. *<!-- -->/
72 * gdata_access_rule_get_scope (rule, NULL, &scope_value);
73 * if (scope_value != NULL && strcmp (scope_value, "test@gmail.com") == 0) {
74 * GDataAccessRule *updated_rule;
75 *
76 * /<!-- -->* Update the rule to make test@gmail.com an editor (full read/write access to the calendar, but they can't change
77 * * the ACL). *<!-- -->/
78 * gdata_access_rule_set_role (rule, GDATA_CALENDAR_ACCESS_ROLE_EDITOR);
79 * updated_rule = GDATA_ACCESS_RULE (gdata_service_update_entry (GDATA_SERVICE (service), GDATA_ENTRY (rule), NULL, &error));
80 *
81 * if (error != NULL) {
82 * g_error ("Error updating access rule for %s: %s", scope_value, error->message);
83 * g_error_free (error);
84 * g_object_unref (acl_feed);
85 * g_object_unref (calendar);
86 * g_object_unref (service);
87 * return;
88 * }
89 *
90 * g_object_unref (updated_rule);
91 * } else {
92 * /<!-- -->* Delete any rule which doesn't apply to test@gmail.com *<!-- -->/
93 * gdata_service_delete_entry (GDATA_SERVICE (service), GDATA_ENTRY (rule), NULL, &error);
94 *
95 * if (error != NULL) {
96 * g_error ("Error deleting access rule for %s: %s", scope_value, error->message);
97 * g_error_free (error);
98 * g_object_unref (acl_feed);
99 * g_object_unref (calendar);
100 * g_object_unref (service);
101 * return;
102 * }
103 * }
104 * }
105 *
106 * g_object_unref (acl_feed);
107 *
108 * /<!-- -->* Create and insert a new access rule for example@gmail.com which allows them to view free/busy information for events in the
109 * * calendar, but doesn't allow them to view the full event details. *<!-- -->/
110 * rule = gdata_access_rule_new (NULL);
111 * gdata_access_rule_set_role (rule, GDATA_CALENDAR_ACCESS_ROLE_FREE_BUSY);
112 * gdata_access_rule_set_scope (rule, GDATA_ACCESS_SCOPE_USER, "example@gmail.com");
113 *
114 * acl_link = gdata_entry_look_up_link (GDATA_ENTRY (calendar), GDATA_LINK_ACCESS_CONTROL_LIST);
115 * new_rule = GDATA_ACCESS_RULE (gdata_service_insert_entry (GDATA_SERVICE (service), gdata_link_get_uri (acl_link), GDATA_ENTRY (rule),
116 * NULL, &error));
117 *
118 * g_object_unref (rule);
119 * g_object_unref (calendar);
120 * g_object_unref (service);
121 *
122 * if (error != NULL) {
123 * g_error ("Error inserting access rule: %s", error->message);
124 * g_error_free (error);
125 * return;
126 * }
127 *
128 * g_object_unref (acl_link);
129 * </programlisting>
130 * </example>
131 *
132 * Before version 0.17.2, the Calendar service could be manipulated using
133 * batch operations. That is no longer supported, and any batch operations
134 * created on the calendar will fail.
135 */
136
137 #include <config.h>
138 #include <glib.h>
139 #include <glib/gi18n-lib.h>
140 #include <libsoup/soup.h>
141 #include <string.h>
142
143 #include "gdata-calendar-service.h"
144 #include "gdata-batchable.h"
145 #include "gdata-service.h"
146 #include "gdata-private.h"
147 #include "gdata-query.h"
148 #include "gdata-calendar-feed.h"
149
150 /* Standards reference here:
151 * https://developers.google.com/google-apps/calendar/v3/reference/ */
152
153 static void
154 parse_error_response (GDataService *self,
155 GDataOperationType operation_type,
156 guint status,
157 const gchar *reason_phrase,
158 const gchar *response_body,
159 gint length,
160 GError **error);
161 static GList *get_authorization_domains (void);
162
163 _GDATA_DEFINE_AUTHORIZATION_DOMAIN (calendar, "cl", "https://www.google.com/calendar/feeds/")
G_DEFINE_TYPE_WITH_CODE(GDataCalendarService,gdata_calendar_service,GDATA_TYPE_SERVICE,G_IMPLEMENT_INTERFACE (GDATA_TYPE_BATCHABLE,NULL))164 G_DEFINE_TYPE_WITH_CODE (GDataCalendarService, gdata_calendar_service, GDATA_TYPE_SERVICE, G_IMPLEMENT_INTERFACE (GDATA_TYPE_BATCHABLE, NULL))
165
166 static void
167 gdata_calendar_service_class_init (GDataCalendarServiceClass *klass)
168 {
169 GDataServiceClass *service_class = GDATA_SERVICE_CLASS (klass);
170 service_class->feed_type = GDATA_TYPE_CALENDAR_FEED;
171 service_class->parse_error_response = parse_error_response;
172 service_class->get_authorization_domains = get_authorization_domains;
173 }
174
175 static void
gdata_calendar_service_init(GDataCalendarService * self)176 gdata_calendar_service_init (GDataCalendarService *self)
177 {
178 /* Nothing to see here */
179 }
180
181 /* The error format used by the Google Calendar API doesn’t seem to be
182 * documented anywhere, which is a little frustrating. Here’s an example of it:
183 * {
184 * "error": {
185 * "errors": [
186 * {
187 * "domain": "global",
188 * "reason": "parseError",
189 * "message": "Parse Error",
190 * }
191 * ],
192 * "code": 400,
193 * "message": "Parse Error"
194 * }
195 * }
196 * or:
197 * {
198 * "error": {
199 * "errors": [
200 * {
201 * "domain": "global",
202 * "reason": "required",
203 * "message": "Missing end time."
204 * }
205 * ],
206 * "code": 400,
207 * "message": "Missing end time."
208 * }
209 * }
210 */
211 static void
parse_error_response(GDataService * self,GDataOperationType operation_type,guint status,const gchar * reason_phrase,const gchar * response_body,gint length,GError ** error)212 parse_error_response (GDataService *self,
213 GDataOperationType operation_type,
214 guint status,
215 const gchar *reason_phrase,
216 const gchar *response_body,
217 gint length,
218 GError **error)
219 {
220 JsonParser *parser = NULL; /* owned */
221 JsonReader *reader = NULL; /* owned */
222 gint i;
223 GError *child_error = NULL;
224
225 if (response_body == NULL) {
226 goto parent;
227 }
228
229 if (length == -1) {
230 length = strlen (response_body);
231 }
232
233 parser = json_parser_new ();
234 if (!json_parser_load_from_data (parser, response_body, length,
235 &child_error)) {
236 goto parent;
237 }
238
239 reader = json_reader_new (json_parser_get_root (parser));
240
241 /* Check that the outermost node is an object. */
242 if (!json_reader_is_object (reader)) {
243 goto parent;
244 }
245
246 /* Grab the ‘error’ member, then its ‘errors’ member. */
247 if (!json_reader_read_member (reader, "error") ||
248 !json_reader_is_object (reader) ||
249 !json_reader_read_member (reader, "errors") ||
250 !json_reader_is_array (reader)) {
251 goto parent;
252 }
253
254 /* Parse each of the errors. Return the first one, and print out any
255 * others. */
256 for (i = 0; i < json_reader_count_elements (reader); i++) {
257 const gchar *domain, *reason, *message, *extended_help;
258 const gchar *location_type, *location;
259
260 /* Parse the error. */
261 if (!json_reader_read_element (reader, i) ||
262 !json_reader_is_object (reader)) {
263 goto parent;
264 }
265
266 json_reader_read_member (reader, "domain");
267 domain = json_reader_get_string_value (reader);
268 json_reader_end_member (reader);
269
270 json_reader_read_member (reader, "reason");
271 reason = json_reader_get_string_value (reader);
272 json_reader_end_member (reader);
273
274 json_reader_read_member (reader, "message");
275 message = json_reader_get_string_value (reader);
276 json_reader_end_member (reader);
277
278 json_reader_read_member (reader, "extendedHelp");
279 extended_help = json_reader_get_string_value (reader);
280 json_reader_end_member (reader);
281
282 json_reader_read_member (reader, "locationType");
283 location_type = json_reader_get_string_value (reader);
284 json_reader_end_member (reader);
285
286 json_reader_read_member (reader, "location");
287 location = json_reader_get_string_value (reader);
288 json_reader_end_member (reader);
289
290 /* End the error element. */
291 json_reader_end_element (reader);
292
293 /* Create an error message, but only for the first error */
294 if (error == NULL || *error == NULL) {
295 if (g_strcmp0 (domain, "usageLimits") == 0 &&
296 g_strcmp0 (reason,
297 "dailyLimitExceededUnreg") == 0) {
298 /* Daily Limit for Unauthenticated Use
299 * Exceeded. */
300 g_set_error (error, GDATA_SERVICE_ERROR,
301 GDATA_SERVICE_ERROR_API_QUOTA_EXCEEDED,
302 _("You have made too many API "
303 "calls recently. Please wait a "
304 "few minutes and try again."));
305 } else if (g_strcmp0 (domain, "global") == 0 &&
306 g_strcmp0 (reason, "notFound") == 0) {
307 /* Calendar not found. */
308 g_set_error (error, GDATA_SERVICE_ERROR,
309 GDATA_SERVICE_ERROR_NOT_FOUND,
310 /* Translators: the parameter is an
311 * error message returned by the
312 * server. */
313 _("The requested resource was not found: %s"),
314 message);
315 } else if ((g_strcmp0 (domain, "global") == 0 &&
316 g_strcmp0 (reason, "required") == 0) ||
317 (g_strcmp0 (domain, "global") == 0 &&
318 g_strcmp0 (reason, "conditionNotMet") == 0)) {
319 /* Client-side protocol error. */
320 g_set_error (error, GDATA_SERVICE_ERROR,
321 GDATA_SERVICE_ERROR_PROTOCOL_ERROR,
322 /* Translators: the parameter is an
323 * error message returned by the
324 * server. */
325 _("Invalid request URI or header, "
326 "or unsupported nonstandard "
327 "parameter: %s"), message);
328 } else if (g_strcmp0 (domain, "global") == 0 &&
329 (g_strcmp0 (reason, "authError") == 0 ||
330 g_strcmp0 (reason, "required") == 0)) {
331 /* Authentication problem */
332 g_set_error (error, GDATA_SERVICE_ERROR,
333 GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
334 _("You must be authenticated to "
335 "do this."));
336 } else if (g_strcmp0 (domain, "global") == 0 &&
337 g_strcmp0 (reason, "forbidden") == 0) {
338 g_set_error (error, GDATA_SERVICE_ERROR,
339 GDATA_SERVICE_ERROR_FORBIDDEN,
340 _("Access was denied by the user "
341 "or server."));
342 } else {
343 /* Unknown or validation (protocol) error. Fall
344 * back to working off the HTTP status code. */
345 g_warning ("Unknown error code ‘%s’ in domain "
346 "‘%s’ received with location type "
347 "‘%s’, location ‘%s’, extended help "
348 "‘%s’ and message ‘%s’.",
349 reason, domain, location_type,
350 location, extended_help, message);
351
352 goto parent;
353 }
354 } else {
355 /* For all errors after the first, log the error in the
356 * terminal. */
357 g_debug ("Error message received in response: domain "
358 "‘%s’, reason ‘%s’, extended help ‘%s’, "
359 "message ‘%s’, location type ‘%s’, location "
360 "‘%s’.",
361 domain, reason, extended_help, message,
362 location_type, location);
363 }
364 }
365
366 /* End the ‘errors’ and ‘error’ members. */
367 json_reader_end_element (reader);
368 json_reader_end_element (reader);
369
370 g_clear_object (&reader);
371 g_clear_object (&parser);
372
373 /* Ensure we’ve actually set an error message. */
374 g_assert (error == NULL || *error != NULL);
375
376 return;
377
378 parent:
379 g_clear_object (&reader);
380 g_clear_object (&parser);
381
382 /* Chain up to the parent class */
383 GDATA_SERVICE_CLASS (gdata_calendar_service_parent_class)->parse_error_response (self, operation_type, status, reason_phrase,
384 response_body, length, error);
385 }
386
387 static GList *
get_authorization_domains(void)388 get_authorization_domains (void)
389 {
390 return g_list_prepend (NULL, get_calendar_authorization_domain ());
391 }
392
393 /**
394 * gdata_calendar_service_new:
395 * @authorizer: (allow-none): a #GDataAuthorizer to authorize the service's requests, or %NULL
396 *
397 * Creates a new #GDataCalendarService using the given #GDataAuthorizer. If @authorizer is %NULL, all requests are made as an unauthenticated user.
398 *
399 * Return value: a new #GDataCalendarService, or %NULL; unref with g_object_unref()
400 *
401 * Since: 0.9.0
402 */
403 GDataCalendarService *
gdata_calendar_service_new(GDataAuthorizer * authorizer)404 gdata_calendar_service_new (GDataAuthorizer *authorizer)
405 {
406 g_return_val_if_fail (authorizer == NULL || GDATA_IS_AUTHORIZER (authorizer), NULL);
407
408 return g_object_new (GDATA_TYPE_CALENDAR_SERVICE,
409 "authorizer", authorizer,
410 NULL);
411 }
412
413 /**
414 * gdata_calendar_service_get_primary_authorization_domain:
415 *
416 * The primary #GDataAuthorizationDomain for interacting with Google Calendar. This will not normally need to be used, as it's used internally
417 * by the #GDataCalendarService methods. However, if using the plain #GDataService methods to implement custom queries or requests which libgdata
418 * does not support natively, then this domain may be needed to authorize the requests.
419 *
420 * The domain never changes, and is interned so that pointer comparison can be used to differentiate it from other authorization domains.
421 *
422 * Return value: (transfer none): the service's authorization domain
423 *
424 * Since: 0.9.0
425 */
426 GDataAuthorizationDomain *
gdata_calendar_service_get_primary_authorization_domain(void)427 gdata_calendar_service_get_primary_authorization_domain (void)
428 {
429 return get_calendar_authorization_domain ();
430 }
431
432 /**
433 * gdata_calendar_service_query_all_calendars:
434 * @self: a #GDataCalendarService
435 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
436 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
437 * @progress_callback: (allow-none) (scope call) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
438 * @progress_user_data: (closure): data to pass to the @progress_callback function
439 * @error: a #GError, or %NULL
440 *
441 * Queries the service to return a list of all calendars from the authenticated account which match the given
442 * @query. It will return all calendars the user has read access to, including primary, secondary and imported
443 * calendars.
444 *
445 * For more details, see gdata_service_query().
446 *
447 * Return value: (transfer full): a #GDataFeed of query results; unref with g_object_unref()
448 */
449 GDataFeed *
gdata_calendar_service_query_all_calendars(GDataCalendarService * self,GDataQuery * query,GCancellable * cancellable,GDataQueryProgressCallback progress_callback,gpointer progress_user_data,GError ** error)450 gdata_calendar_service_query_all_calendars (GDataCalendarService *self, GDataQuery *query, GCancellable *cancellable,
451 GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
452 {
453 GDataFeed *feed;
454 gchar *request_uri;
455
456 g_return_val_if_fail (GDATA_IS_CALENDAR_SERVICE (self), NULL);
457 g_return_val_if_fail (query == NULL || GDATA_IS_QUERY (query), NULL);
458 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
459 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
460
461 /* Ensure we're authenticated first */
462 if (gdata_authorizer_is_authorized_for_domain (gdata_service_get_authorizer (GDATA_SERVICE (self)),
463 get_calendar_authorization_domain ()) == FALSE) {
464 g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
465 _("You must be authenticated to query all calendars."));
466 return NULL;
467 }
468
469 request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList", NULL);
470 feed = gdata_service_query (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
471 cancellable, progress_callback, progress_user_data, error);
472 g_free (request_uri);
473
474 return feed;
475 }
476
477 /**
478 * gdata_calendar_service_query_all_calendars_async:
479 * @self: a #GDataCalendarService
480 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
481 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
482 * @progress_callback: (allow-none) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
483 * @progress_user_data: (closure): data to pass to the @progress_callback function
484 * @destroy_progress_user_data: (allow-none): the function to call when @progress_callback will not be called any more, or %NULL. This function will be
485 * called with @progress_user_data as a parameter and can be used to free any memory allocated for it.
486 * @callback: a #GAsyncReadyCallback to call when authentication is finished
487 * @user_data: (closure): data to pass to the @callback function
488 *
489 * Queries the service to return a list of all calendars from the authenticated account which match the given
490 * @query. @self and @query are all reffed when this function is called, so can safely be unreffed after
491 * this function returns.
492 *
493 * For more details, see gdata_calendar_service_query_all_calendars(), which is the synchronous version of
494 * this function, and gdata_service_query_async(), which is the base asynchronous query function.
495 *
496 * Since: 0.9.1
497 */
498 void
gdata_calendar_service_query_all_calendars_async(GDataCalendarService * self,GDataQuery * query,GCancellable * cancellable,GDataQueryProgressCallback progress_callback,gpointer progress_user_data,GDestroyNotify destroy_progress_user_data,GAsyncReadyCallback callback,gpointer user_data)499 gdata_calendar_service_query_all_calendars_async (GDataCalendarService *self, GDataQuery *query, GCancellable *cancellable,
500 GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
501 GDestroyNotify destroy_progress_user_data,
502 GAsyncReadyCallback callback, gpointer user_data)
503 {
504 gchar *request_uri;
505
506 g_return_if_fail (GDATA_IS_CALENDAR_SERVICE (self));
507 g_return_if_fail (query == NULL || GDATA_IS_QUERY (query));
508 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
509 g_return_if_fail (callback != NULL);
510
511 /* Ensure we're authenticated first */
512 if (gdata_authorizer_is_authorized_for_domain (gdata_service_get_authorizer (GDATA_SERVICE (self)),
513 get_calendar_authorization_domain ()) == FALSE) {
514 g_autoptr(GTask) task = NULL;
515
516 task = g_task_new (self, cancellable, callback, user_data);
517 g_task_set_source_tag (task, gdata_service_query_async);
518 g_task_return_new_error (task, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED, "%s",
519 _("You must be authenticated to query all calendars."));
520
521 return;
522 }
523
524 request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList", NULL);
525 gdata_service_query_async (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
526 cancellable, progress_callback, progress_user_data, destroy_progress_user_data, callback, user_data);
527 g_free (request_uri);
528 }
529
530 /**
531 * gdata_calendar_service_query_own_calendars:
532 * @self: a #GDataCalendarService
533 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
534 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
535 * @progress_callback: (allow-none) (scope call) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
536 * @progress_user_data: (closure): data to pass to the @progress_callback function
537 * @error: a #GError, or %NULL
538 *
539 * Queries the service to return a list of calendars from the authenticated account which match the given
540 * @query, and the authenticated user owns. (i.e. They have full read/write access to the calendar, as well
541 * as the ability to set permissions on the calendar.)
542 *
543 * For more details, see gdata_service_query().
544 *
545 * Return value: (transfer full): a #GDataFeed of query results; unref with g_object_unref()
546 */
547 GDataFeed *
gdata_calendar_service_query_own_calendars(GDataCalendarService * self,GDataQuery * query,GCancellable * cancellable,GDataQueryProgressCallback progress_callback,gpointer progress_user_data,GError ** error)548 gdata_calendar_service_query_own_calendars (GDataCalendarService *self, GDataQuery *query, GCancellable *cancellable,
549 GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
550 {
551 GDataFeed *feed;
552 gchar *request_uri;
553
554 g_return_val_if_fail (GDATA_IS_CALENDAR_SERVICE (self), NULL);
555 g_return_val_if_fail (query == NULL || GDATA_IS_QUERY (query), NULL);
556 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
557 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
558
559 /* Ensure we're authenticated first */
560 if (gdata_authorizer_is_authorized_for_domain (gdata_service_get_authorizer (GDATA_SERVICE (self)),
561 get_calendar_authorization_domain ()) == FALSE) {
562 g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
563 _("You must be authenticated to query your own calendars."));
564 return NULL;
565 }
566
567 request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList?minAccessRole=owner", NULL);
568 feed = gdata_service_query (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
569 cancellable, progress_callback, progress_user_data, error);
570 g_free (request_uri);
571
572 return feed;
573 }
574
575 /**
576 * gdata_calendar_service_query_own_calendars_async:
577 * @self: a #GDataCalendarService
578 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
579 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
580 * @progress_callback: (allow-none) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
581 * @progress_user_data: (closure): data to pass to the @progress_callback function
582 * @destroy_progress_user_data: (allow-none): the function to call when @progress_callback will not be called any more, or %NULL. This function will be
583 * called with @progress_user_data as a parameter and can be used to free any memory allocated for it.
584 * @callback: a #GAsyncReadyCallback to call when authentication is finished
585 * @user_data: (closure): data to pass to the @callback function
586 *
587 * Queries the service to return a list of calendars from the authenticated account which match the given
588 * @query, and the authenticated user owns. @self and @query are all reffed when this function is called,
589 * so can safely be unreffed after this function returns.
590 *
591 * For more details, see gdata_calendar_service_query_own_calendars(), which is the synchronous version of
592 * this function, and gdata_service_query_async(), which is the base asynchronous query function.
593 *
594 * Since: 0.9.1
595 */
596 void
gdata_calendar_service_query_own_calendars_async(GDataCalendarService * self,GDataQuery * query,GCancellable * cancellable,GDataQueryProgressCallback progress_callback,gpointer progress_user_data,GDestroyNotify destroy_progress_user_data,GAsyncReadyCallback callback,gpointer user_data)597 gdata_calendar_service_query_own_calendars_async (GDataCalendarService *self, GDataQuery *query, GCancellable *cancellable,
598 GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
599 GDestroyNotify destroy_progress_user_data,
600 GAsyncReadyCallback callback, gpointer user_data)
601 {
602 gchar *request_uri;
603
604 g_return_if_fail (GDATA_IS_CALENDAR_SERVICE (self));
605 g_return_if_fail (query == NULL || GDATA_IS_QUERY (query));
606 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
607 g_return_if_fail (callback != NULL);
608
609 /* Ensure we're authenticated first */
610 if (gdata_authorizer_is_authorized_for_domain (gdata_service_get_authorizer (GDATA_SERVICE (self)),
611 get_calendar_authorization_domain ()) == FALSE) {
612 g_autoptr(GTask) task = NULL;
613
614 task = g_task_new (self, cancellable, callback, user_data);
615 g_task_set_source_tag (task, gdata_service_query_async);
616 g_task_return_new_error (task, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED, "%s",
617 _("You must be authenticated to query your own calendars."));
618
619 return;
620 }
621
622 request_uri = g_strconcat (_gdata_service_get_scheme (), "://www.googleapis.com/calendar/v3/users/me/calendarList?minAccessRole=owner", NULL);
623 gdata_service_query_async (GDATA_SERVICE (self), get_calendar_authorization_domain (), request_uri, query, GDATA_TYPE_CALENDAR_CALENDAR,
624 cancellable, progress_callback, progress_user_data, destroy_progress_user_data, callback, user_data);
625 g_free (request_uri);
626 }
627
628 static gchar *
build_events_uri(GDataCalendarCalendar * calendar)629 build_events_uri (GDataCalendarCalendar *calendar)
630 {
631 GString *uri;
632 const gchar *calendar_id;
633
634 calendar_id = (calendar != NULL) ? gdata_entry_get_id (GDATA_ENTRY (calendar)) : "default";
635
636 uri = g_string_new (_gdata_service_get_scheme ());
637 g_string_append (uri, "://www.googleapis.com/calendar/v3/calendars/");
638 g_string_append_uri_escaped (uri, calendar_id, NULL, FALSE);
639 g_string_append (uri, "/events");
640
641 return g_string_free (uri, FALSE);
642 }
643
644 /**
645 * gdata_calendar_service_query_events:
646 * @self: a #GDataCalendarService
647 * @calendar: a #GDataCalendarCalendar
648 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
649 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
650 * @progress_callback: (allow-none) (scope call) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
651 * @progress_user_data: (closure): data to pass to the @progress_callback function
652 * @error: a #GError, or %NULL
653 *
654 * Queries the service to return a list of events in the given @calendar, which match @query.
655 *
656 * For more details, see gdata_service_query().
657 *
658 * Return value: (transfer full): a #GDataFeed of query results; unref with g_object_unref()
659 */
660 GDataFeed *
gdata_calendar_service_query_events(GDataCalendarService * self,GDataCalendarCalendar * calendar,GDataQuery * query,GCancellable * cancellable,GDataQueryProgressCallback progress_callback,gpointer progress_user_data,GError ** error)661 gdata_calendar_service_query_events (GDataCalendarService *self, GDataCalendarCalendar *calendar, GDataQuery *query, GCancellable *cancellable,
662 GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
663 {
664 gchar *request_uri;
665 GDataFeed *feed;
666
667 g_return_val_if_fail (GDATA_IS_CALENDAR_SERVICE (self), NULL);
668 g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (calendar), NULL);
669 g_return_val_if_fail (query == NULL || GDATA_IS_QUERY (query), NULL);
670 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
671 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
672
673 /* Ensure we're authenticated first */
674 if (gdata_authorizer_is_authorized_for_domain (gdata_service_get_authorizer (GDATA_SERVICE (self)),
675 get_calendar_authorization_domain ()) == FALSE) {
676 g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
677 _("You must be authenticated to query your own calendars."));
678 return NULL;
679 }
680
681 /* Execute the query. */
682 request_uri = build_events_uri (calendar);
683 feed = gdata_service_query (GDATA_SERVICE (self),
684 get_calendar_authorization_domain (),
685 request_uri, query,
686 GDATA_TYPE_CALENDAR_EVENT, cancellable,
687 progress_callback, progress_user_data,
688 error);
689 g_free (request_uri);
690
691 return feed;
692 }
693
694 /**
695 * gdata_calendar_service_query_events_async:
696 * @self: a #GDataCalendarService
697 * @calendar: a #GDataCalendarCalendar
698 * @query: (allow-none): a #GDataQuery with the query parameters, or %NULL
699 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
700 * @progress_callback: (allow-none) (closure progress_user_data): a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
701 * @progress_user_data: (closure): data to pass to the @progress_callback function
702 * @destroy_progress_user_data: (allow-none): the function to call when @progress_callback will not be called any more, or %NULL. This function will be
703 * called with @progress_user_data as a parameter and can be used to free any memory allocated for it.
704 * @callback: a #GAsyncReadyCallback to call when the query is finished
705 * @user_data: (closure): data to pass to the @callback function
706 *
707 * Queries the service to return a list of events in the given @calendar, which match @query. @self, @calendar and @query are all reffed when this
708 * function is called, so can safely be unreffed after this function returns.
709 *
710 * Get the results of the query using gdata_service_query_finish() in the @callback.
711 *
712 * For more details, see gdata_calendar_service_query_events(), which is the synchronous version of this function, and gdata_service_query_async(),
713 * which is the base asynchronous query function.
714 *
715 * Since: 0.9.1
716 */
717 void
gdata_calendar_service_query_events_async(GDataCalendarService * self,GDataCalendarCalendar * calendar,GDataQuery * query,GCancellable * cancellable,GDataQueryProgressCallback progress_callback,gpointer progress_user_data,GDestroyNotify destroy_progress_user_data,GAsyncReadyCallback callback,gpointer user_data)718 gdata_calendar_service_query_events_async (GDataCalendarService *self, GDataCalendarCalendar *calendar, GDataQuery *query, GCancellable *cancellable,
719 GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
720 GDestroyNotify destroy_progress_user_data,
721 GAsyncReadyCallback callback, gpointer user_data)
722 {
723 gchar *request_uri;
724
725 g_return_if_fail (GDATA_IS_CALENDAR_SERVICE (self));
726 g_return_if_fail (GDATA_IS_CALENDAR_CALENDAR (calendar));
727 g_return_if_fail (query == NULL || GDATA_IS_QUERY (query));
728 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
729 g_return_if_fail (callback != NULL);
730
731 /* Ensure we're authenticated first */
732 if (gdata_authorizer_is_authorized_for_domain (gdata_service_get_authorizer (GDATA_SERVICE (self)),
733 get_calendar_authorization_domain ()) == FALSE) {
734 g_autoptr(GTask) task = NULL;
735
736 task = g_task_new (self, cancellable, callback, user_data);
737 g_task_set_source_tag (task, gdata_service_query_async);
738 g_task_return_new_error (task, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED, "%s",
739 _("You must be authenticated to query your own calendars."));
740
741 return;
742 }
743
744 /* Execute the query. */
745 request_uri = build_events_uri (calendar);
746 gdata_service_query_async (GDATA_SERVICE (self),
747 get_calendar_authorization_domain (),
748 request_uri, query,
749 GDATA_TYPE_CALENDAR_EVENT, cancellable,
750 progress_callback, progress_user_data,
751 destroy_progress_user_data, callback,
752 user_data);
753 g_free (request_uri);
754 }
755
756 /**
757 * gdata_calendar_service_insert_event:
758 * @self: a #GDataCalendarService
759 * @event: the #GDataCalendarEvent to insert
760 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
761 * @error: a #GError, or %NULL
762 *
763 * Inserts @event by uploading it to the online calendar service.
764 *
765 * For more details, see gdata_service_insert_entry().
766 *
767 * Return value: (transfer full): an updated #GDataCalendarEvent, or %NULL; unref with g_object_unref()
768 *
769 * Since: 0.2.0
770 * Deprecated: 0.17.2: Use gdata_calendar_service_insert_calendar_event()
771 * instead to be able to specify the calendar to add the event to; otherwise
772 * the default calendar will be used.
773 */
774 GDataCalendarEvent *
gdata_calendar_service_insert_event(GDataCalendarService * self,GDataCalendarEvent * event,GCancellable * cancellable,GError ** error)775 gdata_calendar_service_insert_event (GDataCalendarService *self, GDataCalendarEvent *event, GCancellable *cancellable, GError **error)
776 {
777 gchar *uri;
778 GDataEntry *entry;
779
780 g_return_val_if_fail (GDATA_IS_CALENDAR_SERVICE (self), NULL);
781 g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (event), NULL);
782 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
783 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
784
785 uri = build_events_uri (NULL);
786 entry = gdata_service_insert_entry (GDATA_SERVICE (self), get_calendar_authorization_domain (), uri, GDATA_ENTRY (event), cancellable, error);
787 g_free (uri);
788
789 return GDATA_CALENDAR_EVENT (entry);
790 }
791
792 /**
793 * gdata_calendar_service_insert_calendar_event:
794 * @self: a #GDataCalendarService
795 * @calendar: the #GDataCalendarCalendar to insert the event into
796 * @event: the #GDataCalendarEvent to insert
797 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
798 * @error: a #GError, or %NULL
799 *
800 * Inserts @event by uploading it to the online calendar service, adding it to
801 * the specified @calendar.
802 *
803 * For more details, see gdata_service_insert_entry().
804 *
805 * Return value: (transfer full): an updated #GDataCalendarEvent, or %NULL;
806 * unref with g_object_unref()
807 *
808 * Since: 0.17.2
809 */
810 GDataCalendarEvent *
gdata_calendar_service_insert_calendar_event(GDataCalendarService * self,GDataCalendarCalendar * calendar,GDataCalendarEvent * event,GCancellable * cancellable,GError ** error)811 gdata_calendar_service_insert_calendar_event (GDataCalendarService *self,
812 GDataCalendarCalendar *calendar,
813 GDataCalendarEvent *event,
814 GCancellable *cancellable,
815 GError **error)
816 {
817 gchar *uri;
818 GDataEntry *entry;
819
820 g_return_val_if_fail (GDATA_IS_CALENDAR_SERVICE (self), NULL);
821 g_return_val_if_fail (GDATA_IS_CALENDAR_CALENDAR (calendar), NULL);
822 g_return_val_if_fail (GDATA_IS_CALENDAR_EVENT (event), NULL);
823 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
824 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
825
826 uri = build_events_uri (calendar);
827 entry = gdata_service_insert_entry (GDATA_SERVICE (self),
828 get_calendar_authorization_domain (),
829 uri, GDATA_ENTRY (event),
830 cancellable, error);
831 g_free (uri);
832
833 return GDATA_CALENDAR_EVENT (entry);
834 }
835
836 /**
837 * gdata_calendar_service_insert_event_async:
838 * @self: a #GDataCalendarService
839 * @event: the #GDataCalendarEvent to insert
840 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
841 * @callback: a #GAsyncReadyCallback to call when insertion is finished
842 * @user_data: (closure): data to pass to the @callback function
843 *
844 * Inserts @event by uploading it to the online calendar service. @self and @event are both reffed when this function is called, so can safely be
845 * unreffed after this function returns.
846 *
847 * @callback should call gdata_service_insert_entry_finish() to obtain a #GDataCalendarEvent representing the inserted event and to check for possible
848 * errors.
849 *
850 * For more details, see gdata_calendar_service_insert_event(), which is the synchronous version of this function, and
851 * gdata_service_insert_entry_async(), which is the base asynchronous insertion function.
852 *
853 * Since: 0.8.0
854 * Deprecated: 0.17.2: Use
855 * gdata_calendar_service_insert_calendar_event_async() instead to be able to
856 * specify the calendar to add the event to; otherwise the default calendar
857 * will be used.
858 */
859 void
gdata_calendar_service_insert_event_async(GDataCalendarService * self,GDataCalendarEvent * event,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)860 gdata_calendar_service_insert_event_async (GDataCalendarService *self, GDataCalendarEvent *event, GCancellable *cancellable,
861 GAsyncReadyCallback callback, gpointer user_data)
862 {
863 gchar *uri;
864
865 g_return_if_fail (GDATA_IS_CALENDAR_SERVICE (self));
866 g_return_if_fail (GDATA_IS_CALENDAR_EVENT (event));
867 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
868
869 uri = build_events_uri (NULL);
870 gdata_service_insert_entry_async (GDATA_SERVICE (self), get_calendar_authorization_domain (), uri, GDATA_ENTRY (event), cancellable,
871 callback, user_data);
872 g_free (uri);
873 }
874
875 /**
876 * gdata_calendar_service_insert_calendar_event_async:
877 * @self: a #GDataCalendarService
878 * @calendar: the #GDataCalendarCalendar to insert the event into
879 * @event: the #GDataCalendarEvent to insert
880 * @cancellable: (allow-none): optional #GCancellable object, or %NULL
881 * @callback: a #GAsyncReadyCallback to call when insertion is finished
882 * @user_data: (closure): data to pass to the @callback function
883 *
884 * Inserts @event by uploading it to the online calendar service, adding it to
885 * the specified @calendar. @self and @event are both reffed when this function
886 * is called, so can safely be unreffed after this function returns.
887 *
888 * @callback should call gdata_service_insert_entry_finish() to obtain a
889 * #GDataCalendarEvent representing the inserted event and to check for possible
890 * errors.
891 *
892 * For more details, see gdata_calendar_service_insert_event(), which is the
893 * synchronous version of this function, and gdata_service_insert_entry_async(),
894 * which is the base asynchronous insertion function.
895 *
896 * Since: 0.17.2
897 */
898 void
gdata_calendar_service_insert_calendar_event_async(GDataCalendarService * self,GDataCalendarCalendar * calendar,GDataCalendarEvent * event,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)899 gdata_calendar_service_insert_calendar_event_async (GDataCalendarService *self,
900 GDataCalendarCalendar *calendar,
901 GDataCalendarEvent *event,
902 GCancellable *cancellable,
903 GAsyncReadyCallback callback,
904 gpointer user_data)
905 {
906 gchar *uri;
907
908 g_return_if_fail (GDATA_IS_CALENDAR_SERVICE (self));
909 g_return_if_fail (GDATA_IS_CALENDAR_EVENT (event));
910 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
911
912 uri = build_events_uri (calendar);
913 gdata_service_insert_entry_async (GDATA_SERVICE (self),
914 get_calendar_authorization_domain (),
915 uri, GDATA_ENTRY (event), cancellable,
916 callback, user_data);
917 g_free (uri);
918 }
919