1 /* oauth_prof.c -- OAuth 2.0 implementation for XOAUTH2 in SMTP and POP3.
2  *
3  * This code is Copyright (c) 2014, by the authors of nmh.  See the
4  * COPYRIGHT file in the root directory of the nmh distribution for
5  * complete copyright information.
6  */
7 
8 #include <h/mh.h>
9 
10 #ifdef OAUTH_SUPPORT
11 
12 #include <sys/stat.h>
13 
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <strings.h>
19 #include <unistd.h>
20 
21 #include <h/oauth.h>
22 #include <h/utils.h>
23 #include "m_maildir.h"
24 
25 static const struct mh_oauth_service_info SERVICES[] = {
26     /* https://developers.google.com/accounts/docs/OAuth2InstalledApp */
27     {
28         /* name */ "gmail",
29         /* display_name */ "Gmail",
30 
31         /* client_id */ "91584523849-8lv9kgp1rvp8ahta6fa4b125tn2polcg.apps.googleusercontent.com",
32         /* client_secret */ "Ua8sX34xyv7hVrKM-U70dKI6",
33 
34         /* auth_endpoint */ "https://accounts.google.com/o/oauth2/auth",
35         /* redirect_uri */ "urn:ietf:wg:oauth:2.0:oob",
36         /* token_endpoint */ "https://accounts.google.com/o/oauth2/token",
37         /* scope */ "https://mail.google.com/"
38     }
39 };
40 
41 /* Copy service info so we don't have to free it only sometimes. */
42 static void
copy_svc(mh_oauth_service_info * to,const mh_oauth_service_info * from)43 copy_svc(mh_oauth_service_info *to, const mh_oauth_service_info *from)
44 {
45     to->display_name = from->display_name;
46 #define copy(_field_) to->_field_ = getcpy(from->_field_)
47     copy(name);
48     copy(scope);
49     copy(client_id);
50     copy(client_secret);
51     copy(auth_endpoint);
52     copy(token_endpoint);
53     copy(redirect_uri);
54 #undef copy
55 }
56 
57 /* Return profile component node name for a service parameter. */
58 char *
mh_oauth_node_name_for_svc(const char * base_name,const char * svc)59 mh_oauth_node_name_for_svc(const char *base_name, const char *svc)
60 {
61     /* TODO: s/_/-/g ? */
62     return concat("oauth-", svc, "-", base_name, NULL);
63 }
64 
65 /* Update one service_info field if overridden in profile. */
66 static void
update_svc_field(char ** field,const char * base_name,const char * svc)67 update_svc_field(char **field, const char *base_name, const char *svc)
68 {
69     char *name = mh_oauth_node_name_for_svc(base_name, svc);
70     const char *value = context_find(name);
71     if (value != NULL) {
72         free(*field);
73         *field = mh_xstrdup(value);
74     }
75     free(name);
76 }
77 
78 /* Update all service_info fields that are overridden in profile. */
79 static boolean
update_svc(mh_oauth_service_info * svc,const char * svc_name,char * errbuf,size_t errbuflen)80 update_svc(mh_oauth_service_info *svc, const char *svc_name, char *errbuf,
81 	   size_t errbuflen)
82 {
83 #define update(name)                                                    \
84     update_svc_field(&svc->name, #name, svc_name);                       \
85     if (svc->name == NULL) {                                             \
86     	snprintf(errbuf, errbuflen, "%s", #name " is missing");		 \
87 	errbuf[errbuflen - 1] = '\0';					 \
88         return FALSE;                                                    \
89     }
90     update(scope);
91     update(client_id);
92     update(client_secret);
93     update(auth_endpoint);
94     update(token_endpoint);
95     update(redirect_uri);
96 #undef update
97 
98     if (svc->name == NULL) {
99         svc->name = getcpy(svc_name);
100     }
101 
102     if (svc->display_name == NULL) {
103         svc->display_name = svc->name;
104     }
105 
106     return TRUE;
107 }
108 
109 boolean
mh_oauth_get_service_info(const char * svc_name,mh_oauth_service_info * svcinfo,char * errbuf,size_t errbuflen)110 mh_oauth_get_service_info(const char *svc_name, mh_oauth_service_info *svcinfo,
111 			  char *errbuf, size_t errbuflen)
112 {
113     int i;
114 
115     svcinfo->name = svcinfo->display_name = NULL;
116     svcinfo->scope = svcinfo->client_id = NULL;
117     svcinfo->client_secret = svcinfo->auth_endpoint = NULL;
118     svcinfo->token_endpoint = svcinfo->redirect_uri = NULL;
119 
120     for (i = 0; i < (int)DIM(SERVICES); i++) {
121         if (strcmp(SERVICES[i].name, svc_name) == 0) {
122             copy_svc(svcinfo, &SERVICES[i]);
123             break;
124         }
125     }
126 
127     if (!update_svc(svcinfo, svc_name, errbuf, errbuflen)) {
128         return FALSE;
129     }
130 
131     return TRUE;
132 }
133 
134 const char *
mh_oauth_cred_fn(const char * svc)135 mh_oauth_cred_fn(const char *svc)
136 {
137     char *result, *result_if_allocated;
138 
139     char *component = mh_oauth_node_name_for_svc("credential-file", svc);
140     result = context_find(component);
141     free(component);
142 
143     if (result == NULL) {
144         result = concat("oauth-", svc, NULL);
145         result_if_allocated = result;
146     } else {
147         result_if_allocated = NULL;
148     }
149 
150     if (result[0] != '/') {
151         const char *tmp = m_maildir(result);
152         free(result_if_allocated);
153         result = getcpy(tmp);
154     }
155 
156     return result;
157 }
158 #endif
159