X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=blobdiff_plain;f=src%2Fprefs_account.c;h=0f94467460c7abf8296897d254ad5d513ff1dc61;hp=7e84ec7a365f54840aa3ad47c9d7d5f581ad210c;hb=e3a613af5ac6ed643b53f3b95f48cb4cb0b5c4e0;hpb=c23d02b23105ab05d0b0e1a037da7596e6c61777 diff --git a/src/prefs_account.c b/src/prefs_account.c index 7e84ec7a3..0f9446746 100644 --- a/src/prefs_account.c +++ b/src/prefs_account.c @@ -68,6 +68,7 @@ #ifdef USE_GNUTLS #include #endif +#include static gboolean cancelled; static gboolean new_account; @@ -95,6 +96,9 @@ struct AutocheckWidgets { static GSList *prefs_pages = NULL; +static pthread_t oauth2_listener_tid; +static int oauth2_listener_cancel = 0; + typedef struct BasicPage { PrefsPage page; @@ -422,6 +426,8 @@ static void prefs_account_oauth2_provider_set_data_from_optmenu static void prefs_account_oauth2_provider_set_optmenu (PrefParam *pparam); static void prefs_account_oauth2_copy_url (GtkButton *button, gpointer data); +static void * prefs_account_oauth2_listener(void *params); +static int prefs_account_oauth2_get_line(int sock, char *buf, int size); static void prefs_account_oauth2_set_sensitivity(void); static void prefs_account_oauth2_set_auth_sensitivity(void); static void prefs_account_oauth2_obtain_tokens(GtkButton *button, gpointer data); @@ -3776,6 +3782,13 @@ static void send_destroy_widget_func(PrefsPage *_page) static void oauth2_destroy_widget_func(PrefsPage *_page) { /* Oauth2Page *page = (Oauth2Page *) _page; */ + + if(oauth2_listener_tid){ + debug_print("Closing oauth2 listener thread\n"); + oauth2_listener_cancel = 1; + pthread_join(oauth2_listener_tid, NULL); + oauth2_listener_tid = (pthread_t)NULL; + } } static void compose_destroy_widget_func(PrefsPage *_page) @@ -4105,6 +4118,8 @@ static void register_oauth2_page(void) oauth2_page.page.save_page = oauth2_save_func; oauth2_page.page.can_close = oauth2_can_close_func; + oauth2_listener_tid = (pthread_t)NULL; + prefs_account_register_page((PrefsPage *) &oauth2_page); } @@ -5113,6 +5128,22 @@ static void prefs_account_oauth2_copy_url(GtkButton *button, gpointer data) open_uri(url, prefs_common_get_uri_cmd()); g_free(url); + + //Start listener for authorisation reply + //Needs to be in a separate thread to avoid hanging while we wait, and to allow cancellation of the process + //Avoid starting multiple threads if someone clicks this button more than once. + + //if thead exists cancel it here + if(oauth2_listener_tid){ + debug_print("Cancelling old oauth2 listener thread\n"); + oauth2_listener_cancel = 1; + pthread_join(oauth2_listener_tid, NULL); + oauth2_listener_tid = (pthread_t)NULL; + } + debug_print("Starting oauth2 listener thread\n"); + oauth2_listener_cancel = 0; + pthread_create(&oauth2_listener_tid, NULL, prefs_account_oauth2_listener, (void*)win); + } static void prefs_account_oauth2_obtain_tokens(GtkButton *button, gpointer data) @@ -5977,3 +6008,186 @@ static void prefs_account_receive_itv_spinbutton_value_changed_cb(GtkWidget *w, PREFS_RECV_AUTOCHECK_MIN_INTERVAL); } } + +//Automation of the oauth2 authorisation process to receive loopback callback generated by redirect in browser +static void * prefs_account_oauth2_listener(void * param) +{ + int socket_desc, client_sock, c; + struct sockaddr_in server , client; + char client_message[2000]; + char reply[600]; + char reply_message[400]; + gchar *trim_text = NULL; + fd_set rfds; + gint ret; + struct timeval timeout; + struct BasicProtocol *protocol_optmenu = (struct BasicProtocol *)oauth2_page.protocol_optmenu; + GtkWidget *optmenu = protocol_optmenu->combobox; + Oauth2Service service; + OAUTH2Data *OAUTH2Data = g_malloc(sizeof(* OAUTH2Data)); + + + //pthread_detach(pthread_self()); + debug_print("oauth2 listener thread running\n"); + + //Create socket + socket_desc = socket(AF_INET , SOCK_STREAM , 0); + if (socket_desc == -1) + { + debug_print("oauth2 listener could not create socket\n"); + return NULL; + } + debug_print("oauth2 listener socket created\n"); + + //Prepare the sockaddr_in structure + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons( 8888 ); + + //Bind + if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) + { + debug_print("oauth2 listener bind failed\n"); + return NULL; + } + debug_print("oauth2 listener bind done\n"); + + listen(socket_desc , 1); + + //Accept and incoming connection + debug_print("oauth2 listener waiting for incoming connections...\n"); + c = sizeof(struct sockaddr_in); + + do{ + FD_ZERO(&rfds); + FD_SET(socket_desc, &rfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + select(socket_desc+1, &rfds, NULL, NULL, &timeout); + + //select woke up, maybe accept connection from an incoming client + if(FD_ISSET(socket_desc, &rfds)){ + + client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); + if (client_sock < 0){ + debug_print("oauth2 listener accept failed\n"); + return NULL; + } + debug_print("oauth2 listener connection accepted\n"); + + //Receive message sent to the loopback address by the authorisation page + prefs_account_oauth2_get_line(client_sock, client_message, sizeof(client_message)); + trim_text = g_strdup(client_message); + g_strstrip(trim_text); + + gtk_entry_set_text(GTK_ENTRY(oauth2_page.oauth2_authcode_entry), trim_text != NULL ? trim_text : ""); + gtk_widget_set_sensitive(oauth2_page.oauth2_authcode_entry, FALSE); + gtk_widget_set_sensitive(oauth2_page.oauth2_authorise_btn, FALSE); + + oauth2_init (OAUTH2Data); + + OAUTH2Data->custom_client_secret = + g_strdup(gtk_entry_get_text((GtkEntry *)oauth2_page.oauth2_client_secret_entry)); + OAUTH2Data->custom_client_id = + g_strdup(gtk_entry_get_text((GtkEntry *)oauth2_page.oauth2_client_id_entry)); + + service = combobox_get_active_data(GTK_COMBO_BOX(optmenu)); + ret = oauth2_obtain_tokens (service, OAUTH2Data, trim_text); + + if(!ret){ + if(OAUTH2Data->refresh_token != NULL){ + passwd_store_set_account(tmp_ac_prefs.account_id, + PWS_ACCOUNT_OAUTH2_REFRESH, + OAUTH2Data->refresh_token, + FALSE); + log_message(LOG_PROTOCOL, "OAuth2 refresh token stored\n"); + } + + if(OAUTH2Data->access_token != NULL){ + passwd_store_set_account(tmp_ac_prefs.account_id, + PWS_ACCOUNT_RECV, + OAUTH2Data->access_token, + FALSE); + + passwd_store_set_account(tmp_ac_prefs.account_id, + PWS_ACCOUNT_SEND, + OAUTH2Data->access_token, + FALSE); + log_message(LOG_PROTOCOL, "OAuth2 access token stored\n"); + + gtk_entry_set_text(GTK_ENTRY(basic_page.pass_entry), OAUTH2Data->access_token); + gtk_entry_set_text(GTK_ENTRY(send_page.smtp_pass_entry), OAUTH2Data->access_token); + } + + if(OAUTH2Data->expiry_str != NULL){ + passwd_store_set_account(tmp_ac_prefs.account_id, + PWS_ACCOUNT_OAUTH2_EXPIRY, + OAUTH2Data->expiry_str, + FALSE); + log_message(LOG_PROTOCOL, "OAuth2 access token expiry stored\n"); + } + + tmp_ac_prefs.oauth2_date = g_get_real_time () / G_USEC_PER_SEC; + + sprintf(reply_message, "

