[Commits] [SCM] claws branch, oauth2, created. 3.17.8-25-gd0f3df42c

miras at claws-mail.org miras at claws-mail.org
Thu Feb 25 23:44:57 CET 2021


The branch, oauth2 has been created
        at  d0f3df42c68d9f68576666b61f746c891ff407af (commit)

- Log -----------------------------------------------------------------
commit d0f3df42c68d9f68576666b61f746c891ff407af
Author: Michael Rasmussen <mir at datanom.net>
Date:   Thu Feb 25 23:42:58 2021 +0100

    Add oauth2 version 5
    
    Signed-off-by: Michael Rasmussen <mir at datanom.net>

diff --git a/AUTHORS b/AUTHORS
index 7d9f066f3..21b050d27 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -333,3 +333,4 @@ contributors (in addition to the above; based on Changelog)
 	Jean Delvare
 	Damian Poddebniak
 	Alvar Penning
+    David Fletcher
diff --git a/src/Makefile.am b/src/Makefile.am
index d7929b259..de7efd86f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -178,6 +178,7 @@ claws_mail_SOURCES = \
 	news.c \
 	news_gtk.c \
 	noticeview.c \
+	oauth2.c \
 	partial_download.c \
 	password.c \
 	password_gtk.c \
@@ -297,6 +298,7 @@ claws_mailinclude_HEADERS = \
 	news.h \
 	news_gtk.h \
 	noticeview.h \
+	oauth2.h \
 	partial_download.h \
 	password.h \
 	password_gtk.h \
diff --git a/src/account.c b/src/account.c
index a8d8375f6..734b97901 100644
--- a/src/account.c
+++ b/src/account.c
@@ -953,7 +953,8 @@ static void account_clone(GtkWidget *widget, gpointer data)
 	ACP_FASSIGN(out_ssl_client_cert_pass);
 	
 	/* receive */
-	ACP_FASSIGN(use_apop_auth);
+	ACP_FASSIGN(use_pop_auth);
+    ACP_FASSIGN(pop_auth_type);
 	ACP_FASSIGN(rmmail);
 	ACP_FASSIGN(msg_leave_time);
 	ACP_FASSIGN(msg_leave_hour);
diff --git a/src/common/smtp.c b/src/common/smtp.c
index 2460f7e04..b0d91c0cc 100644
--- a/src/common/smtp.c
+++ b/src/common/smtp.c
@@ -46,6 +46,7 @@ static gint smtp_starttls(SMTPSession *session);
 static gint smtp_auth_cram_md5(SMTPSession *session);
 static gint smtp_auth_login(SMTPSession *session);
 static gint smtp_auth_plain(SMTPSession *session);
+static gint smtp_auth_oauth2(SMTPSession *session);
 
 static gint smtp_ehlo(SMTPSession *session);
 static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg);
@@ -175,6 +176,11 @@ static gint smtp_auth(SMTPSession *session)
                  &&
 		  (session->avail_auth_type & SMTPAUTH_PLAIN) != 0)
 		smtp_auth_plain(session);
+	else if ((session->forced_auth_type == SMTPAUTH_OAUTH2
+		  || session->forced_auth_type == 0)
+                 &&
+		  (session->avail_auth_type & SMTPAUTH_OAUTH2) != 0)
+		smtp_auth_oauth2(session);
 	else if (session->forced_auth_type == 0) {
 		log_warning(LOG_PROTOCOL, _("No SMTP AUTH method available\n"));
 		return SM_AUTHFAIL;
@@ -317,6 +323,8 @@ static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg)
 				session->avail_auth_type |= SMTPAUTH_CRAM_MD5;
 			if (strcasestr(p, "DIGEST-MD5"))
 				session->avail_auth_type |= SMTPAUTH_DIGEST_MD5;
+			if (strcasestr(p, "XOAUTH2"))
+				session->avail_auth_type |= SMTPAUTH_OAUTH2;
 		}
 		if (g_ascii_strncasecmp(p, "SIZE", 4) == 0) {
 			p += 5;
@@ -391,6 +399,36 @@ static gint smtp_auth_plain(SMTPSession *session)
 	return SM_OK;
 }
 
+
+static gint smtp_auth_oauth2(SMTPSession *session)
+{
+	gchar buf[MESSAGEBUFSIZE], *b64buf, *out;
+	gint len;
+
+	session->state = SMTP_AUTH_OAUTH2;
+	session->auth_type = SMTPAUTH_OAUTH2;
+
+	memset(buf, 0, sizeof buf);
+
+	/* "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"*/
+        /* session->pass contains the OAUTH2 Access Token*/
+	len = sprintf(buf, "user=%s\1auth=Bearer %s\1\1", session->user, session->pass);
+	b64buf = g_base64_encode(buf, len);
+	out = g_strconcat("AUTH XOAUTH2 ", b64buf, NULL);
+	g_free(b64buf);
+
+	if (session_send_msg(SESSION(session), out) < 0) {
+		g_free(out);
+		return SM_ERROR;
+	}
+
+	g_free(out);
+
+	log_print(LOG_PROTOCOL, "ESMTP> [AUTH XOAUTH2]\n");
+
+	return SM_OK;
+}
+
 static gint smtp_auth_login(SMTPSession *session)
 {
 	session->state = SMTP_AUTH;
@@ -509,6 +547,7 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
 	case SMTP_AUTH_PLAIN:
 	case SMTP_AUTH_LOGIN_USER:
 	case SMTP_AUTH_LOGIN_PASS:
+        case SMTP_AUTH_OAUTH2:
 	case SMTP_AUTH_CRAM_MD5:
 		log_print(LOG_PROTOCOL, "ESMTP< %s\n", msg);
 		break;
@@ -631,6 +670,7 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
 		break;
 	case SMTP_AUTH_PLAIN:
 	case SMTP_AUTH_LOGIN_PASS:
+        case SMTP_AUTH_OAUTH2:
 	case SMTP_AUTH_CRAM_MD5:
 		ret = smtp_from(smtp_session);
 		break;
diff --git a/src/common/smtp.h b/src/common/smtp.h
index 706a0978a..f20bf711c 100644
--- a/src/common/smtp.h
+++ b/src/common/smtp.h
@@ -55,7 +55,8 @@ typedef enum
 	SMTPAUTH_CRAM_MD5   = 1 << 1,
 	SMTPAUTH_DIGEST_MD5 = 1 << 2,
 	SMTPAUTH_TLS_AVAILABLE = 1 << 3,
-	SMTPAUTH_PLAIN      = 1 << 4
+	SMTPAUTH_PLAIN      = 1 << 4,
+	SMTPAUTH_OAUTH2     = 1 << 5
 } SMTPAuthType;
 
 typedef enum
@@ -70,6 +71,7 @@ typedef enum
 	SMTP_AUTH_LOGIN_PASS,
 	SMTP_AUTH_CRAM_MD5,
 	SMTP_AUTH_PLAIN,
+	SMTP_AUTH_OAUTH2,
 	SMTP_RCPT,
 	SMTP_DATA,
 	SMTP_SEND_DATA,
diff --git a/src/etpan/imap-thread.c b/src/etpan/imap-thread.c
index eeccac793..6586f26bf 100644
--- a/src/etpan/imap-thread.c
+++ b/src/etpan/imap-thread.c
@@ -999,7 +999,9 @@ static void login_run(struct etpan_thread_op * op)
 			param->type, NULL, NULL, NULL,
 			NULL, param->login,
 			param->password, NULL);
-	else
+	else if (!strcmp(param->type, "XOAUTH2")) {
+                r = mailimap_oauth2_authenticate(param->imap, param->login, param->password);
+	} else
 		r = mailimap_authenticate(param->imap,
 			param->type, NULL, NULL, NULL,
 			param->login, param->login,
diff --git a/src/imap.c b/src/imap.c
index 43388a5f7..dd0ab1deb 100644
--- a/src/imap.c
+++ b/src/imap.c
@@ -72,6 +72,7 @@
 #include "main.h"
 #include "passwordstore.h"
 #include "file-utils.h"
+#include "oauth2.h"
 
 typedef struct _IMAPFolder	IMAPFolder;
 typedef struct _IMAPSession	IMAPSession;
@@ -912,6 +913,9 @@ static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass
 	case IMAP_AUTH_PLAIN:
 		ok = imap_cmd_login(session, user, pass, "PLAIN");
 		break;
+	case IMAP_AUTH_OAUTH2:
+		ok = imap_cmd_login(session, user, pass, "XOAUTH2");
+		break;
 	case IMAP_AUTH_LOGIN:
 		ok = imap_cmd_login(session, user, pass, "LOGIN");
 		break;
@@ -928,6 +932,7 @@ static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass
 				"\t DIGEST-MD5 %d\n"
 				"\t SCRAM-SHA-1 %d\n"
 				"\t PLAIN %d\n"
+				"\t OAUTH2 %d\n"
 				"\t LOGIN %d\n"
 				"\t GSSAPI %d\n", 
 			imap_has_capability(session, "ANONYMOUS"),
@@ -935,6 +940,7 @@ static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass
 			imap_has_capability(session, "DIGEST-MD5"),
 			imap_has_capability(session, "SCRAM-SHA-1"),
 			imap_has_capability(session, "PLAIN"),
+			imap_has_capability(session, "XOAUTH2"),
 			imap_has_capability(session, "LOGIN"),
 			imap_has_capability(session, "GSSAPI"));
 		if (imap_has_capability(session, "CRAM-MD5"))
@@ -945,6 +951,8 @@ static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass
 			ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-1");
 		if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "PLAIN"))
 			ok = imap_cmd_login(session, user, pass, "PLAIN");
+		if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "XOAUTH2"))
+			ok = imap_cmd_login(session, user, pass, "XOAUTH2");
 		if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "LOGIN"))
 			ok = imap_cmd_login(session, user, pass, "LOGIN");
 		if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "GSSAPI"))
