/src/neomutt/conn/connaccount.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Connection Credentials |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 2020 Richard Russon <rich@flatcap.org> |
7 | | * |
8 | | * @copyright |
9 | | * This program is free software: you can redistribute it and/or modify it under |
10 | | * the terms of the GNU General Public License as published by the Free Software |
11 | | * Foundation, either version 2 of the License, or (at your option) any later |
12 | | * version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
15 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
16 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
17 | | * details. |
18 | | * |
19 | | * You should have received a copy of the GNU General Public License along with |
20 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | /** |
24 | | * @page conn_account Connection Credentials |
25 | | * |
26 | | * Connection Credentials |
27 | | */ |
28 | | |
29 | | #include "config.h" |
30 | | #include <assert.h> |
31 | | #include <stdbool.h> |
32 | | #include <stdio.h> |
33 | | #include <string.h> |
34 | | #include <sys/types.h> |
35 | | #include "mutt/lib.h" |
36 | | #include "gui/lib.h" |
37 | | #include "mutt.h" |
38 | | #include "connaccount.h" |
39 | | #include "editor/lib.h" |
40 | | #include "history/lib.h" |
41 | | #include "accountcmd.h" |
42 | | #include "globals.h" |
43 | | |
44 | | /** |
45 | | * mutt_account_getuser - Retrieve username into ConnAccount, if necessary |
46 | | * @param cac ConnAccount to fill |
47 | | * @retval 0 Success |
48 | | * @retval -1 Failure |
49 | | */ |
50 | | int mutt_account_getuser(struct ConnAccount *cac) |
51 | 0 | { |
52 | 0 | if (cac->flags & MUTT_ACCT_USER) |
53 | 0 | return 0; |
54 | 0 | if (!cac->get_field) |
55 | 0 | return -1; |
56 | | |
57 | 0 | const char *user = cac->get_field(MUTT_CA_USER, cac->gf_data); |
58 | 0 | if (user) |
59 | 0 | { |
60 | 0 | mutt_str_copy(cac->user, user, sizeof(cac->user)); |
61 | 0 | } |
62 | 0 | else if (mutt_account_call_external_cmd(cac) != MUTT_ACCT_NO_FLAGS) |
63 | 0 | { |
64 | | /* The external command might interact with the screen */ |
65 | 0 | if (!OptNoCurses) |
66 | 0 | mutt_need_hard_redraw(); |
67 | 0 | return 0; |
68 | 0 | } |
69 | 0 | else if (OptNoCurses) |
70 | 0 | { |
71 | 0 | return -1; |
72 | 0 | } |
73 | 0 | else |
74 | 0 | { |
75 | | /* prompt (defaults to unix username), copy into cac->user */ |
76 | 0 | char prompt[256] = { 0 }; |
77 | | /* L10N: Example: Username at myhost.com */ |
78 | 0 | snprintf(prompt, sizeof(prompt), _("Username at %s: "), cac->host); |
79 | 0 | mutt_str_copy(cac->user, Username, sizeof(cac->user)); |
80 | |
|
81 | 0 | struct Buffer *buf = buf_pool_get(); |
82 | 0 | const int rc = mw_get_field(prompt, buf, MUTT_COMP_UNBUFFERED, HC_OTHER, NULL, NULL); |
83 | 0 | mutt_str_copy(cac->user, buf_string(buf), sizeof(cac->user)); |
84 | 0 | buf_pool_release(&buf); |
85 | 0 | if (rc != 0) |
86 | 0 | return -1; |
87 | 0 | } |
88 | | |
89 | 0 | cac->flags |= MUTT_ACCT_USER; |
90 | 0 | return 0; |
91 | 0 | } |
92 | | |
93 | | /** |
94 | | * mutt_account_getlogin - Retrieve login info into ConnAccount, if necessary |
95 | | * @param cac ConnAccount to fill |
96 | | * @retval 0 Success |
97 | | * @retval -1 Failure |
98 | | */ |
99 | | int mutt_account_getlogin(struct ConnAccount *cac) |
100 | 0 | { |
101 | 0 | if (cac->flags & MUTT_ACCT_LOGIN) |
102 | 0 | return 0; |
103 | 0 | if (!cac->get_field) |
104 | 0 | return -1; |
105 | | |
106 | 0 | const char *login = cac->get_field(MUTT_CA_LOGIN, cac->gf_data); |
107 | 0 | if (!login && (mutt_account_getuser(cac) == 0)) |
108 | 0 | { |
109 | 0 | login = cac->user; |
110 | 0 | } |
111 | |
|
112 | 0 | if (!login) |
113 | 0 | { |
114 | 0 | mutt_debug(LL_DEBUG1, "Couldn't get user info\n"); |
115 | 0 | return -1; |
116 | 0 | } |
117 | | |
118 | 0 | mutt_str_copy(cac->login, login, sizeof(cac->login)); |
119 | 0 | cac->flags |= MUTT_ACCT_LOGIN; |
120 | 0 | return 0; |
121 | 0 | } |
122 | | |
123 | | /** |
124 | | * mutt_account_getpass - Fetch password into ConnAccount, if necessary |
125 | | * @param cac ConnAccount to fill |
126 | | * @retval 0 Success |
127 | | * @retval -1 Failure |
128 | | */ |
129 | | int mutt_account_getpass(struct ConnAccount *cac) |
130 | 0 | { |
131 | 0 | if (cac->flags & MUTT_ACCT_PASS) |
132 | 0 | return 0; |
133 | 0 | if (!cac->get_field) |
134 | 0 | return -1; |
135 | | |
136 | 0 | const char *pass = cac->get_field(MUTT_CA_PASS, cac->gf_data); |
137 | 0 | if (pass) |
138 | 0 | { |
139 | 0 | mutt_str_copy(cac->pass, pass, sizeof(cac->pass)); |
140 | 0 | } |
141 | 0 | else if (mutt_account_call_external_cmd(cac) != MUTT_ACCT_NO_FLAGS) |
142 | 0 | { |
143 | | /* The external command might interact with the screen */ |
144 | 0 | if (!OptNoCurses) |
145 | 0 | mutt_need_hard_redraw(); |
146 | 0 | return 0; |
147 | 0 | } |
148 | 0 | else if (OptNoCurses) |
149 | 0 | { |
150 | 0 | return -1; |
151 | 0 | } |
152 | 0 | else |
153 | 0 | { |
154 | 0 | char prompt[256] = { 0 }; |
155 | 0 | snprintf(prompt, sizeof(prompt), _("Password for %s@%s: "), |
156 | 0 | (cac->flags & MUTT_ACCT_LOGIN) ? cac->login : cac->user, cac->host); |
157 | 0 | cac->pass[0] = '\0'; |
158 | |
|
159 | 0 | struct Buffer *buf = buf_pool_get(); |
160 | 0 | const int rc = mw_get_field(prompt, buf, MUTT_COMP_PASS | MUTT_COMP_UNBUFFERED, |
161 | 0 | HC_OTHER, NULL, NULL); |
162 | 0 | mutt_str_copy(cac->pass, buf_string(buf), sizeof(cac->pass)); |
163 | 0 | buf_pool_release(&buf); |
164 | 0 | if (rc != 0) |
165 | 0 | return -1; |
166 | 0 | } |
167 | | |
168 | 0 | cac->flags |= MUTT_ACCT_PASS; |
169 | 0 | return 0; |
170 | 0 | } |
171 | | |
172 | | /** |
173 | | * mutt_account_unsetpass - Unset ConnAccount's password |
174 | | * @param cac ConnAccount to modify |
175 | | */ |
176 | | void mutt_account_unsetpass(struct ConnAccount *cac) |
177 | 0 | { |
178 | 0 | cac->flags &= ~MUTT_ACCT_PASS; |
179 | 0 | memset(cac->pass, 0, sizeof(cac->pass)); |
180 | 0 | } |
181 | | |
182 | | /** |
183 | | * mutt_account_getoauthbearer - Get an OAUTHBEARER/XOAUTH2 token |
184 | | * @param cac Account to use |
185 | | * @param xoauth2 Generate a deprecated XOAUTH2 token |
186 | | * @retval ptr OAuth token |
187 | | * @retval NULL Error |
188 | | * |
189 | | * Run an external command to generate the oauth refresh token for an account, |
190 | | * then create and encode the OAUTHBEARER token based on RFC7628. |
191 | | * |
192 | | * @note Caller should free the token |
193 | | */ |
194 | | char *mutt_account_getoauthbearer(struct ConnAccount *cac, bool xoauth2) |
195 | 0 | { |
196 | 0 | if (!cac || !cac->get_field) |
197 | 0 | return NULL; |
198 | | |
199 | | /* The oauthbearer token includes the login */ |
200 | 0 | if (mutt_account_getlogin(cac)) |
201 | 0 | return NULL; |
202 | | |
203 | 0 | const char *cmd = cac->get_field(MUTT_CA_OAUTH_CMD, cac->gf_data); |
204 | 0 | if (!cmd) |
205 | 0 | { |
206 | | /* L10N: You will see this error message if (1) you have "oauthbearer" in |
207 | | one of your $*_authenticators and (2) you do not have the corresponding |
208 | | $*_oauth_refresh_command defined. So the message does not mean "None of |
209 | | your $*_oauth_refresh_command's are defined." */ |
210 | 0 | mutt_error(_("No OAUTH refresh command defined")); |
211 | 0 | return NULL; |
212 | 0 | } |
213 | | |
214 | 0 | FILE *fp = NULL; |
215 | 0 | pid_t pid = filter_create(cmd, NULL, &fp, NULL, EnvList); |
216 | 0 | if (pid < 0) |
217 | 0 | { |
218 | 0 | mutt_perror(_("Unable to run refresh command")); |
219 | 0 | return NULL; |
220 | 0 | } |
221 | | |
222 | 0 | size_t token_size = 0; |
223 | 0 | char *token = mutt_file_read_line(NULL, &token_size, fp, NULL, MUTT_RL_NO_FLAGS); |
224 | 0 | mutt_file_fclose(&fp); |
225 | 0 | filter_wait(pid); |
226 | | |
227 | | /* The refresh cmd in some cases will invoke gpg to decrypt a token */ |
228 | 0 | if (!OptNoCurses) |
229 | 0 | mutt_need_hard_redraw(); |
230 | |
|
231 | 0 | if (!token || (*token == '\0')) |
232 | 0 | { |
233 | 0 | mutt_error(_("Command returned empty string")); |
234 | 0 | FREE(&token); |
235 | 0 | return NULL; |
236 | 0 | } |
237 | | |
238 | 0 | if ((!xoauth2 && (token_size > 512)) || (xoauth2 && (token_size > 4096))) |
239 | 0 | { |
240 | 0 | mutt_error(_("OAUTH token is too big: %ld"), token_size); |
241 | 0 | FREE(&token); |
242 | 0 | return NULL; |
243 | 0 | } |
244 | | |
245 | | /* 4500 is chosen to allow for both a token that is 4096-long plus a |
246 | | * username that can be up to 320-long. */ |
247 | 0 | char oauthbearer[4500] = { 0 }; |
248 | 0 | int oalen = 0; |
249 | 0 | if (xoauth2) |
250 | 0 | { |
251 | 0 | oalen = snprintf(oauthbearer, sizeof(oauthbearer), |
252 | 0 | "user=%s\001auth=Bearer %s\001\001", cac->login, token); |
253 | 0 | } |
254 | 0 | else |
255 | 0 | { |
256 | 0 | oalen = snprintf(oauthbearer, sizeof(oauthbearer), |
257 | 0 | "n,a=%s,\001host=%s\001port=%d\001auth=Bearer %s\001\001", |
258 | 0 | cac->login, cac->host, cac->port, token); |
259 | 0 | } |
260 | 0 | FREE(&token); |
261 | |
|
262 | 0 | size_t encoded_len = oalen * 4 / 3 + 10; |
263 | 0 | assert(encoded_len < 6010); // Assure LGTM that we won't overflow |
264 | | |
265 | 0 | char *encoded_token = mutt_mem_malloc(encoded_len); |
266 | 0 | mutt_b64_encode(oauthbearer, oalen, encoded_token, encoded_len); |
267 | |
|
268 | 0 | return encoded_token; |
269 | 0 | } |