Authorisation complete

Your oauth2 authorisation code has been received by Claws Mail

"); + }else{ + //Something went wrong + log_message(LOG_PROTOCOL, "oauth2 authorisation code not received\n"); + sprintf(reply_message, "

Authorisation NOT completed

Your authorisation code was not received by Claws Mail

"); + } + + sprintf(reply, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: %lu\r\n\r\n%s", strlen(reply_message), reply_message); + write(client_sock, reply, strlen(reply)); + close(client_sock); + } + + }while(ret && !oauth2_listener_cancel); + + close(socket_desc); + g_free(trim_text); + g_free(OAUTH2Data); + + //To Do + //(3) Work out how to give an indicataion in the main window that authentication has happened + //(6) Clean up, test, copy over to git version + //(1) Work out why extracting code from this causes segfault in oauth2_get_token_from_response + //http://127.0.0.1:8888/?code=M.R3_BAY.09a02423-7bf8-8e51-c4a3-5565f8e7d56b HTTP/1.1 + return NULL; +} + + + + + +static int prefs_account_oauth2_get_line(int sock, char *buf, int size) +{ + int i = 0; + char c = '\0'; + int n; + + while ((i < size - 1) && (c != '\n')) + { + n = recv(sock, &c, 1, 0); + //printf("%02X\n", c); + if (n > 0) + { + if (c == '\r') + { + n = recv(sock, &c, 1, MSG_PEEK); + //printf("%02X\n", c); + if ((n > 0) && (c == '\n')) + recv(sock, &c, 1, 0); + else + c = '\n'; + } + buf[i] = c; + i++; + } + else + c = '\n'; + } + buf[i] = '\0'; + + return(i); +}