@@ -1308,6 +1316,9 @@ static gint imap_session_authenticate(IMAPSession *session,
 	gint ok = MAILIMAP_NO_ERROR;
 	g_return_val_if_fail(account->userid != NULL, MAILIMAP_ERROR_BAD_STATE);
 
+	if(account->imap_auth_type == IMAP_AUTH_OAUTH2)
+	        oauth2_check_passwds (account);
+	
 	if (!password_get(account->userid, account->recv_server, "imap",
 			 SESSION(session)->port, &acc_pass)) {
 		acc_pass = passwd_store_get_account(account->account_id,
diff --git a/src/imap.h b/src/imap.h
index 50569e0cf..f100dcb6b 100644
--- a/src/imap.h
+++ b/src/imap.h
@@ -31,7 +31,8 @@ typedef enum
 	IMAP_AUTH_DIGEST_MD5	= 1 << 4,
 	IMAP_AUTH_SCRAM_SHA1	= 1 << 5,
 	IMAP_AUTH_PLAIN   = 1 << 6,
-	IMAP_AUTH_LOGIN   = 1 << 7
+	IMAP_AUTH_LOGIN   = 1 << 7,
+	IMAP_AUTH_OAUTH2   = 1 << 8
 } IMAPAuthType;
 
 FolderClass *imap_get_class		(void);
diff --git a/src/inc.c b/src/inc.c
index 44355f353..e84cd4b4b 100644
--- a/src/inc.c
+++ b/src/inc.c
@@ -61,6 +61,7 @@
 #include "hooks.h"
 #include "logwindow.h"
 #include "passwordstore.h"
+#include "oauth2.h"
 
 extern SessionStats session_stats;
 
@@ -633,6 +634,10 @@ static gint inc_start(IncProgressDialog *inc_dialog)
 				(inc_dialog->dialog->window,
 				 NULL, NULL);
 
+		if(pop3_session->ac_prefs->use_pop_auth && 
+		   pop3_session->ac_prefs->pop_auth_type == POPAUTH_OAUTH2)
+		     oauth2_check_passwds (pop3_session->ac_prefs);
+		
 		if (password_get(pop3_session->user,
 					pop3_session->ac_prefs->recv_server,
 					"pop3", pop3_get_port(pop3_session),
diff --git a/src/oauth2.c b/src/oauth2.c
new file mode 100644
index 000000000..161adcb9e
--- /dev/null
+++ b/src/oauth2.c
@@ -0,0 +1,481 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2020 the Claws Mail team
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#ifdef ENABLE_NLS
+#include <glib/gi18n.h>
+#else
+#define _(a) (a)
+#define N_(a) (a)
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "oauth2.h"
+#include "md5.h"
+#include "utils.h"
+#include "log.h"
+#include "time.h"
+#include "common/passcrypt.h"
+
+static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body);
+static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token);
+static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry);
+static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response);
+
+
+static gint oauth2_post_request (gchar *buf, gchar *host, gchar *resource, gchar *header, gchar *body)
+{
+       gint len;
+  
+       len = strlen(body);
+       if (header[0])
+	 return snprintf(buf, OAUTH2BUFSIZE, "POST %s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept: text/html,application/json\r\nContent-Length: %i\r\nHost: %s\r\nConnection: close\r\nUser-Agent: ClawsMail\r\n%s\r\n\r\n%s", resource, len, host, header, body);
+       else
+	 return snprintf(buf, OAUTH2BUFSIZE, "POST %s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept: text/html,application/json\r\nContent-Length: %i\r\nHost: %s\r\nConnection: close\r\nUser-Agent: ClawsMail\r\n\r\n%s", resource, len, host, body);
+}
+
+static gint oauth2_filter_access (gchar *json, gchar *access_token, gint *expiry)
+{
+       GMatchInfo *matchInfo;
+       GRegex *regex;
+       
+       regex = g_regex_new ("\"access_token\": ?\"(.*?)\",?", 0, 0, NULL);
+       g_regex_match (regex, json, 0, &matchInfo);
+       if (g_match_info_matches (matchInfo)) 
+	 g_stpcpy (access_token,g_match_info_fetch (matchInfo, 1));
+       else{  
+	 g_match_info_free (matchInfo);
+	 return (-1);
+       }
+       
+       g_match_info_free (matchInfo);
+       
+       regex = g_regex_new ("\"expires_in\": ?([0-9]*),?", 0, 0, NULL);
+       g_regex_match (regex, json, 0, &matchInfo);
+       if (g_match_info_matches (matchInfo)){
+	 // Reduce available token life to avoid attempting connections with (near) expired tokens
+	 *expiry = (g_get_real_time () / G_USEC_PER_SEC) + atoi(g_match_info_fetch (matchInfo, 1)) - 120; 
+       }else{
+	 g_match_info_free (matchInfo);
+	 return (-2);
+       }
+       
+       g_match_info_free (matchInfo);
+       
+       return(0);
+}
+
+static gint oauth2_filter_refresh (gchar *json, gchar *refresh_token)
+{
+       GMatchInfo *matchInfo;
+       GRegex *regex;
+       
+       regex = g_regex_new ("\"refresh_token\": ?\"(.*?)\",?", 0, 0, NULL);
+       g_regex_match (regex, json, 0, &matchInfo);
+       if (g_match_info_matches (matchInfo)) 
+	 g_stpcpy (refresh_token,g_match_info_fetch (matchInfo, 1));
+       else{  
+	 g_match_info_free (matchInfo);
+	 return (-1);
+       }
+       
+       g_match_info_free (matchInfo);
+       
+       return(0);
+}
+
+gint oauth2_obtain_tokens (Oauth2Service provider, OAUTH2Data *OAUTH2Data, const gchar *authcode)
+{
+	gchar *request;
+	gchar *response;
+	gchar *body;
+	gchar *header;
+	gchar *tmp_hd;
+	gchar *access_token;
+	gchar *refresh_token;
+	gint expiry = 0;
+	gint ret;
+	SockInfo *sock;
+	gchar *client_id;
+	gchar *client_secret;
+
+	gint i;
+
+	i = (int)provider - 1;
+	if (i < 0 || i > (OAUTH2AUTH_LAST-1))
+	  return (1);
+
+	sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
+	if(sock == NULL || ssl_init_socket(sock) == FALSE){
+	  log_message(LOG_PROTOCOL, "OAUTH2 SSL connecion error\n");
+	  return (1);
+	}
+
+	sock_set_nonblocking_mode(sock, FALSE);
+	sock_set_io_timeout(10);
+	sock->gnutls_priority = "NORMAL:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1";
+
+	refresh_token = g_malloc(OAUTH2BUFSIZE+1);	
+	access_token = g_malloc(OAUTH2BUFSIZE+1);
+	request = g_malloc(OAUTH2BUFSIZE+1);
+	response = g_malloc(OAUTH2BUFSIZE+1);
+	body = g_malloc(OAUTH2BUFSIZE+1);
+
+	if(OAUTH2Data->custom_client_id)
+	  client_id = g_strdup(OAUTH2Data->custom_client_id);
+	else
+	  client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
+	
+	body = g_strconcat ("client_id=", g_uri_escape_string (client_id, NULL, FALSE), 
+			    "&code=",g_uri_escape_string (authcode, NULL, FALSE), NULL);
+
+	if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
+	  //Only allow custom client secret if the service provider would usually expect a client secret
+	  if(OAUTH2Data->custom_client_secret)
+	    client_secret = g_strdup(OAUTH2Data->custom_client_secret);
+	  else
+	    client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
+	  body = g_strconcat (body, "&client_secret=", g_uri_escape_string (client_secret, NULL, FALSE), NULL);
+	}else{
+	  client_secret = g_strconcat ("", NULL);
+	}
+
+	if(OAUTH2info[i][OA2_REDIRECT_URI][0])
+	  body = g_strconcat (body, "&redirect_uri=",g_uri_escape_string (OAUTH2info[i][OA2_REDIRECT_URI], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_GRANT_TYPE_ACCESS][0])
+	  body = g_strconcat (body, "&grant_type=", g_uri_escape_string (OAUTH2info[i][OA2_GRANT_TYPE_ACCESS], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_TENANT][0])
+	  body = g_strconcat (body, "&tenant=", g_uri_escape_string (OAUTH2info[i][OA2_TENANT], NULL, FALSE), NULL);	
+	if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0])
+	  body = g_strconcat (body, "&scope=", g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_STATE][0])
+	  body = g_strconcat (body, "&state=", g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE), NULL);
+
+	if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
+	  tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);	  
+	  header = g_strconcat ("Authorization: Basic ", g_base64_encode (tmp_hd, strlen(tmp_hd)), NULL);
+	  g_free(tmp_hd);
+	}else{
+	  header = g_strconcat ("", NULL);
+	}
+
+	oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_ACCESS_RESOURCE], header, body);
+	ret = oauth2_contact_server (sock, request, response);
+
+	if(oauth2_filter_access (response, access_token, &expiry) == 0){
+	  OAUTH2Data->access_token = access_token;
+	  OAUTH2Data->expiry = expiry;
+	  OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
+	  ret = 0;
+	  log_message(LOG_PROTOCOL, "OAUTH2 access token obtained\n");
+	}else{
+	  log_message(LOG_PROTOCOL, "OAUTH2 access token not obtained\n");
+	  debug_print("OAUTH2 - request: %s\n Response: %s", request, response);
+	  ret = 1;
+	}
+
+	if(oauth2_filter_refresh (response, refresh_token) == 0){
+	  OAUTH2Data->refresh_token = refresh_token;
+	  log_message(LOG_PROTOCOL, "OAUTH2 refresh token obtained\n");
+	}else{
+	  log_message(LOG_PROTOCOL, "OAUTH2 refresh token not obtained\n");
+	}
+
+	sock_close(sock, TRUE);
+	g_free(body);
+	g_free(header);
+	g_free(request);
+	g_free(response);
+	g_free(client_id);
+	g_free(client_secret);
+
+	return (ret);
+}
+
+gint oauth2_use_refresh_token (Oauth2Service provider, OAUTH2Data *OAUTH2Data)
+{
+
+	gchar *request;
+	gchar *response;
+	gchar *body;
+	gchar *header;
+	gchar *tmp_hd;
+	gchar *access_token;
+	gint expiry = 0;
+	gint ret;
+	SockInfo *sock;
+	gchar *client_id;
+	gchar *client_secret;
+
+	gint i;
+
+	i = (int)provider - 1;
+	if (i < 0 || i > (OAUTH2AUTH_LAST-1))
+	  return (1);
+
+	sock = sock_connect(OAUTH2info[i][OA2_BASE_URL], 443);
+	if(sock == NULL || ssl_init_socket(sock) == FALSE){
+	  log_message(LOG_PROTOCOL, "OAUTH2 SSL connecion error\n");
+	  return (1);
+	}
+
+	sock_set_nonblocking_mode(sock, FALSE);
+	sock_set_io_timeout(10);
+	sock->gnutls_priority = "NORMAL:!VERS-SSL3.0:!VERS-TLS1.0:!VERS-TLS1.1";
+
+	access_token = g_malloc(OAUTH2BUFSIZE+1);
+	request = g_malloc(OAUTH2BUFSIZE+1);
+	response = g_malloc(OAUTH2BUFSIZE+1);
+	body = g_malloc(OAUTH2BUFSIZE+1);
+
+	if(OAUTH2Data->custom_client_id)
+	  client_id = g_strdup(OAUTH2Data->custom_client_id);
+	else
+	  client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
+
+	body = g_strconcat ("client_id=", g_uri_escape_string (client_id, NULL, FALSE), 
+			    "&refresh_token=",OAUTH2Data->refresh_token, NULL); 
+
+	if(OAUTH2info[i][OA2_CLIENT_SECRET][0]){
+	  //Only allow custom client secret if the service provider would usually expect a client secret
+	  if(OAUTH2Data->custom_client_secret)
+	    client_secret = g_strdup(OAUTH2Data->custom_client_secret);
+	  else
+	    client_secret = oauth2_decode(OAUTH2info[i][OA2_CLIENT_SECRET]);
+	  body = g_strconcat (body, "&client_secret=", g_uri_escape_string (client_secret, NULL, FALSE), NULL);
+	}else{
+	  client_secret = g_strconcat ("", NULL);
+	}
+
+	if(OAUTH2info[i][OA2_GRANT_TYPE_REFRESH][0])
+	  body = g_strconcat (body, "&grant_type=", g_uri_escape_string (OAUTH2info[i][OA2_GRANT_TYPE_REFRESH], NULL, FALSE), NULL);	
+	if(OAUTH2info[i][OA2_SCOPE_FOR_ACCESS][0])
+	  body = g_strconcat (body, "&scope=", g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_ACCESS], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_STATE][0])
+	  body = g_strconcat (body, "&state=", g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE), NULL);
+
+	if(OAUTH2info[i][OA2_HEADER_AUTH_BASIC][0]){
+	  tmp_hd = g_strconcat(client_id, ":", client_secret, NULL);	  
+	  header = g_strconcat ("Authorization: Basic ", g_base64_encode (tmp_hd, strlen(tmp_hd)), NULL);
+	  g_free(tmp_hd);
+	}else{
+	  header = g_strconcat ("", NULL);
+	}
+
+	oauth2_post_request (request, OAUTH2info[i][OA2_BASE_URL], OAUTH2info[i][OA2_REFRESH_RESOURCE], header, body);
+	ret = oauth2_contact_server (sock, request, response);
+
+	if(oauth2_filter_access (response, access_token, &expiry) == 0){
+	  OAUTH2Data->access_token = access_token;
+	  OAUTH2Data->expiry = expiry;
+	  OAUTH2Data->expiry_str = g_strdup_printf ("%i", expiry);
+	  ret = 0;
+	  log_message(LOG_PROTOCOL, "OAUTH2 access token obtained\n");
+	}else{
+	  log_message(LOG_PROTOCOL, "OAUTH2 access token not obtained\n");
+	  debug_print("OAUTH2 - request: %s\n Response: %s", request, response);
+	  ret = 1;
+	}
+
+	debug_print("OAUTH2 - access token: %s\n", access_token);
+	debug_print("OAUTH2 - access token expiry: %i\n", expiry);
+	
+	sock_close(sock, TRUE);
+	g_free(body);
+	g_free(header);
+	g_free(request);
+	g_free(response);
+	g_free(client_id);
+	g_free(client_secret);
+
+	return (ret);
+}
+
+static gint oauth2_contact_server (SockInfo *sock, gchar *request, gchar *response)
+{
+	gint len;
+	gint ret;
+	gchar *token;
+	gint toread = OAUTH2BUFSIZE;	
+	time_t startplus = time(NULL);
+	
+	len = strlen(request);
+	
+	startplus += 10;
+	
+	if (sock_write (sock, request, len+1) < 0) {
+	  log_message(LOG_PROTOCOL, "OAUTH2 socket write error\n");
+	  return (1);
+	}
+	
+	token = g_strconcat ("", NULL);
+	do {
+	  
+	  ret = sock_read  (sock, response, OAUTH2BUFSIZE);
+	  if (ret < 0 && errno == EAGAIN)
+	    continue;
+	  if (ret < 0) 
+	    break;
+	  if (ret == 0)
+	    break;
+	  
+	  toread -= ret;
+	  token = g_strconcat(token, response, NULL);
+	} while ((toread > 0) && (time(NULL) < startplus)); 
+	
+	if(time(NULL) >= startplus)
+	  log_message(LOG_PROTOCOL, "OAUTH2 socket timeout error \n");
+	
+	g_free(token);
+	
+	return (0);
+}
+
+gint oauth2_authorisation_url (Oauth2Service provider, gchar **url, const gchar *custom_client_id)
+{
+        gint i;
+	const gchar *client_id;
+
+	i = (int)provider - 1;
+	if (i < 0 || i > (OAUTH2AUTH_LAST-1))
+	  return (1);
+	
+	if(custom_client_id)
+	  client_id = custom_client_id;
+	else
+	  client_id = oauth2_decode(OAUTH2info[i][OA2_CLIENT_ID]);
+	
+	*url = g_strconcat ("https://", OAUTH2info[i][OA2_BASE_URL],OAUTH2info[i][OA2_AUTH_RESOURCE], "?client_id=",
+			    g_uri_escape_string (client_id, NULL, FALSE), NULL);
+			    
+	if(OAUTH2info[i][OA2_REDIRECT_URI][0])
+	  *url = g_strconcat (*url, "&redirect_uri=", g_uri_escape_string (OAUTH2info[i][OA2_REDIRECT_URI], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_RESPONSE_TYPE][0])
+	  *url = g_strconcat (*url, "&response_type=",g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_TYPE], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_SCOPE_FOR_AUTH][0])
+	  *url = g_strconcat (*url, "&scope=", g_uri_escape_string (OAUTH2info[i][OA2_SCOPE_FOR_AUTH], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_TENANT][0])
+	  *url = g_strconcat (*url, "&tenant=", g_uri_escape_string (OAUTH2info[i][OA2_TENANT], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_RESPONSE_MODE][0])
+	  *url = g_strconcat (*url, "&response_mode=", g_uri_escape_string (OAUTH2info[i][OA2_RESPONSE_MODE], NULL, FALSE), NULL);
+	if(OAUTH2info[i][OA2_STATE][0])
+	  *url = g_strconcat (*url, "&state=", g_uri_escape_string (OAUTH2info[i][OA2_STATE], NULL, FALSE), NULL);
+
+	return (0);
+}
+
+gint oauth2_check_passwds (PrefsAccount *ac_prefs)
+{
+	gchar *uid = g_strdup_printf("%d", ac_prefs->account_id);
+	gint expiry;
+	OAUTH2Data *OAUTH2Data = g_malloc(sizeof(* OAUTH2Data)); 
+	gint ret;
+
+	oauth2_init (OAUTH2Data);
+
+	if (ac_prefs->oauth2_use_custom_id) {
+	  OAUTH2Data->custom_client_id = ac_prefs->oauth2_cust_client_id;
+	  OAUTH2Data->custom_client_secret = ac_prefs->oauth2_cust_client_secret;
+	}
+	
+	if(passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_EXPIRY)) {
+	  expiry = atoi(passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY));
+	  if (expiry >  (g_get_real_time () / G_USEC_PER_SEC)){
+	    g_free(OAUTH2Data);
+	    log_message(LOG_PROTOCOL, "OAUTH2 access token still fresh\n");
+	    return (0);
+	  }
+	}
+	
+	if(passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_REFRESH)) {
+	  log_message(LOG_PROTOCOL, "OAUTH2 obtaining access token using refresh token\n");
+	  OAUTH2Data->refresh_token = passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_REFRESH);
+	  ret = oauth2_use_refresh_token (ac_prefs->oauth2_provider, OAUTH2Data);
+	}else if (passwd_store_has_password(PWS_ACCOUNT, uid, PWS_ACCOUNT_OAUTH2_AUTH)) {
+	  log_message(LOG_PROTOCOL, "OAUTH2 trying for fresh access token with auth code\n");
+	  ret = oauth2_obtain_tokens (ac_prefs->oauth2_provider, OAUTH2Data, 
+				      passwd_store_get_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_AUTH));
+	}else{
+	  ret = 1;
+	}
+	
+	if (ret){
+	  log_message(LOG_PROTOCOL, "OAUTH2 access token not obtained\n");
+	}else{
+	  passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_RECV, OAUTH2Data->access_token, FALSE);
+	  passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_SEND, OAUTH2Data->access_token, FALSE);
+	  passwd_store_set_account(ac_prefs->account_id, PWS_ACCOUNT_OAUTH2_EXPIRY, OAUTH2Data->expiry_str, FALSE);
+	  log_message(LOG_PROTOCOL, "OAUTH2 access token updated\n");
+	}
+
+	g_free(OAUTH2Data);
+	
+	return (ret);
+}
+
+/* returns allocated string which must be freed */
+guchar* oauth2_decode(const gchar *in)
+{
+         guchar *tmp;
+	 gsize len;
+	 
+	 tmp = g_base64_decode(in, &len);
+	 passcrypt_decrypt(tmp, len);
+	 return tmp;
+}
+
+/* For testing */
+void oauth2_encode(const gchar *in)
+{
+         guchar *tmp = g_strdup(in);
+	 guchar *tmp2 = g_strdup(in);
+	 gchar *result;
+	 gsize len = strlen(in);
+	 
+	 passcrypt_encrypt(tmp, len);
+	 result = g_base64_encode(tmp, len); 
+	 tmp2 = oauth2_decode(result);
+	 
+	 log_message(LOG_PROTOCOL, "OAUTH2 original: %s\n", in);
+	 log_message(LOG_PROTOCOL, "OAUTH2 encoded: %s\n", result);
+	 log_message(LOG_PROTOCOL, "OAUTH2 decoded: %s\n\n", tmp2);
+	 
+	 g_free(tmp);  
+	 g_free(tmp2);
+	 g_free(result);
+}
+
+gint oauth2_init (OAUTH2Data *OAUTH2Data)
+{ 
+	 OAUTH2Data->refresh_token = NULL;
+	 OAUTH2Data->access_token = NULL;
+	 OAUTH2Data->expiry_str = NULL;
+	 OAUTH2Data->expiry = 0;
+	 OAUTH2Data->custom_client_id = NULL;
+	 OAUTH2Data->custom_client_secret = NULL;
+	 
+	 return (0);
+}
diff --git a/src/oauth2.h b/src/oauth2.h
new file mode 100644
index 000000000..6de3429dc
--- /dev/null
+++ b/src/oauth2.h
@@ -0,0 +1,156 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2020 the Claws Mail team
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+
+#include "socket.h"
+#include "passwordstore.h"
+#include "smtp.h"
+#include "prefs_account.h"
+
+#define OAUTH2BUFSIZE		8192
+
+typedef enum
+{
+	OA2_BASE_URL,
+	OA2_CLIENT_ID,
+	OA2_CLIENT_SECRET,
+	OA2_REDIRECT_URI,
+	OA2_AUTH_RESOURCE,
+	OA2_ACCESS_RESOURCE,
+	OA2_REFRESH_RESOURCE,
+	OA2_RESPONSE_TYPE,
+	OA2_SCOPE_FOR_AUTH,
+	OA2_GRANT_TYPE_ACCESS,
+	OA2_GRANT_TYPE_REFRESH,
+	OA2_TENANT,
+	OA2_STATE,
+	OA2_ACCESS_TYPE,
+	OA2_SCOPE_FOR_ACCESS,
+	OA2_RESPONSE_MODE,
+	OA2_HEADER_AUTH_BASIC
+} Oauth2Params;
+
+//Yahoo requires token requests to send POST header Authorization: Basic 
+//where the password is Base64 encoding of client_id:client_secret
+
+static gchar *OAUTH2info[4][17]={
+  {"accounts.google.com", 
+   "G/jjil7/XHfv4mw90hhhFy5hRci8NeOF3w7QtX8hb9yljE+mU0/MvGk3G4RoUWK13phSIZ7+JSSg4R2f1RV2NbaT5DODMMt5", 
+   "cABm8Lx5PgnrUOOwNJSamcG8Nlj8g8go", 
+   "urn:ietf:wg:oauth:2.0:oob", 
+   "/o/oauth2/auth", 
+   "/o/oauth2/token", 
+   "/o/oauth2/token", 
+   "code", 
+   "https://mail.google.com", 
+   "authorization_code", 
+   "refresh_token",
+   "",
+   "",
+   "",
+   "",
+   "",
+   ""},
+  {"login.microsoftonline.com", 
+   "Srm4tajDIHKiu25KIxOlaqei+AJ8q/DPT7PNOhskKrzIjlGT", 
+   "",
+   "https://login.microsoftonline.com/common/oauth2/nativeclient",
+   "/common/oauth2/v2.0/authorize",
+   "/common/oauth2/v2.0/token",
+   "/common/oauth2/v2.0/token",
+   "code",
+   "wl.imap offline_access",
+   "authorization_code",
+   "refresh_token",
+   "common",
+   "",
+   "offline",
+   "wl.imap offline_access",
+   "fragment",
+   ""},
+  {"login.microsoftonline.com", 
+   "Srm4tajDIHKiu25KIxOlaqei+AJ8q/DPT7PNOhskKrzIjlGT", 
+   "",
+   "https://login.microsoftonline.com/common/oauth2/nativeclient",
+   "/common/oauth2/v2.0/authorize",
+   "/common/oauth2/v2.0/token",
+   "/common/oauth2/v2.0/token",
+   "code",
+   "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
+   "authorization_code",
+   "refresh_token",
+   "common",
+   "",
+   "offline",
+   "offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/POP.AccessAsUser.All https://outlook.office.com/SMTP.Send",
+   "fragment",
+   ""},
+  {"api.login.yahoo.com",
+   "TTzJciHB9+id6C5eZ1lhRQJVGy8GNYh+iXh8nhiD3cofx5zi4xHLN7Y/IWASKh4Oy7cghOQCs8Q1kmKB2xRWlKP8/fFNXSBFNYpni83PHGUUKgbTYJUz+3/nLLOJASYf", 
+   "T/PyRkrw/ByaZ8mkn6aISpsXhci/fieo+ibj1aRkkqhUKqPKeeH7Xg==", 
+   "oob",
+   "/oauth2/request_auth",
+   "/oauth2/get_token",
+   "/oauth2/get_token",
+   "code",
+   "",
+   "authorization_code",
+   "refresh_token",
+   "",
+   "",
+   "",
+   "",
+   "",
+   "1"}
+};
+
+typedef enum
+{
+	OAUTH2AUTH_NONE,
+	OAUTH2AUTH_GOOGLE,
+	OAUTH2AUTH_OUTLOOK,
+	OAUTH2AUTH_EXCHANGE,
+	OAUTH2AUTH_YAHOO,
+	OAUTH2AUTH_LAST = OAUTH2AUTH_YAHOO
+} Oauth2Service;
+
+
+typedef struct _OAUTH2Data OAUTH2Data;
+struct _OAUTH2Data
+{
+	gchar *refresh_token;
+	gchar *access_token;
+        gint expiry;
+        gchar *expiry_str;
+        gchar *custom_client_id;
+        gchar *custom_client_secret;
+};
+
+gint oauth2_init (OAUTH2Data *OAUTH2Data);
+gint oauth2_check_passwds (PrefsAccount *ac_prefs);
+gint oauth2_obtain_tokens (Oauth2Service provider, OAUTH2Data *OAUTH2Data, const gchar *authcode);
+gint oauth2_authorisation_url (Oauth2Service provider, gchar **url, const gchar *custom_client_id);
+gint oauth2_use_refresh_token (Oauth2Service provider, OAUTH2Data *OAUTH2Data);
+guchar* oauth2_decode(const gchar *in);
+void oauth2_encode(const gchar *in);
diff --git a/src/passwordstore.h b/src/passwordstore.h
index b9198207e..125e65773 100644
--- a/src/passwordstore.h
+++ b/src/passwordstore.h
@@ -93,7 +93,9 @@ gboolean passwd_store_has_password_account(gint account_id,
 #define PWS_ACCOUNT_RECV_CERT "recv_cert"
 #define PWS_ACCOUNT_SEND_CERT "send_cert"
 #define PWS_ACCOUNT_PROXY_PASS "proxy_pass"
-
+#define PWS_ACCOUNT_OAUTH2_AUTH "oauth2_auth"
+#define PWS_ACCOUNT_OAUTH2_REFRESH "oauth2_refresh"
+#define PWS_ACCOUNT_OAUTH2_EXPIRY "oauth2_access_expiry"
 #define PWS_CORE_PROXY "proxy"
 #define PWS_CORE_PROXY_PASS "proxy_pass"
 
diff --git a/src/pop.c b/src/pop.c
index 2b8c70a6d..0954df85c 100644
--- a/src/pop.c
+++ b/src/pop.c
@@ -178,6 +178,31 @@ static gint pop3_getauth_apop_send(Pop3Session *session)
 	return PS_SUCCESS;
 }
 
