566
set_authentication_details (GDataService *self, const gchar *username, const gchar *password, gboolean authenticated)
568
GObject *service = G_OBJECT (self);
569
GDataServicePrivate *priv = self->priv;
571
g_static_mutex_lock (&(priv->authentication_mutex));
573
g_object_freeze_notify (service);
575
if (authenticated == TRUE) {
576
/* Update several properties the service holds */
577
g_free (priv->username);
579
/* Ensure the username is always a full e-mail address */
580
if (strchr (username, '@') == NULL)
581
priv->username = g_strdup_printf ("%s@" EMAIL_DOMAIN, username);
583
priv->username = g_strdup (username);
585
g_free (priv->password);
586
priv->password = g_strdup (password);
588
g_object_notify (service, "username");
589
g_object_notify (service, "password");
592
if (priv->authenticated != authenticated) {
593
priv->authenticated = authenticated;
594
g_object_notify (service, "authenticated");
597
g_object_thaw_notify (service);
599
g_static_mutex_unlock (&(priv->authentication_mutex));
603
authenticate (GDataService *self, const gchar *username, const gchar *password, gchar *captcha_token, gchar *captcha_answer,
604
GCancellable *cancellable, GError **error)
606
GDataServicePrivate *priv = self->priv;
607
GDataServiceClass *klass;
608
SoupMessage *message;
613
/* Prepare the request */
614
klass = GDATA_SERVICE_GET_CLASS (self);
615
request_body = soup_form_encode ("accountType", "HOSTED_OR_GOOGLE",
618
"service", klass->service_name,
619
"source", priv->client_id,
620
(captcha_token == NULL) ? NULL : "logintoken", captcha_token,
621
"loginanswer", captcha_answer,
624
/* Free the CAPTCHA token and answer if necessary */
625
g_free (captcha_token);
626
g_free (captcha_answer);
628
/* Build the message */
629
message = soup_message_new (SOUP_METHOD_POST, klass->authentication_uri);
630
soup_message_set_request (message, "application/x-www-form-urlencoded", SOUP_MEMORY_TAKE, request_body, strlen (request_body));
632
/* Send the message */
633
status = _gdata_service_send_message (self, message, cancellable, error);
635
if (status == SOUP_STATUS_CANCELLED) {
636
/* Cancelled (the error has already been set) */
637
g_object_unref (message);
639
} else if (status != SOUP_STATUS_OK) {
640
const gchar *response_body = message->response_body->data;
641
gchar *error_start, *error_end, *uri_start, *uri_end, *uri = NULL;
643
/* Parse the error response; see: http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Errors */
644
if (response_body == NULL)
648
error_start = strstr (response_body, "Error=");
649
if (error_start == NULL)
651
error_start += strlen ("Error=");
653
error_end = strstr (error_start, "\n");
654
if (error_end == NULL)
657
if (strncmp (error_start, "CaptchaRequired", error_end - error_start) == 0) {
658
const gchar *captcha_base_uri = "http://www.google.com/accounts/";
659
gchar *captcha_start, *captcha_end, *captcha_uri, *new_captcha_answer;
660
guint captcha_base_uri_length;
662
/* CAPTCHA required to log in */
663
captcha_start = strstr (response_body, "CaptchaUrl=");
664
if (captcha_start == NULL)
666
captcha_start += strlen ("CaptchaUrl=");
668
captcha_end = strstr (captcha_start, "\n");
669
if (captcha_end == NULL)
672
/* Do some fancy memory stuff to save ourselves another alloc */
673
captcha_base_uri_length = strlen (captcha_base_uri);
674
captcha_uri = g_malloc (captcha_base_uri_length + (captcha_end - captcha_start) + 1);
675
memcpy (captcha_uri, captcha_base_uri, captcha_base_uri_length);
676
memcpy (captcha_uri + captcha_base_uri_length, captcha_start, (captcha_end - captcha_start));
677
captcha_uri[captcha_base_uri_length + (captcha_end - captcha_start)] = '\0';
679
/* Request a CAPTCHA answer from the application */
680
g_signal_emit (self, service_signals[SIGNAL_CAPTCHA_CHALLENGE], 0, captcha_uri, &new_captcha_answer);
681
g_free (captcha_uri);
683
if (new_captcha_answer == NULL || *new_captcha_answer == '\0') {
684
/* Translators: see http://en.wikipedia.org/wiki/CAPTCHA for information about CAPTCHAs */
685
g_set_error_literal (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_CAPTCHA_REQUIRED,
686
_("A CAPTCHA must be filled out to log in."));
690
/* Get the CAPTCHA token */
691
captcha_start = strstr (response_body, "CaptchaToken=");
692
if (captcha_start == NULL)
694
captcha_start += strlen ("CaptchaToken=");
696
captcha_end = strstr (captcha_start, "\n");
697
if (captcha_end == NULL)
700
/* Save the CAPTCHA token and answer, and attempt to log in with them */
701
g_object_unref (message);
703
return authenticate (self, username, password, g_strndup (captcha_start, captcha_end - captcha_start), new_captcha_answer,
705
} else if (strncmp (error_start, "Unknown", error_end - error_start) == 0) {
707
} else if (strncmp (error_start, "BadAuthentication", error_end - error_start) == 0) {
708
/* Looks like Error=BadAuthentication errors don't return a URI */
709
g_set_error_literal (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_BAD_AUTHENTICATION,
710
_("Your username or password were incorrect."));
714
/* Get the information URI */
715
uri_start = strstr (response_body, "Url=");
716
if (uri_start == NULL)
718
uri_start += strlen ("Url=");
720
uri_end = strstr (uri_start, "\n");
724
uri = g_strndup (uri_start, uri_end - uri_start);
726
if (strncmp (error_start, "NotVerified", error_end - error_start) == 0) {
727
g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_NOT_VERIFIED,
728
/* Translators: the parameter is a URI for further information. */
729
_("Your account's e-mail address has not been verified. (%s)"), uri);
731
} else if (strncmp (error_start, "TermsNotAgreed", error_end - error_start) == 0) {
732
g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_TERMS_NOT_AGREED,
733
/* Translators: the parameter is a URI for further information. */
734
_("You have not agreed to the service's terms and conditions. (%s)"), uri);
736
} else if (strncmp (error_start, "AccountMigrated", error_end - error_start) == 0) {
737
/* This is non-standard, and used by YouTube since it's got messed-up accounts */
738
g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_ACCOUNT_MIGRATED,
739
/* Translators: the parameter is a URI for further information. */
740
_("This account has been migrated. Please log in online to receive your new username and password. (%s)"), uri);
742
} else if (strncmp (error_start, "AccountDeleted", error_end - error_start) == 0) {
743
g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_ACCOUNT_DELETED,
744
/* Translators: the parameter is a URI for further information. */
745
_("This account has been deleted. (%s)"), uri);
747
} else if (strncmp (error_start, "AccountDisabled", error_end - error_start) == 0) {
748
g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_ACCOUNT_DISABLED,
749
/* Translators: the parameter is a URI for further information. */
750
_("This account has been disabled. (%s)"), uri);
752
} else if (strncmp (error_start, "ServiceDisabled", error_end - error_start) == 0) {
753
g_set_error (error, GDATA_AUTHENTICATION_ERROR, GDATA_AUTHENTICATION_ERROR_SERVICE_DISABLED,
754
/* Translators: the parameter is a URI for further information. */
755
_("This account's access to this service has been disabled. (%s)"), uri);
757
} else if (strncmp (error_start, "ServiceUnavailable", error_end - error_start) == 0) {
758
g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_UNAVAILABLE,
759
/* Translators: the parameter is a URI for further information. */
760
_("This service is not available at the moment. (%s)"), uri);
764
/* Unknown error type! */
769
g_object_unref (message);
774
g_assert (message->response_body->data != NULL);
776
g_static_mutex_lock (&(priv->authentication_mutex));
777
retval = klass->parse_authentication_response (self, status, message->response_body->data, message->response_body->length, error);
778
g_static_mutex_unlock (&(priv->authentication_mutex));
780
g_object_unref (message);
785
g_assert (klass->parse_error_response != NULL);
786
klass->parse_error_response (self, GDATA_OPERATION_AUTHENTICATION, status, message->reason_phrase, message->response_body->data,
787
message->response_body->length, error);
789
g_object_unref (message);
797
} AuthenticateAsyncData;
800
authenticate_async_data_free (AuthenticateAsyncData *self)
802
g_free (self->username);
803
g_free (self->password);
805
g_slice_free (AuthenticateAsyncData, self);
809
authenticate_thread (GSimpleAsyncResult *result, GDataService *service, GCancellable *cancellable)
811
GError *error = NULL;
813
AuthenticateAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
815
/* Authenticate and return */
816
success = authenticate (service, data->username, data->password, NULL, NULL, cancellable, &error);
817
set_authentication_details (service, data->username, data->password, success);
818
g_simple_async_result_set_op_res_gboolean (result, success);
820
if (success == FALSE) {
821
g_simple_async_result_set_from_error (result, error);
822
g_error_free (error);
827
* gdata_service_authenticate_async:
828
* @self: a #GDataService
829
* @username: the user's username
830
* @password: the user's password
831
* @cancellable: optional #GCancellable object, or %NULL
832
* @callback: a #GAsyncReadyCallback to call when authentication is finished
833
* @user_data: (closure): data to pass to the @callback function
835
* Authenticates the #GDataService with the online service using the given @username and @password. @self, @username and
836
* @password are all reffed/copied when this function is called, so can safely be freed after this function returns.
838
* For more details, see gdata_service_authenticate(), which is the synchronous version of this function.
840
* When the operation is finished, @callback will be called. You can then call gdata_service_authenticate_finish()
841
* to get the results of the operation.
412
* gdata_service_is_authorized:
413
* @self: a #GDataService
415
* Determines whether the service is authorized for all the #GDataAuthorizationDomain<!-- -->s it belongs to (as returned by
416
* gdata_service_get_authorization_domains()). If the service's #GDataService:authorizer is %NULL, %FALSE is always returned.
418
* This is basically a convenience method for checking that the service's #GDataAuthorizer is authorized for all the service's
419
* #GDataAuthorizationDomain<!-- -->s.
421
* Return value: %TRUE if the service is authorized for all its domains, %FALSE otherwise
426
gdata_service_is_authorized (GDataService *self)
429
gboolean authorised = TRUE;
431
g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
433
/* If we don't have an authoriser set, we can't be authorised */
434
if (self->priv->authorizer == NULL) {
438
domains = gdata_service_get_authorization_domains (G_OBJECT_TYPE (self));
440
/* Find any domains which we're not authorised for */
441
for (i = domains; i != NULL; i = i->next) {
442
if (gdata_authorizer_is_authorized_for_domain (self->priv->authorizer, GDATA_AUTHORIZATION_DOMAIN (i->data)) == FALSE) {
448
g_list_free (domains);
454
* gdata_service_get_authorizer:
455
* @self: a #GDataService
457
* Gets the #GDataAuthorizer object currently in use by the service. See the documentation for #GDataService:authorizer for more details.
459
* Return value: (transfer none): the authorizer object for this service, or %NULL
464
gdata_service_get_authorizer (GDataService *self)
466
g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
468
return self->priv->authorizer;
472
* gdata_service_set_authorizer:
473
* @self: a #GDataService
474
* @authorizer: a new authorizer object for the service, or %NULL
476
* Sets #GDataService:authorizer to @authorizer. This may be %NULL if the service will only make requests in future which don't require authorization.
477
* See the documentation for #GDataService:authorizer for more information.
844
gdata_service_authenticate_async (GDataService *self, const gchar *username, const gchar *password,
845
GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
482
gdata_service_set_authorizer (GDataService *self, GDataAuthorizer *authorizer)
847
GSimpleAsyncResult *result;
848
AuthenticateAsyncData *data;
484
GDataServicePrivate *priv = self->priv;
850
486
g_return_if_fail (GDATA_IS_SERVICE (self));
851
g_return_if_fail (username != NULL);
852
g_return_if_fail (password != NULL);
853
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
855
data = g_slice_new (AuthenticateAsyncData);
856
data->username = g_strdup (username);
857
data->password = g_strdup (password);
859
result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_service_authenticate_async);
860
g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) authenticate_async_data_free);
861
g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) authenticate_thread, G_PRIORITY_DEFAULT, cancellable);
862
g_object_unref (result);
866
* gdata_service_authenticate_finish:
867
* @self: a #GDataService
868
* @async_result: a #GAsyncResult
869
* @error: a #GError, or %NULL
871
* Finishes an asynchronous authentication operation started with gdata_service_authenticate_async().
873
* Return value: %TRUE if authentication was successful, %FALSE otherwise
876
gdata_service_authenticate_finish (GDataService *self, GAsyncResult *async_result, GError **error)
878
GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (async_result);
881
g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
882
g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), FALSE);
883
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
885
g_warn_if_fail (g_simple_async_result_get_source_tag (result) == gdata_service_authenticate_async);
887
if (g_simple_async_result_propagate_error (result, error) == TRUE)
890
success = g_simple_async_result_get_op_res_gboolean (result);
891
g_assert (success == TRUE);
897
* gdata_service_authenticate:
898
* @self: a #GDataService
899
* @username: the user's username
900
* @password: the user's password
901
* @cancellable: optional #GCancellable object, or %NULL
902
* @error: a #GError, or %NULL
904
* Authenticates the #GDataService with the online service using @username and @password; i.e. logs into the service with the given
905
* user account. @username should be a full e-mail address (e.g. <literal>john.smith\@gmail.com</literal>). If a full e-mail address is
906
* not given, @username will have <literal>\@gmail.com</literal> appended to create an e-mail address
908
* If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
909
* If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
911
* A %GDATA_AUTHENTICATION_ERROR_BAD_AUTHENTICATION will be returned if authentication failed due to an incorrect username or password.
912
* Other #GDataAuthenticationError errors can be returned for other conditions.
914
* If the service requires a CAPTCHA to be completed, the #GDataService::captcha-challenge signal will be emitted. The return value from
915
* a signal handler for the signal should be a newly allocated string containing the text from the image. If the text is %NULL or empty,
916
* authentication will fail with a %GDATA_AUTHENTICATION_ERROR_CAPTCHA_REQUIRED error. Otherwise, authentication will be automatically and
917
* transparently restarted with the new CAPTCHA details.
919
* A %GDATA_SERVICE_ERROR_PROTOCOL_ERROR will be returned if the server's responses were invalid. Subclasses of #GDataService can override
920
* parsing the authentication response, and may return their own error codes. See their documentation for more details.
922
* Return value: %TRUE if authentication was successful, %FALSE otherwise
925
gdata_service_authenticate (GDataService *self, const gchar *username, const gchar *password, GCancellable *cancellable, GError **error)
929
g_return_val_if_fail (GDATA_IS_SERVICE (self), FALSE);
930
g_return_val_if_fail (username != NULL, FALSE);
931
g_return_val_if_fail (password != NULL, FALSE);
932
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
933
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
935
retval = authenticate (self, username, password, NULL, NULL, cancellable, error);
936
set_authentication_details (self, username, password, retval);
487
g_return_if_fail (authorizer == NULL || GDATA_IS_AUTHORIZER (authorizer));
489
if (priv->authorizer != NULL) {
490
g_object_unref (priv->authorizer);
493
priv->authorizer = authorizer;
495
if (priv->authorizer != NULL) {
496
g_object_ref (priv->authorizer);
499
g_object_notify (G_OBJECT (self), "authorizer");
503
* gdata_service_get_authorization_domains:
504
* @service_type: the #GType of the #GDataService subclass to retrieve the authorization domains for
506
* Retrieves the full list of #GDataAuthorizationDomain<!-- -->s which relate to the specified @service_type. All the
507
* #GDataAuthorizationDomain<!-- -->s are unique and interned, so can be compared with other domains by simple pointer comparison.
509
* Note that in addition to this method, #GDataService subclasses may expose some or all of their authorization domains individually by means of
510
* individual accessor functions.
512
* Return value: (transfer container) (element-type GDataAuthorizationDomain): an unordered list of #GDataAuthorizationDomain<!-- -->s; free with
518
gdata_service_get_authorization_domains (GType service_type)
520
GDataServiceClass *klass;
521
GList *domains = NULL;
523
g_return_val_if_fail (g_type_is_a (service_type, GDATA_TYPE_SERVICE), NULL);
525
klass = GDATA_SERVICE_CLASS (g_type_class_ref (service_type));
526
if (klass->get_authorization_domains != NULL) {
527
domains = klass->get_authorization_domains ();
529
g_type_class_unref (klass);
942
_gdata_service_build_message (GDataService *self, const gchar *method, const gchar *uri, const gchar *etag, gboolean etag_if_match)
535
_gdata_service_build_message (GDataService *self, GDataAuthorizationDomain *domain, const gchar *method, const gchar *uri,
536
const gchar *etag, gboolean etag_if_match)
944
538
SoupMessage *message;
945
539
GDataServiceClass *klass;