[frogr] Implemented 2nd phase of authorization process in terms of OAuth.
- From: Mario Sanchez Prada <msanchez src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [frogr] Implemented 2nd phase of authorization process in terms of OAuth.
- Date: Wed, 4 Apr 2012 14:16:46 +0000 (UTC)
commit accd0aaaf6bd1d3cef49816447f26e737325023e
Author: Mario Sanchez Prada <msanchez igalia com>
Date: Wed Apr 4 14:31:55 2012 +0200
Implemented 2nd phase of authorization process in terms of OAuth.
src/examples/example.c | 2 +-
src/flicksoup/fsp-error.c | 1 -
src/flicksoup/fsp-error.h | 6 +-
src/flicksoup/fsp-parser.c | 155 ++++++++++++++-----------------------------
src/flicksoup/fsp-parser.h | 2 +-
src/flicksoup/fsp-session.c | 82 ++++++++++++++++-------
src/flicksoup/fsp-session.h | 1 +
src/frogr-auth-dialog.c | 35 ++++++++--
src/frogr-controller.c | 20 ++++--
src/frogr-controller.h | 2 +-
10 files changed, 162 insertions(+), 144 deletions(-)
---
diff --git a/src/examples/example.c b/src/examples/example.c
index c44a377..00e5656 100644
--- a/src/examples/example.c
+++ b/src/examples/example.c
@@ -589,7 +589,7 @@ get_auth_url_cb (GObject *object,
/* Continue finishing the authorization */
g_print ("Finishing authorization...\n");
- fsp_session_complete_auth_async (session, NULL, complete_auth_cb, NULL);
+ fsp_session_complete_auth_async (session, NULL, NULL, complete_auth_cb, NULL);
g_free (auth_url);
}
diff --git a/src/flicksoup/fsp-error.c b/src/flicksoup/fsp-error.c
index 5a1357c..dfaef18 100644
--- a/src/flicksoup/fsp-error.c
+++ b/src/flicksoup/fsp-error.c
@@ -264,7 +264,6 @@ fsp_error_get_from_response_code (FspErrorMethod method, gint code)
{
switch (method)
{
- case FSP_ERROR_METHOD_GET_AUTH_TOKEN:
case FSP_ERROR_METHOD_GET_UPLOAD_STATUS:
case FSP_ERROR_METHOD_GROUP_GET_LIST:
/* We shouldn't get errors in this range for these methods,
diff --git a/src/flicksoup/fsp-error.h b/src/flicksoup/fsp-error.h
index fb10d33..76e3693 100644
--- a/src/flicksoup/fsp-error.h
+++ b/src/flicksoup/fsp-error.h
@@ -79,8 +79,9 @@ typedef enum {
FSP_ERROR_SERVICE_UNAVAILABLE,
/* Errors from flicksoup only */
- FSP_ERROR_REQUEST_TOKEN,
- FSP_ERROR_ACCESS_TOKEN,
+ FSP_ERROR_OAUTH_UNKNOWN_ERROR,
+ FSP_ERROR_OAUTH_NOT_AUTHORIZED_YET,
+ FSP_ERROR_OAUTH_VERIFIER_INVALID,
/* Default fallback for other kind of errors */
FSP_ERROR_OTHER
@@ -88,7 +89,6 @@ typedef enum {
typedef enum {
FSP_ERROR_METHOD_UNDEFINED,
- FSP_ERROR_METHOD_GET_AUTH_TOKEN,
FSP_ERROR_METHOD_GET_UPLOAD_STATUS,
FSP_ERROR_METHOD_PHOTO_UPLOAD,
FSP_ERROR_METHOD_PHOTO_GET_INFO,
diff --git a/src/flicksoup/fsp-parser.c b/src/flicksoup/fsp-parser.c
index c882b20..2ec72ed 100644
--- a/src/flicksoup/fsp-parser.c
+++ b/src/flicksoup/fsp-parser.c
@@ -22,6 +22,12 @@
#include "fsp-error.h"
+#ifdef HAVE_LIBSOUP_GNOME
+#include <libsoup/soup-gnome.h>
+#else
+#include <libsoup/soup.h>
+#endif
+
#include <libxml/parser.h>
#include <libxml/xpath.h>
@@ -62,9 +68,6 @@ _get_error_method_from_parser (gpointer (*body_parser)
(xmlDoc *doc,
GError **error));
static gpointer
-_get_auth_token_parser (xmlDoc *doc,
- GError **error);
-static gpointer
_get_upload_status_parser (xmlDoc *doc,
GError **error);
static gpointer
@@ -258,9 +261,7 @@ _get_error_method_from_parser (gpointer (*body_parser)
{
FspErrorMethod error_method = FSP_ERROR_METHOD_UNDEFINED;
- if (body_parser == _get_auth_token_parser)
- error_method = FSP_ERROR_METHOD_GET_AUTH_TOKEN;
- else if (body_parser == _get_upload_status_parser)
+ if (body_parser == _get_upload_status_parser)
error_method = FSP_ERROR_METHOD_GET_UPLOAD_STATUS;
else if (body_parser == _photo_get_upload_result_parser)
error_method = FSP_ERROR_METHOD_PHOTO_UPLOAD;
@@ -334,94 +335,6 @@ _process_xml_response (FspParser *self,
}
static gpointer
-_get_auth_token_parser (xmlDoc *doc,
- GError **error)
-{
- xmlXPathContext *xpathCtx = NULL;
- xmlXPathObject * xpathObj = NULL;
- FspDataAuthToken *auth_token = NULL;
- GError *err = NULL;
-
- g_return_val_if_fail (doc != NULL, NULL);
-
- xpathCtx = xmlXPathNewContext (doc);
- xpathObj = xmlXPathEvalExpression ((xmlChar *)"/rsp/auth", xpathCtx);
-
- if ((xpathObj != NULL) && (xpathObj->nodesetval->nodeNr > 0))
- {
- /* Matching nodes found */
- xmlNode *node = NULL;
- xmlChar *content = NULL;
-
- auth_token = FSP_DATA_AUTH_TOKEN (fsp_data_new (FSP_AUTH_TOKEN));
-
- /* Traverse children of the 'auth' node */
- node = xpathObj->nodesetval->nodeTab[0];
- for (node = node->children; node != NULL; node = node->next)
- {
- if (node->type != XML_ELEMENT_NODE)
- continue;
-
- /* Token string */
- if (!g_strcmp0 ((gchar *) node->name, "token"))
- {
- content = xmlNodeGetContent (node);
- auth_token->token = g_strdup ((gchar *) content);
- xmlFree (content);
- }
-
- /* Permissions */
- if (!g_strcmp0 ((gchar *) node->name, "perms"))
- {
- content = xmlNodeGetContent (node);
- auth_token->permissions = g_strdup ((gchar *) content);
- xmlFree (content);
- }
-
- /* User profile */
- if (!g_strcmp0 ((gchar *) node->name, "user"))
- {
- xmlChar *value = NULL;
-
- value = xmlGetProp (node, (const xmlChar *) "nsid");
- auth_token->nsid = g_strdup ((gchar *) value);
- xmlFree (value);
-
- value = xmlGetProp (node, (const xmlChar *) "username");
- auth_token->username = g_strdup ((gchar *) value);
- xmlFree (value);
-
- value = xmlGetProp (node, (const xmlChar *) "fullname");
- auth_token->fullname = g_strdup ((gchar *) value);
- xmlFree (value);
- }
- }
-
- if (!auth_token->token)
- {
- /* If we don't get enough information, return NULL */
- g_object_unref (auth_token);
- auth_token = NULL;
-
- err = g_error_new (FSP_ERROR, FSP_ERROR_MISSING_DATA,
- "No token found in the response");
- }
- }
- else
- err = g_error_new (FSP_ERROR, FSP_ERROR_MISSING_DATA,
- "No 'auth' node found in the response");
- /* Free */
- xmlXPathFreeObject (xpathObj);
- xmlXPathFreeContext (xpathCtx);
-
- /* Propagate error */
- if (err != NULL)
- g_propagate_error (error, err);
-
- return auth_token;
-}
-
-static gpointer
_get_upload_status_parser (xmlDoc *doc,
GError **error)
{
@@ -1116,9 +1029,8 @@ fsp_parser_get_request_token (FspParser *self,
if (!auth_token->token || !auth_token->token_secret)
{
GError *err = NULL;
- err = g_error_new (FSP_ERROR, FSP_ERROR_REQUEST_TOKEN,
- "An error happened requesting a new token");
-
+ err = g_error_new (FSP_ERROR, FSP_ERROR_OAUTH_UNKNOWN_ERROR,
+ "An unknown error happened requesting a new token");
/* Propagate error */
if (err != NULL)
g_propagate_error (error, err);
@@ -1129,20 +1041,55 @@ fsp_parser_get_request_token (FspParser *self,
}
FspDataAuthToken *
-fsp_parser_get_auth_token (FspParser *self,
- const gchar *buffer,
- gulong buf_size,
- GError **error)
+fsp_parser_get_access_token (FspParser *self,
+ const gchar *buffer,
+ gulong buf_size,
+ GError **error)
{
FspDataAuthToken *auth_token = NULL;
+ gchar *response_str = NULL;
+ gchar **response_array = NULL;
+ gint i = 0;
g_return_val_if_fail (FSP_IS_PARSER (self), NULL);
g_return_val_if_fail (buffer != NULL, NULL);
/* Process the response */
- auth_token =
- FSP_DATA_AUTH_TOKEN (_process_xml_response (self, buffer, buf_size,
- _get_auth_token_parser, error));
+ auth_token = FSP_DATA_AUTH_TOKEN (fsp_data_new (FSP_AUTH_TOKEN));
+ response_str = g_strndup (buffer, buf_size);
+ response_array = g_strsplit (response_str, "&", -1);
+ g_free (response_str);
+
+ for (i = 0; response_array[i]; i++)
+ {
+ if (g_str_has_prefix (response_array[i], "fullname="))
+ auth_token->fullname = soup_uri_decode (&response_array[i][9]);
+
+ if (g_str_has_prefix (response_array[i], "username="))
+ auth_token->username = soup_uri_decode (&response_array[i][9]);
+
+ if (g_str_has_prefix (response_array[i], "user_nsid="))
+ auth_token->nsid = soup_uri_decode (&response_array[i][10]);
+
+ if (g_str_has_prefix (response_array[i], "oauth_token="))
+ auth_token->token = g_strdup (&response_array[i][12]);
+
+ if (g_str_has_prefix (response_array[i], "oauth_token_secret="))
+ auth_token->token_secret = g_strdup (&response_array[i][19]);
+ }
+ g_strfreev (response_array);
+
+ /* Create the GError if needed*/
+ if (!auth_token->token || !auth_token->token_secret)
+ {
+ GError *err = NULL;
+ err = g_error_new (FSP_ERROR, FSP_ERROR_OAUTH_UNKNOWN_ERROR,
+ "An unknown error happened getting a permanent token");
+
+ /* Propagate error */
+ if (err != NULL)
+ g_propagate_error (error, err);
+ }
/* Return value */
return auth_token;
diff --git a/src/flicksoup/fsp-parser.h b/src/flicksoup/fsp-parser.h
index 5d5b0e3..03bd06b 100644
--- a/src/flicksoup/fsp-parser.h
+++ b/src/flicksoup/fsp-parser.h
@@ -74,7 +74,7 @@ fsp_parser_get_request_token (FspParser *self,
GError **error);
FspDataAuthToken *
-fsp_parser_get_auth_token (FspParser *self,
+fsp_parser_get_access_token (FspParser *self,
const gchar *buffer,
gulong buf_size,
GError **error);
diff --git a/src/flicksoup/fsp-session.c b/src/flicksoup/fsp-session.c
index e6bbc66..923dc01 100644
--- a/src/flicksoup/fsp-session.c
+++ b/src/flicksoup/fsp-session.c
@@ -136,7 +136,7 @@ _get_request_token_session_cb (SoupSession *session,
SoupMessage *msg,
gpointer data);
static void
-_get_auth_token_soup_session_cb (SoupSession *session,
+_get_access_token_soup_session_cb (SoupSession *session,
SoupMessage *msg,
gpointer data);
static void
@@ -234,6 +234,9 @@ _get_signed_url (FspSession *self,
... );
static void
+_clear_token_information (FspSession *self);
+
+static void
_perform_async_request (SoupSession *soup_session,
const gchar *url,
SoupSessionCallback request_cb,
@@ -491,7 +494,7 @@ _get_request_token_session_cb (SoupSession *session,
}
static void
-_get_auth_token_soup_session_cb (SoupSession *session,
+_get_access_token_soup_session_cb (SoupSession *session,
SoupMessage *msg,
gpointer data)
{
@@ -500,7 +503,7 @@ _get_auth_token_soup_session_cb (SoupSession *session,
/* Handle message with the right parser */
_handle_soup_response (msg,
- (FspParserFunc) fsp_parser_get_auth_token,
+ (FspParserFunc) fsp_parser_get_access_token,
data);
}
@@ -919,12 +922,27 @@ _check_errors_on_soup_response (SoupMessage *msg,
GError **error)
{
GError *err = NULL;
+ gchar *response_str = NULL;
g_assert (SOUP_IS_MESSAGE (msg));
- /* Check non-succesful SoupMessage's only */
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ /* First check it's not an OAuth problem */
+ response_str = g_strndup (msg->response_body->data, msg->response_body->length);
+ if (g_str_has_prefix (response_str, "oauth_problem="))
{
+ if (!g_strcmp0 (&response_str[14], "token_rejected"))
+ err = g_error_new (FSP_ERROR, FSP_ERROR_OAUTH_NOT_AUTHORIZED_YET,
+ "[OAuth]: The access token has been rejected");
+ else if (!g_strcmp0 (&response_str[14], "verifier_invalid"))
+ err = g_error_new (FSP_ERROR, FSP_ERROR_OAUTH_VERIFIER_INVALID,
+ "[OAuth] The verification code is invalid");
+ else
+ err = g_error_new (FSP_ERROR, FSP_ERROR_OAUTH_UNKNOWN_ERROR,
+ "[OAuth] unknown error");
+ }
+ else if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ {
+ /* Check non-succesful SoupMessage's only */
if (msg->status_code == SOUP_STATUS_CANCELLED)
err = g_error_new (FSP_ERROR, FSP_ERROR_CANCELLED,
"Cancelled by user");
@@ -939,6 +957,8 @@ _check_errors_on_soup_response (SoupMessage *msg,
"Network error");
}
+ g_free (response_str);
+
/* Propagate error */
if (err != NULL)
g_propagate_error (error, err);
@@ -1145,6 +1165,27 @@ _get_signed_url (FspSession *self,
}
static void
+_clear_token_information (FspSession *self)
+{
+ FspSessionPrivate *priv = NULL;
+
+ g_return_if_fail (FSP_IS_SESSION (self));
+
+ priv = self->priv;
+ if (priv->token)
+ {
+ g_free (priv->token);
+ priv->token = NULL;
+ }
+
+ if (priv->token_secret)
+ {
+ g_free (priv->token_secret);
+ priv->token_secret = NULL;
+ }
+}
+
+static void
_perform_async_request (SoupSession *soup_session,
const gchar *url,
SoupSessionCallback request_cb,
@@ -1653,20 +1694,12 @@ fsp_session_get_auth_url_async (FspSession *self,
GAsyncReadyCallback cb,
gpointer data)
{
- FspSessionPrivate *priv = NULL;
gchar *url = NULL;
g_return_if_fail (FSP_IS_SESSION (self));
- priv = self->priv;
-
- /* We need to make sure that any token is removed at this point to
- avoid calculating the signature wrongly, and also because they
- will get replaced anyway by the new token. */
- g_free (priv->token);
- priv->token = NULL;
- g_free (priv->token_secret);
- priv->token_secret = NULL;
+ /* If we're here, we're no longer interested in former tokens */
+ _clear_token_information (self);
/* Build the signed url */
url = _get_signed_url (self,
@@ -1676,7 +1709,7 @@ fsp_session_get_auth_url_async (FspSession *self,
NULL);
/* Perform the async request */
- _perform_async_request (priv->soup_session, url,
+ _perform_async_request (self->priv->soup_session, url,
_get_request_token_session_cb, G_OBJECT (self),
c, cb, fsp_session_get_auth_url_async, data);
@@ -1726,6 +1759,7 @@ fsp_session_get_auth_url_finish (FspSession *self,
void
fsp_session_complete_auth_async (FspSession *self,
+ const gchar *code,
GCancellable *c,
GAsyncReadyCallback cb,
gpointer data)
@@ -1742,15 +1776,15 @@ fsp_session_complete_auth_async (FspSession *self,
/* Build the signed url */
url = _get_signed_url (self,
- FLICKR_API_BASE_URL,
- AUTHORIZATION_METHOD_ORIGINAL,
- "method", "flickr.auth.getToken",
- "api_key", priv->api_key,
+ FLICKR_ACCESS_TOKEN_OAUTH_URL,
+ AUTHORIZATION_METHOD_OAUTH_1,
+ "oauth_token", priv->token,
+ "oauth_verifier", code,
NULL);
/* Perform the async request */
_perform_async_request (priv->soup_session, url,
- _get_auth_token_soup_session_cb, G_OBJECT (self),
+ _get_access_token_soup_session_cb, G_OBJECT (self),
c, cb, fsp_session_complete_auth_async, data);
g_free (url);
@@ -1760,9 +1794,11 @@ fsp_session_complete_auth_async (FspSession *self,
GError *err = NULL;
/* Build and report error */
- err = g_error_new (FSP_ERROR, FSP_ERROR_ACCESS_TOKEN, "No access token got");
+ err = g_error_new (FSP_ERROR, FSP_ERROR_OAUTH_NOT_AUTHORIZED_YET, "Not authorized yet");
g_simple_async_report_gerror_in_idle (G_OBJECT (self),
cb, data, err);
+ /* Ensure we clean things up */
+ _clear_token_information (self);
}
}
@@ -1815,7 +1851,7 @@ fsp_session_check_auth_info_async (FspSession *self,
/* Perform the async request */
_perform_async_request (priv->soup_session, url,
- _get_auth_token_soup_session_cb, G_OBJECT (self),
+ _get_access_token_soup_session_cb, G_OBJECT (self),
c, cb, fsp_session_check_auth_info_async, data);
g_free (url);
diff --git a/src/flicksoup/fsp-session.h b/src/flicksoup/fsp-session.h
index eb7690f..1e8ba06 100644
--- a/src/flicksoup/fsp-session.h
+++ b/src/flicksoup/fsp-session.h
@@ -101,6 +101,7 @@ fsp_session_get_auth_url_finish (FspSession *self,
void
fsp_session_complete_auth_async (FspSession *self,
+ const gchar *code,
GCancellable *c,
GAsyncReadyCallback cb,
gpointer data);
diff --git a/src/frogr-auth-dialog.c b/src/frogr-auth-dialog.c
index 31673e0..c369236 100644
--- a/src/frogr-auth-dialog.c
+++ b/src/frogr-auth-dialog.c
@@ -100,11 +100,12 @@ _code_entry_text_inserted_cb (GtkEditable *editable, gchar *new_text,
}
static GtkWidget *
-_build_verification_code_entry_widget ()
+_build_verification_code_entry_widget (GtkWidget *dialog)
{
GtkWidget *hbox = NULL;
GtkWidget *entry = NULL;
GtkWidget *separator = NULL;
+ gchar *entry_key = NULL;
gint i = 0;
hbox = frogr_gtk_compat_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
@@ -115,10 +116,13 @@ _build_verification_code_entry_widget ()
gtk_entry_set_width_chars (GTK_ENTRY (entry), 3);
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 6);
+ entry_key = g_strdup_printf ("vercode-%d", i + 1);
+ g_object_set_data (G_OBJECT (dialog), entry_key, entry);
+ g_free (entry_key);
+
g_signal_connect (G_OBJECT (entry), "insert-text",
G_CALLBACK (_code_entry_text_inserted_cb),
NULL);
-
if (i < 2)
{
separator = frogr_gtk_compat_separator_new (GTK_ORIENTATION_HORIZONTAL);
@@ -155,7 +159,7 @@ _ask_for_auth_confirmation (GtkWindow *parent)
vbox = frogr_gtk_compat_box_new (GTK_ORIENTATION_VERTICAL, 0);
/* Entry widgets for the verification code */
- ver_code_entry = _build_verification_code_entry_widget ();
+ ver_code_entry = _build_verification_code_entry_widget (dialog);
gtk_box_pack_start (GTK_BOX (vbox), ver_code_entry, TRUE, TRUE, 0);
/* Description label */
@@ -171,13 +175,34 @@ _ask_for_auth_confirmation (GtkWindow *parent)
gtk_widget_show_all (dialog);
}
+static const gchar*
+_get_entry_code_for_dialog (GtkDialog *dialog, const gchar *entry_key)
+{
+ GtkWidget *entry = g_object_get_data (G_OBJECT (dialog), entry_key);
+ if (entry == NULL)
+ return NULL;
+
+ return gtk_entry_get_text (GTK_ENTRY (entry));
+}
+
static void
_ask_for_auth_confirmation_response_cb (GtkDialog *dialog, gint response, gpointer data)
{
if (response == GTK_RESPONSE_OK)
{
- FrogrController *controller = frogr_controller_get_instance();
- frogr_controller_complete_auth (controller);
+ const gchar *vercode_part1 = NULL;
+ const gchar *vercode_part2 = NULL;
+ const gchar *vercode_part3 = NULL;
+ gchar *vercode_full = NULL;
+
+ vercode_part1 = _get_entry_code_for_dialog (dialog, "vercode-1");
+ vercode_part2 = _get_entry_code_for_dialog (dialog, "vercode-2");
+ vercode_part3 = _get_entry_code_for_dialog (dialog, "vercode-3");
+
+ vercode_full = g_strdup_printf ("%s-%s-%s", vercode_part1, vercode_part2, vercode_part3);
+
+ frogr_controller_complete_auth (frogr_controller_get_instance(), vercode_full);
+ g_free (vercode_full);
}
gtk_widget_destroy (GTK_WIDGET (dialog));
diff --git a/src/frogr-controller.c b/src/frogr-controller.c
index 64a3ad1..117b986 100644
--- a/src/frogr-controller.c
+++ b/src/frogr-controller.c
@@ -357,9 +357,19 @@ _notify_error_to_user (FrogrController *self, GError *error)
error_function = frogr_util_show_error_dialog;
break;
- case FSP_ERROR_REQUEST_TOKEN:
- case FSP_ERROR_ACCESS_TOKEN:
- msg = g_strdup_printf (_("Unable to authenticate in flickr"));
+ case FSP_ERROR_OAUTH_UNKNOWN_ERROR:
+ msg = g_strdup_printf (_("Unable to authenticate in flickr\nPlease try again."));
+ error_function = frogr_util_show_error_dialog;
+ break;
+
+ case FSP_ERROR_OAUTH_NOT_AUTHORIZED_YET:
+ msg = g_strdup_printf (_("You have not properly authorized %s yet.\n"
+ "Please try again."), APP_SHORTNAME);
+ error_function = frogr_util_show_error_dialog;
+ break;
+
+ case FSP_ERROR_OAUTH_VERIFIER_INVALID:
+ msg = g_strdup_printf (_("Invalid verification code.\nPlease try again."));
error_function = frogr_util_show_error_dialog;
break;
@@ -2507,7 +2517,7 @@ frogr_controller_open_auth_url (FrogrController *self)
}
void
-frogr_controller_complete_auth (FrogrController *self)
+frogr_controller_complete_auth (FrogrController *self, const gchar *verification_code)
{
FrogrControllerPrivate *priv = FROGR_CONTROLLER_GET_PRIVATE (self);
@@ -2516,7 +2526,7 @@ frogr_controller_complete_auth (FrogrController *self)
priv->fetching_auth_token = TRUE;
_enable_cancellable (self, TRUE);
- fsp_session_complete_auth_async (priv->session, priv->last_cancellable, _complete_auth_cb, self);
+ fsp_session_complete_auth_async (priv->session, verification_code, priv->last_cancellable, _complete_auth_cb, self);
gdk_threads_add_timeout (DEFAULT_TIMEOUT, (GSourceFunc) _show_progress_on_idle, GINT_TO_POINTER (FETCHING_AUTH_TOKEN));
/* Make sure we show proper feedback if connection is too slow */
diff --git a/src/frogr-controller.h b/src/frogr-controller.h
index c831afc..b71e27a 100644
--- a/src/frogr-controller.h
+++ b/src/frogr-controller.h
@@ -108,7 +108,7 @@ void frogr_controller_show_add_to_group_dialog (FrogrController *self,
void frogr_controller_open_auth_url (FrogrController *self);
-void frogr_controller_complete_auth (FrogrController *self);
+void frogr_controller_complete_auth (FrogrController *self, const gchar *verification_code);
gboolean frogr_controller_is_authorized (FrogrController *self);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]