+static gint pop3_getauth_oauth2_send(Pop3Session *session)
+{
+	gchar buf[MESSAGEBUFSIZE], *b64buf, *out;
+	gint len;
+
+	cm_return_val_if_fail(session->user != NULL, -1);
+	cm_return_val_if_fail(session->pass != NULL, -1);
+
+	session->state = POP3_GETAUTH_OAUTH2;
+	memset(buf, 0, sizeof buf);
+
+	/* "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"*/
+       /* session->pass contains the OAUTH2 Access Token*/
+	len = sprintf(buf, "user=%s\1auth=Bearer %s\1\1", session->user, session->pass);
+	b64buf = g_base64_encode(buf, len);
+	out = g_strconcat("AUTH XOAUTH2 ", b64buf, NULL);
+	g_free(b64buf);
+       
+	pop3_gen_send(session, "%s", out);
+        /* Any error response contains base64({JSON-Body}) containing three values: status, schemes, and scope */
+        /* This could be dealt with but is currently written to the log in a fairly graceful fail - not crucial */
+	g_free(out);
+	return PS_SUCCESS;
+}
+
 static gint pop3_getrange_stat_send(Pop3Session *session)
 {
 	session->state = POP3_GETRANGE_STAT;
@@ -508,6 +533,8 @@ static void pop3_gen_send(Pop3Session *session, const gchar *format, ...)
 
 	if (!g_ascii_strncasecmp(buf, "PASS ", 5))
 		log_print(LOG_PROTOCOL, "POP> PASS ********\n");
+        else if  (!g_ascii_strncasecmp(buf, "AUTH XOAUTH2 ", 13))
+		log_print(LOG_PROTOCOL, "POP> AUTH XOAUTH2  ********\n");
 	else
 		log_print(LOG_PROTOCOL, "POP> %s\n", buf);
 
@@ -964,8 +991,10 @@ static gint pop3_session_recv_msg(Session *session, const gchar *msg)
 			val = pop3_stls_send(pop3_session);
 		else
 #endif
-		if (pop3_session->ac_prefs->use_apop_auth)
+		if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_APOP)
 			val = pop3_getauth_apop_send(pop3_session);
+                else if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_OAUTH2)
+			val = pop3_getauth_oauth2_send(pop3_session);
 		else
 			val = pop3_getauth_user_send(pop3_session);
 		break;
@@ -973,8 +1002,10 @@ static gint pop3_session_recv_msg(Session *session, const gchar *msg)
 	case POP3_STLS:
 		if (pop3_stls_recv(pop3_session) != PS_SUCCESS)
 			return -1;
-		if (pop3_session->ac_prefs->use_apop_auth)
+		if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_APOP)
 			val = pop3_getauth_apop_send(pop3_session);
+                else if (pop3_session->ac_prefs->use_pop_auth && pop3_session->ac_prefs->pop_auth_type == POPAUTH_OAUTH2)
+			val = pop3_getauth_oauth2_send(pop3_session);
 		else
 			val = pop3_getauth_user_send(pop3_session);
 		break;
@@ -984,6 +1015,7 @@ static gint pop3_session_recv_msg(Session *session, const gchar *msg)
 		break;
 	case POP3_GETAUTH_PASS:
 	case POP3_GETAUTH_APOP:
+        case POP3_GETAUTH_OAUTH2:
 		if (!pop3_session->pop_before_smtp)
 			val = pop3_getrange_stat_send(pop3_session);
 		else
diff --git a/src/pop.h b/src/pop.h
index ba13e86e3..4fc5257d4 100644
--- a/src/pop.h
+++ b/src/pop.h
@@ -53,6 +53,7 @@ typedef enum {
 	POP3_GETAUTH_USER,
 	POP3_GETAUTH_PASS,
 	POP3_GETAUTH_APOP,
+	POP3_GETAUTH_OAUTH2,
 	POP3_GETRANGE_STAT,
 	POP3_GETRANGE_LAST,
 	POP3_GETRANGE_UIDL,
diff --git a/src/prefs_account.c b/src/prefs_account.c
index 7f33edc00..a7475d143 100644
--- a/src/prefs_account.c
+++ b/src/prefs_account.c
@@ -54,6 +54,8 @@
 #include "colorlabel.h"
 #include "smtp.h"
 #include "imap.h"
+#include "pop.h"
+#include "oauth2.h"
 #include "remotefolder.h"
 #include "combobox.h"
 #include "setup.h"
@@ -64,6 +66,9 @@
 #include "ssl_certificate.h"
 #include "passwordstore.h"
 #include "file-utils.h"
+#ifdef USE_GNUTLS
+#include <gnutls/gnutls.h>
+#endif
 
 static gboolean cancelled;
 static gboolean new_account;
@@ -137,7 +142,8 @@ typedef struct ReceivePage
     GtkWidget *vbox;
 
 	GtkWidget *pop3_frame;
-	GtkWidget *use_apop_checkbtn;
+	GtkWidget *pop_auth_checkbtn;
+	GtkWidget *pop_auth_type_optmenu;
 	GtkWidget *rmmail_checkbtn;
 	GtkWidget *leave_time_spinbtn;
 	GtkWidget *leave_hour_spinbtn;
@@ -195,6 +201,31 @@ typedef struct SendPage
 	GtkWidget *pop_auth_minutes_lbl;
 } SendPage;
 
+typedef struct Oauth2Page
+{
+        PrefsPage page;
+
+        GtkWidget *vbox;
+        GtkWidget *oauth2_sensitive;
+
+	GtkWidget *oauth2_authorise_btn;
+	GtkWidget *oauth2_deauthorise_btn;
+	GtkWidget *oauth2_authcode_entry;
+	GtkWidget *oauth2_auth_optmenu;	
+ 	GtkWidget *oauth2_link_button;
+        gpointer *protocol_optmenu;
+        GtkWidget *oauth2_customid_checkbtn;
+        GtkWidget *oauth2_cust_client_id_entry;
+        GtkWidget *oauth2_cust_client_secret_entry;
+
+} Oauth2Page;
+
+typedef struct
+{
+        gchar *auth_uri;
+        GtkWidget *entry;
+} AuthCodeQueryButtonData;
+
 typedef struct ComposePage
 {
     PrefsPage page;
@@ -343,6 +374,7 @@ typedef struct AdvancedPage
 static BasicPage basic_page;
 static ReceivePage receive_page;
 static SendPage send_page;
+static Oauth2Page oauth2_page;
 static ComposePage compose_page;
 static TemplatesPage templates_page;
 static PrivacyPage privacy_page;
@@ -382,7 +414,18 @@ static void prefs_account_imap_auth_type_set_optmenu	(PrefParam *pparam);
 static void prefs_account_smtp_auth_type_set_data_from_optmenu
 							(PrefParam *pparam);
 static void prefs_account_smtp_auth_type_set_optmenu	(PrefParam *pparam);
+static void prefs_account_pop_auth_type_set_data_from_optmenu
+							(PrefParam *pparam);
+static void prefs_account_pop_auth_type_set_optmenu	(PrefParam *pparam);
 
+static void prefs_account_oauth2_provider_set_data_from_optmenu
+							(PrefParam *pparam);
+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_set_sensitivity(void);
+static void prefs_account_oauth2_set_auth_sensitivity(void);
+static void prefs_account_oauth2_obtain_tokens(GtkButton *button, gpointer data);
 static void prefs_account_set_autochk_interval_from_widgets(PrefParam *pparam);
 static void prefs_account_set_autochk_interval_to_widgets(PrefParam *pparam);
 
@@ -474,10 +517,15 @@ static PrefParam basic_param[] = {
 };
 
 static PrefParam receive_param[] = {
-	{"use_apop_auth", "FALSE", &tmp_ac_prefs.use_apop_auth, P_BOOL,
-	 &receive_page.use_apop_checkbtn,
+	{"use_pop_auth", "FALSE", &tmp_ac_prefs.use_pop_auth, P_BOOL,
+	 &receive_page.pop_auth_checkbtn,
 	 prefs_set_data_from_toggle, prefs_set_toggle},
 
+	{"pop_auth_method", "0", &tmp_ac_prefs.pop_auth_type, P_ENUM,
+	 &receive_page.pop_auth_type_optmenu,
+	 prefs_account_pop_auth_type_set_data_from_optmenu,
+	 prefs_account_pop_auth_type_set_optmenu},
+
 	{"remove_mail", "TRUE", &tmp_ac_prefs.rmmail, P_BOOL,
 	 &receive_page.rmmail_checkbtn,
 	 prefs_set_data_from_toggle, prefs_set_toggle},
@@ -601,6 +649,31 @@ static PrefParam send_param[] = {
 	{NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
 };
 
+static PrefParam oauth2_param[] = {
+	{"oauth2_auth_provider", "0", &tmp_ac_prefs.oauth2_provider, P_ENUM,
+	 &oauth2_page.oauth2_auth_optmenu,
+	 prefs_account_oauth2_provider_set_data_from_optmenu,
+	 prefs_account_oauth2_provider_set_optmenu},
+
+	{"oauth2_date", 0, &tmp_ac_prefs.oauth2_date, P_INT,
+	 NULL, NULL, NULL},
+
+	{"oauth2_authcode", NULL, &tmp_ac_prefs.oauth2_authcode, P_PASSWORD,
+	 NULL, NULL, NULL},
+
+	{"oauth2_use_custom_id", "FALSE", &tmp_ac_prefs.oauth2_use_custom_id, P_BOOL,
+	 &oauth2_page.oauth2_customid_checkbtn,
+	 prefs_set_data_from_toggle, prefs_set_toggle},
+
+	{"oauth2_cust_client_id", NULL, &tmp_ac_prefs.oauth2_cust_client_id, P_STRING,
+	 &oauth2_page.oauth2_cust_client_id_entry, prefs_set_data_from_entry, prefs_set_entry},
+
+	{"oauth2_cust_client_secret", NULL, &tmp_ac_prefs.oauth2_cust_client_secret, P_STRING,
+	 &oauth2_page.oauth2_cust_client_secret_entry, prefs_set_data_from_entry, prefs_set_entry},
+
+	{NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
+};
+
 static PrefParam compose_param[] = {
 	{"signature_type", "0", &tmp_ac_prefs.sig_type, P_ENUM,
 	 &compose_page.sigfile_radiobtn,
@@ -1479,10 +1552,10 @@ static void receive_create_widget_func(PrefsPage * _page,
 	ReceivePage *page = (ReceivePage *) _page;
 	PrefsAccount *ac_prefs = (PrefsAccount *) data;
 
-	GtkWidget *vbox1, *vbox2, *vbox3, *vbox4;
-	GtkWidget *hbox1, *hbox2;
+	GtkWidget *vbox1, *vbox2, *vbox3, *vbox4, *vbox5;
+	GtkWidget *hbox1, *hbox2, *hbox3;
 	GtkWidget *frame1;
-	GtkWidget *use_apop_checkbtn;
+	GtkWidget *pop_auth_checkbtn;
 	GtkWidget *rmmail_checkbtn;
 	GtkWidget *hbox_spc;
 	GtkWidget *leave_time_label;
@@ -1516,8 +1589,8 @@ static void receive_create_widget_func(PrefsPage * _page,
 	GtkObject *adj;
 	struct AutocheckWidgets *autochk_widgets;
 
-	GtkWidget *optmenu;
-	GtkListStore *menu;
+	GtkWidget *optmenu, *optmenu2;
+	GtkListStore *menu, *menu2;
 	GtkTreeIter iter;
 	GtkWidget *recvatgetall_checkbtn;
 
@@ -1557,8 +1630,38 @@ static void receive_create_widget_func(PrefsPage * _page,
 			  local_inbox_entry);
 
 	vbox2 = gtkut_get_options_frame(vbox1, &frame1, _("POP"));
-	PACK_CHECK_BUTTON (vbox2, use_apop_checkbtn,
-			   _("Use secure authentication (APOP)"));
+
+	PACK_CHECK_BUTTON (vbox2, pop_auth_checkbtn,
+			   _("Use secure POP authentication"));
+
+	vbox5 = gtk_vbox_new (FALSE, 0);
+	gtk_widget_show (vbox5);
+	gtk_box_pack_start (GTK_BOX (vbox2), vbox5, FALSE, FALSE, 0);
+
+	hbox3 = gtk_hbox_new (FALSE, 8);
+	gtk_widget_show (hbox3);
+	gtk_box_pack_start (GTK_BOX (vbox5), hbox3, FALSE, FALSE, 0);
+
+	hbox_spc = gtk_hbox_new (FALSE, 0);
+	gtk_widget_show (hbox_spc);
+	gtk_box_pack_start (GTK_BOX (hbox3), hbox_spc, FALSE, FALSE, 0);
+	gtk_widget_set_size_request (hbox_spc, 12, -1);
+
+	label = gtk_label_new (_("Authentication method"));
+	gtk_widget_show (label);
+	gtk_box_pack_start (GTK_BOX (hbox3), label, FALSE, FALSE, 0);
+
+	optmenu2 = gtkut_sc_combobox_create(NULL, FALSE);
+	menu2 = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu2)));
+	gtk_widget_show (optmenu2);
+	gtk_box_pack_start (GTK_BOX (hbox3), optmenu2, FALSE, FALSE, 0);
+
+	COMBOBOX_ADD (menu2, _("Select"), 0);
+	COMBOBOX_ADD (menu2, NULL, 0);
+	COMBOBOX_ADD (menu2, "APOP", POPAUTH_APOP);
+	COMBOBOX_ADD (menu2, "OAUTH2", POPAUTH_OAUTH2);
+
+	SET_TOGGLE_SENSITIVITY (pop_auth_checkbtn, vbox5);
 
 	PACK_CHECK_BUTTON (vbox2, rmmail_checkbtn,
 			   _("Remove messages on server when received"));
@@ -1695,6 +1798,7 @@ static void receive_create_widget_func(PrefsPage * _page,
 	COMBOBOX_ADD (menu, "SCRAM-SHA-1", IMAP_AUTH_SCRAM_SHA1);
 	COMBOBOX_ADD (menu, "PLAIN", IMAP_AUTH_PLAIN);
 	COMBOBOX_ADD (menu, "LOGIN", IMAP_AUTH_LOGIN);
+	COMBOBOX_ADD (menu, "OAUTH2", IMAP_AUTH_OAUTH2);
 
 	hbox1 = gtk_hbox_new (FALSE, 8);
 	gtk_widget_show (hbox1);
@@ -1805,7 +1909,8 @@ static void receive_create_widget_func(PrefsPage * _page,
 		 _("'Get Mail' checks for new messages on this account"));
 
 	page->pop3_frame               = frame1;
-	page->use_apop_checkbtn          = use_apop_checkbtn;
+	page->pop_auth_checkbtn          = pop_auth_checkbtn;
+	page->pop_auth_type_optmenu      = optmenu2;
 	page->rmmail_checkbtn            = rmmail_checkbtn;
 	page->leave_time_spinbtn         = leave_time_spinbtn;
 	page->leave_hour_spinbtn         = leave_hour_spinbtn;
@@ -1950,6 +2055,7 @@ static void send_create_widget_func(PrefsPage * _page,
 	COMBOBOX_ADD (menu, "PLAIN", SMTPAUTH_PLAIN);
 	COMBOBOX_ADD (menu, "LOGIN", SMTPAUTH_LOGIN);
 	COMBOBOX_ADD (menu, "CRAM-MD5", SMTPAUTH_CRAM_MD5);
+	COMBOBOX_ADD (menu, "OAUTH2", SMTPAUTH_OAUTH2);
 	COMBOBOX_ADD (menu, "DIGEST-MD5", SMTPAUTH_DIGEST_MD5);
 	gtk_list_store_set(menu, &iter, COMBOBOX_SENS, FALSE, -1);
 
@@ -2095,6 +2201,217 @@ static void send_create_widget_func(PrefsPage * _page,
 	page->page.widget = vbox1;
 }
 	
+static void oauth2_create_widget_func(PrefsPage * _page,
+                                           GtkWindow * window,
+                                           gpointer data)
+{
+	Oauth2Page *page = (Oauth2Page *) _page;
+	PrefsAccount *ac_prefs = (PrefsAccount *) data;
+
+	GtkWidget *vbox1, *vbox2, *vbox3;
+	GtkWidget *hbox;
+	GtkWidget *hbox_spc;
+	GtkWidget *oauth2_authorise_btn;
+	GtkWidget *auth_vbox, *auth_frame;
+	GtkWidget *label;
+	GtkWidget *oauth2_authcode_entry;
+	GtkWidget *oauth2_auth_optmenu;
+        GtkWidget *oauth2_link_button;
+	GtkWidget *oauth2_customid_checkbtn;
+	GtkWidget *oauth2_cust_client_id_entry;
+	GtkWidget *oauth2_cust_client_secret_entry;
+	GtkWidget *table1;
+	GtkListStore *menu;
+	GtkTreeIter iter;
+	char *buf;
+	struct BasicProtocol *protocol_optmenu;
+
+	vbox1 = gtk_vbox_new (FALSE, VSPACING);
+	gtk_widget_show (vbox1);
+	gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER);
+
+	auth_vbox = gtkut_get_options_frame(vbox1, &auth_frame,
+			_("Authorisation"));
+
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_widget_show (hbox);
+	gtk_box_pack_start (GTK_BOX (auth_vbox), hbox, FALSE, FALSE, 0);
+
+	hbox_spc = gtk_hbox_new (FALSE, 0);
+	gtk_widget_show (hbox_spc);
+	gtk_widget_set_size_request (hbox_spc, 12, -1);
+	gtk_box_pack_start (GTK_BOX (hbox), hbox_spc, FALSE, FALSE, 0);
+
+	/* Email service provider */
+
+	hbox = gtk_hbox_new (FALSE, 8);
+	gtk_widget_show (hbox);
+	gtk_box_pack_start (GTK_BOX (auth_vbox), hbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new (_("Select OAUTH2 Email Service Provider"));
+	gtk_widget_show (label);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+	oauth2_auth_optmenu = gtkut_sc_combobox_create(NULL, FALSE);
+	menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(oauth2_auth_optmenu)));
+	gtk_widget_show (oauth2_auth_optmenu);
+	gtk_box_pack_start (GTK_BOX (hbox), oauth2_auth_optmenu, FALSE, FALSE, 0);
+
+	COMBOBOX_ADD (menu, _("Select"), NULL);
+	COMBOBOX_ADD (menu, NULL, 0);
+	COMBOBOX_ADD (menu, "Google/Gmail", OAUTH2AUTH_GOOGLE);
+	COMBOBOX_ADD (menu, "MS Outlook", OAUTH2AUTH_OUTLOOK);
+	COMBOBOX_ADD (menu, "MS Exchange", OAUTH2AUTH_EXCHANGE);
+	COMBOBOX_ADD (menu, "Yahoo", OAUTH2AUTH_YAHOO);
+
+	protocol_optmenu = g_new(struct BasicProtocol, 1);
+	protocol_optmenu->combobox = oauth2_auth_optmenu;
+	protocol_optmenu->label = label;
+	protocol_optmenu->descrlabel = label;
+
+	hbox = gtk_hbox_new (FALSE, 8);
+	gtk_widget_show (hbox);
+	gtk_box_pack_start (GTK_BOX (auth_vbox), hbox, FALSE, FALSE, 0);
+
+	vbox3 = gtk_vbox_new (FALSE, 0);
+	gtk_widget_show (vbox3);
+	gtk_box_pack_start (GTK_BOX (auth_vbox), vbox3, FALSE, FALSE, 0);
+
+	PACK_CHECK_BUTTON (vbox3, oauth2_customid_checkbtn,
+			   _("Use custom client details"));
+
+	vbox2 = gtk_vbox_new (FALSE, 0);
+	gtk_widget_show (vbox2);
+	gtk_box_pack_start (GTK_BOX (vbox3), vbox2, FALSE, FALSE, 0);
+
+	table1 = gtk_table_new (3, 2, FALSE);
+	gtk_widget_show (table1);
+	gtk_container_add (GTK_CONTAINER (vbox2), table1);
+	gtk_container_set_border_width (GTK_CONTAINER (table1), 8);
+	gtk_table_set_row_spacings (GTK_TABLE (table1), VSPACING_NARROW);
+	gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
+
+	label = gtk_label_new (_("Client ID"));
+	gtk_widget_show (label);
+	gtk_table_attach (GTK_TABLE (table1), label, 0, 1, 0, 1,
+			  GTK_FILL, 0, 0, 0);
+	gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
+
+	label = gtk_label_new (_("Client secret"));
+	gtk_widget_show (label);
+	gtk_table_attach (GTK_TABLE (table1), label, 0, 1, 1, 2,
+			  GTK_FILL, 0, 0, 0);
+	gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
+
+	oauth2_cust_client_id_entry = gtk_entry_new ();
+	gtk_widget_show (oauth2_cust_client_id_entry);
+	gtk_table_attach (GTK_TABLE (table1), oauth2_cust_client_id_entry, 1, 2, 0, 1,
+			  GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+			  GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+
+	oauth2_cust_client_secret_entry = gtk_entry_new ();
+	gtk_widget_show (oauth2_cust_client_secret_entry);
+	gtk_table_attach (GTK_TABLE (table1), oauth2_cust_client_secret_entry, 1, 2, 1, 2,
+			  GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+			  GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+
+	hbox_spc = gtk_hbox_new (FALSE, 0);
+	gtk_widget_show (hbox_spc);
+	gtk_box_pack_start (GTK_BOX (hbox), hbox_spc, FALSE, FALSE, 0);
+	gtk_widget_set_size_request (hbox_spc, 12, -1);
+
+	SET_TOGGLE_SENSITIVITY (oauth2_customid_checkbtn, vbox2);
+
+	hbox_spc = gtk_hbox_new (FALSE, 0);
+	gtk_widget_show (hbox_spc);
+	gtk_box_pack_start (GTK_BOX (vbox2), hbox_spc, FALSE, FALSE, 0);
+	gtk_widget_set_size_request (hbox_spc, 12, 10);
+
+	hbox = gtk_hbox_new (FALSE, 8);
+	gtk_widget_show (hbox);
+	gtk_box_pack_start (GTK_BOX (vbox3), hbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new (_("Obtain authorisation code via a browser"));
+	gtk_widget_show (label);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+	oauth2_link_button = gtk_button_new_with_label(_("Click to copy code to clipboard"));
+	g_signal_connect(G_OBJECT(oauth2_link_button), "clicked", G_CALLBACK(prefs_account_oauth2_copy_url), NULL);
+	gtk_widget_set_sensitive(oauth2_link_button, TRUE);
+	gtk_widget_show (oauth2_link_button);
+	gtk_box_pack_start (GTK_BOX (hbox), oauth2_link_button, FALSE, FALSE, 0);
+
+	/* Authorisation code */
+	hbox = gtk_hbox_new (FALSE, 8);
+	gtk_widget_show (hbox);
+	gtk_box_pack_start (GTK_BOX (vbox3), hbox, FALSE, FALSE, 0);
+	gtk_widget_set_size_request (hbox, -1, 50);
+
+	label = gtk_label_new (_("Authorisation code"));
+	gtk_widget_show (label);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+	oauth2_authcode_entry = gtk_entry_new ();
+	gtk_widget_show (oauth2_authcode_entry);
+	gtk_widget_set_size_request (oauth2_authcode_entry, DEFAULT_ENTRY_WIDTH, -1);
+	gtk_box_pack_start (GTK_BOX (hbox), oauth2_authcode_entry, TRUE, TRUE, 0);
+
+	hbox = gtk_hbox_new (FALSE, 8);
+	gtk_widget_show (hbox);
+	gtk_box_pack_start (GTK_BOX (vbox3), hbox, FALSE, FALSE, 0);
+
+	label = gtk_label_new (_("Complete authorisation "));
+	gtk_widget_show (label);
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+	oauth2_authorise_btn = gtk_button_new_with_label(_("Authorise"));
+	gtk_box_pack_start(GTK_BOX(hbox), oauth2_authorise_btn, FALSE, FALSE, 0);
+	g_signal_connect(G_OBJECT(oauth2_authorise_btn), "clicked",
+			 G_CALLBACK(prefs_account_oauth2_obtain_tokens), NULL);
+	gtk_widget_show(oauth2_authorise_btn);
+
+	/* Add items to page struct */
+
+	page->oauth2_link_button = oauth2_link_button;
+	page->oauth2_authcode_entry = oauth2_authcode_entry;
+	page->oauth2_auth_optmenu = oauth2_auth_optmenu;
+	page->protocol_optmenu = (gpointer)protocol_optmenu;
+	page->oauth2_authorise_btn = oauth2_authorise_btn;
+	page->oauth2_customid_checkbtn = oauth2_customid_checkbtn;
+	page->oauth2_cust_client_id_entry = oauth2_cust_client_id_entry;
+	page->oauth2_cust_client_secret_entry = oauth2_cust_client_secret_entry;
+	page->vbox = vbox1;
+	page->page.widget = vbox1;
+	page->oauth2_sensitive = vbox3;
+
+	tmp_ac_prefs = *ac_prefs;
+
+	g_signal_connect(G_OBJECT(oauth2_auth_optmenu), "changed", 
+			 G_CALLBACK(prefs_account_oauth2_set_sensitivity), NULL);
+	g_signal_connect(G_OBJECT(oauth2_authcode_entry), "changed", 
+			 G_CALLBACK(prefs_account_oauth2_set_auth_sensitivity), NULL);
+
+	if (new_account) {
+		prefs_set_dialog_to_default(oauth2_param);
+	} else {
+		prefs_set_dialog(oauth2_param);
+
+		/* Passwords are handled outside of PrefParams. */
+		buf = passwd_store_get_account(ac_prefs->account_id,
+				PWS_ACCOUNT_OAUTH2_AUTH);
+		gtk_entry_set_text(GTK_ENTRY(page->oauth2_authcode_entry), buf != NULL ? buf : "");
+		if (buf != NULL) {
+			memset(buf, 0, strlen(buf));
+			g_free(buf);
+		}
+	}
+
+	/* For testing */
+	/* 	oauth2_encode(OAUTH2info[0][OA2_CLIENT_ID]); */
+
+}
+
+
 static void compose_create_widget_func(PrefsPage * _page,
                                            GtkWindow * window,
                                            gpointer data)
@@ -3336,7 +3653,10 @@ static gint prefs_basic_apply(void)
 			PWS_ACCOUNT_RECV,
 			gtk_entry_get_text(GTK_ENTRY(basic_page.pass_entry)),
 			FALSE);
-	
+
+	/* Manual password change - reset expiry on OAUTH2 tokens*/
+	passwd_store_set_account(tmp_ac_prefs.account_id, PWS_ACCOUNT_OAUTH2_EXPIRY, "0", FALSE);
+
 	if (protocol == A_IMAP4 || protocol == A_NNTP) {
 		new_id = g_strdup_printf("#%s/%s",
 				protocol == A_IMAP4 ? "imap":"news",
@@ -3377,6 +3697,22 @@ static gint prefs_send_apply(void)
 			gtk_entry_get_text(GTK_ENTRY(send_page.smtp_pass_entry)),
 			FALSE);
 
+	/* Manual password change - reset expiry on OAUTH2 tokens*/
+	passwd_store_set_account(tmp_ac_prefs.account_id, PWS_ACCOUNT_OAUTH2_EXPIRY, "0", FALSE);
+
+	return 0;
+}
+
+static gint prefs_oauth2_apply(void)
+{
+	prefs_set_data_from_dialog(oauth2_param);
+
+	/* Passwords are stored outside of PrefParams. */
+	passwd_store_set_account(tmp_ac_prefs.account_id,
+			PWS_ACCOUNT_OAUTH2_AUTH,
+			gtk_entry_get_text(GTK_ENTRY(oauth2_page.oauth2_authcode_entry)),
+			FALSE);
+
 	return 0;
 }
 
@@ -3477,6 +3813,11 @@ static void send_destroy_widget_func(PrefsPage *_page)
 	/* SendPage *page = (SendPage *) _page; */
 }
 
+static void oauth2_destroy_widget_func(PrefsPage *_page)
+{
+	/* Oauth2Page *page = (Oauth2Page *) _page; */
+}
+
 static void compose_destroy_widget_func(PrefsPage *_page)
 {
 	/* ComposePage *page = (ComposePage *) _page; */
@@ -3539,6 +3880,16 @@ static gboolean send_can_close_func(PrefsPage *_page)
 	return prefs_send_apply() >= 0;
 }
 
+static gboolean oauth2_can_close_func(PrefsPage *_page)
+{	
+	Oauth2Page *page = (Oauth2Page *) _page;
+
+	if (!page->page.page_open)
+		return TRUE;
+
+	return prefs_oauth2_apply() >= 0;
+}
+
 static gboolean compose_can_close_func(PrefsPage *_page)
 {	
 	ComposePage *page = (ComposePage *) _page;
@@ -3634,6 +3985,17 @@ static void send_save_func(PrefsPage *_page)
 		cancelled = FALSE;
 }
 
+static void oauth2_save_func(PrefsPage *_page)
+{
+	Oauth2Page *page = (Oauth2Page *) _page;
+
+	if (!page->page.page_open)
+		return;
+
+	if (prefs_oauth2_apply() >= 0)
+		cancelled = FALSE;
+}
+
 static void compose_save_func(PrefsPage *_page)
 {
 	ComposePage *page = (ComposePage *) _page;
@@ -3768,6 +4130,24 @@ static void register_send_page(void)
 	prefs_account_register_page((PrefsPage *) &send_page);
 }
 
+static void register_oauth2_page(void)
+{
+	static gchar *path[3];
+
+	path[0] = _("Account");
+	path[1] = _("OAUTH2");
+	path[2] = NULL;
+        
+	oauth2_page.page.path = path;
+	oauth2_page.page.weight = 1000.0;
+	oauth2_page.page.create_widget = oauth2_create_widget_func;
+	oauth2_page.page.destroy_widget = oauth2_destroy_widget_func;
+	oauth2_page.page.save_page = oauth2_save_func;
+	oauth2_page.page.can_close = oauth2_can_close_func;
+
+	prefs_account_register_page((PrefsPage *) &oauth2_page);
+}
+
 static void register_compose_page(void)
 {
 	static gchar *path[3];
@@ -3967,6 +4347,7 @@ void prefs_account_init()
 #endif
 	register_proxy_page();
 	register_advanced_page();
+	register_oauth2_page();
 }
 
 PrefsAccount *prefs_account_new(void)
@@ -3978,6 +4359,7 @@ PrefsAccount *prefs_account_new(void)
 	prefs_set_default(basic_param);
 	prefs_set_default(receive_param);
 	prefs_set_default(send_param);
+	prefs_set_default(oauth2_param);
 	prefs_set_default(compose_param);
 	prefs_set_default(templates_param);
 	prefs_set_default(privacy_param);
@@ -4010,6 +4392,7 @@ PrefsAccount *prefs_account_new_from_config(const gchar *label)
 	prefs_set_default(basic_param);
 	prefs_set_default(receive_param);
 	prefs_set_default(send_param);
+	prefs_set_default(oauth2_param);
 	prefs_set_default(compose_param);
 	prefs_set_default(templates_param);
 	prefs_set_default(privacy_param);
@@ -4021,6 +4404,7 @@ PrefsAccount *prefs_account_new_from_config(const gchar *label)
 	prefs_read_config(basic_param, label, rcpath, NULL);
 	prefs_read_config(receive_param, label, rcpath, NULL);
 	prefs_read_config(send_param, label, rcpath, NULL);
+	prefs_read_config(oauth2_param, label, rcpath, NULL);
 	prefs_read_config(compose_param, label, rcpath, NULL);
 	prefs_read_config(templates_param, label, rcpath, NULL);
 	prefs_read_config(privacy_param, label, rcpath, NULL);
@@ -4157,6 +4541,7 @@ void prefs_account_write_config_all(GList *account_list)
 		WRITE_PARAM(basic_param)
 		WRITE_PARAM(receive_param)
 		WRITE_PARAM(send_param)
+		WRITE_PARAM(oauth2_param)
 		WRITE_PARAM(compose_param)
 		WRITE_PARAM(templates_param)
 		WRITE_PARAM(privacy_param)
@@ -4201,6 +4586,7 @@ void prefs_account_free(PrefsAccount *ac_prefs)
 	prefs_free(basic_param);
 	prefs_free(receive_param);
 	prefs_free(send_param);
+	prefs_free(oauth2_param);
 	prefs_free(compose_param);
 	prefs_free(templates_param);
 	prefs_free(privacy_param);
@@ -4681,6 +5067,157 @@ static void prefs_account_smtp_auth_type_set_optmenu(PrefParam *pparam)
 	combobox_select_by_data(optmenu, type);
 }
 
+static void prefs_account_pop_auth_type_set_data_from_optmenu(PrefParam *pparam)
+{
+	*((RecvProtocol *)pparam->data) =
+		combobox_get_active_data(GTK_COMBO_BOX(*pparam->widget));
+}
+
+static void prefs_account_pop_auth_type_set_optmenu(PrefParam *pparam)
+{
+	POPAuthType type = *((POPAuthType *)pparam->data);
+	GtkComboBox *optmenu = GTK_COMBO_BOX(*pparam->widget);
+
+	combobox_select_by_data(optmenu, type);
+}
+
+static void prefs_account_oauth2_provider_set_data_from_optmenu(PrefParam *pparam)
+{
+	*((Oauth2Service *)pparam->data) =
+		combobox_get_active_data(GTK_COMBO_BOX(*pparam->widget));
+}
+
+static void prefs_account_oauth2_provider_set_optmenu(PrefParam *pparam)
+{
+	Oauth2Service type = *((Oauth2Service *)pparam->data);
+	GtkComboBox *optmenu = GTK_COMBO_BOX(*pparam->widget);
+
+	combobox_select_by_data(optmenu, type);
+}
+
+static void prefs_account_oauth2_set_auth_sensitivity(void)
+{
+  const gchar *authcode = gtk_entry_get_text ((GtkEntry *)oauth2_page.oauth2_authcode_entry);
+  
+  if(strlen(authcode) == 0)
+    gtk_widget_set_sensitive(oauth2_page.oauth2_authorise_btn, FALSE);
+  else
+    gtk_widget_set_sensitive(oauth2_page.oauth2_authorise_btn, TRUE);
+}
+
+static void prefs_account_oauth2_set_sensitivity(void)
+{
+	struct BasicProtocol *protocol_optmenu = (struct BasicProtocol *)oauth2_page.protocol_optmenu;
+	GtkWidget *optmenu = protocol_optmenu->combobox;
+	Oauth2Service service;
+
+	service = combobox_get_active_data(GTK_COMBO_BOX(optmenu));
+
+	if(service == OAUTH2AUTH_NONE)
+	  gtk_widget_set_sensitive(oauth2_page.oauth2_sensitive, FALSE);
+	else
+	  gtk_widget_set_sensitive(oauth2_page.oauth2_sensitive, TRUE);
+}
+
+static void prefs_account_oauth2_copy_url(GtkButton *button, gpointer data)
+{
+	struct BasicProtocol *protocol_optmenu = (struct BasicProtocol *)oauth2_page.protocol_optmenu;
+
+	GtkWidget *optmenu = protocol_optmenu->combobox;
+
+	GtkWidget *win;
+	GtkClipboard *clip, *clip2;
+	gint len;
+	gchar *url;
+	url = g_malloc(OAUTH2BUFSIZE+1);
+	Oauth2Service service;
+	const gchar * custom_client_id = NULL;
+	
+	service = combobox_get_active_data(GTK_COMBO_BOX(optmenu));
+	
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oauth2_page.oauth2_customid_checkbtn)) == TRUE)
+	  custom_client_id = gtk_entry_get_text ((GtkEntry *)oauth2_page.oauth2_cust_client_id_entry);
+	
+	oauth2_authorisation_url(service, &url, custom_client_id);
+	
+	win = gtk_widget_get_toplevel (optmenu);
+	len = strlen(url);
+       
+	clip = gtk_widget_get_clipboard (win, GDK_SELECTION_PRIMARY);
+	clip2 = gtk_widget_get_clipboard (win, GDK_SELECTION_CLIPBOARD);
+	gtk_clipboard_set_text (clip, url, len);
+	gtk_clipboard_set_text (clip2, url, len);
+
+	open_uri(url, prefs_common_get_uri_cmd());
+
+	g_free(url);
+}
+
+static void prefs_account_oauth2_obtain_tokens(GtkButton *button, gpointer data)
+{
+	struct BasicProtocol *protocol_optmenu = (struct BasicProtocol *)oauth2_page.protocol_optmenu;
+
+	GtkWidget *optmenu = protocol_optmenu->combobox;
+	Oauth2Service service;
+        OAUTH2Data *OAUTH2Data = g_malloc(sizeof(* OAUTH2Data));
+	const gchar *authcode = gtk_entry_get_text ((GtkEntry *)oauth2_page.oauth2_authcode_entry);
+	gint ret;
+
+	oauth2_init (OAUTH2Data);
+
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oauth2_page.oauth2_customid_checkbtn)) == TRUE){
+	  OAUTH2Data->custom_client_secret = 
+	    g_strdup(gtk_entry_get_text ((GtkEntry *)oauth2_page.oauth2_cust_client_secret_entry));
+	  OAUTH2Data->custom_client_id = 
+	    g_strdup(gtk_entry_get_text ((GtkEntry *)oauth2_page.oauth2_cust_client_id_entry));
+	}
+
+	service = combobox_get_active_data(GTK_COMBO_BOX(optmenu));
+	ret = oauth2_obtain_tokens (service, OAUTH2Data, authcode);
+	
+	if(ret){
+	  g_free(OAUTH2Data);
+	  return;
+	}
+
+	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;
+
+	g_free(OAUTH2Data);
+
+}
+
 static void prefs_account_set_autochk_interval_from_widgets(PrefParam *pparam)
 {
 	struct AutocheckWidgets *autochk_widgets =
diff --git a/src/prefs_account.h b/src/prefs_account.h
index 4c53fea07..88c922eb5 100644
--- a/src/prefs_account.h
+++ b/src/prefs_account.h
@@ -41,9 +41,16 @@ typedef enum {
 	SIG_DIRECT
 } SigType;
 
+typedef enum
+{
+	POPAUTH_APOP      = 1 << 0,
+	POPAUTH_OAUTH2    = 1 << 1
+} POPAuthType;
+
 #include <glib.h>
 
 #include "smtp.h"
+#include "pop.h"
 #include "gtk/prefswindow.h"
 
 struct _Folder;
@@ -89,7 +96,8 @@ struct _PrefsAccount
 	gboolean use_tls_sni;
 
 	/* Receive */
-	gboolean use_apop_auth;
+	gboolean use_pop_auth;
+        POPAuthType pop_auth_type;
 	gboolean rmmail;
 	gint msg_leave_time;
 	gint msg_leave_hour;
@@ -127,6 +135,14 @@ struct _PrefsAccount
 
 	GSList *customhdr_list;
 
+        /* OAUTH2 */
+        gint oauth2_provider;
+        gint oauth2_date;
+        gchar *oauth2_authcode;
+        gboolean oauth2_use_custom_id;
+        gchar *oauth2_cust_client_id;
+        gchar *oauth2_cust_client_secret;
+
 	/* Compose */
 	SigType sig_type;
 	gchar    *sig_path;
diff --git a/src/send_message.c b/src/send_message.c
index 61f5e55eb..f8bc5a5e5 100644
--- a/src/send_message.c
+++ b/src/send_message.c
@@ -57,6 +57,7 @@
 #include "log.h"
 #include "passwordstore.h"
 #include "file-utils.h"
+#include "oauth2.h"
 
 typedef struct _SendProgressDialog	SendProgressDialog;
 
@@ -298,6 +299,9 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g
 		}
 		port = ac_prefs->set_smtpport ? ac_prefs->smtpport : SMTP_PORT;
 #endif
+		
+		if(ac_prefs->use_smtp_auth && ac_prefs->smtp_auth_type == SMTPAUTH_OAUTH2)
+		        oauth2_check_passwds (ac_prefs);
 
 		if (ac_prefs->use_smtp_auth) {
 			smtp_session->forced_auth_type = ac_prefs->smtp_auth_type;

-----------------------------------------------------------------------


hooks/post-receive
-- 
Claws Mail


More information about the Commits mailing list