Re: Hi all
[notmuch-archives.git] / a8 / 3356504690e635f5aeaabb0d79a297a8241931
1 Return-Path: <neil@linux.intel.com>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 32007431FAE\r
6         for <notmuch@notmuchmail.org>; Sun,  7 Jul 2013 04:13:36 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -5\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-5 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_HI=-5] autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id DbiC0UVgXAAf for <notmuch@notmuchmail.org>;\r
16         Sun,  7 Jul 2013 04:13:30 -0700 (PDT)\r
17 Received: from mga09.intel.com (mga09.intel.com [134.134.136.24])\r
18         by olra.theworths.org (Postfix) with ESMTP id A7AB6431FAF\r
19         for <notmuch@notmuchmail.org>; Sun,  7 Jul 2013 04:13:29 -0700 (PDT)\r
20 Received: from orsmga002.jf.intel.com ([10.7.209.21])\r
21         by orsmga102.jf.intel.com with ESMTP; 07 Jul 2013 04:10:54 -0700\r
22 X-ExtLoop1: 1\r
23 X-IronPort-AV: E=Sophos;i="4.87,1013,1363158000"; d="scan'208";a="365957323"\r
24 Received: from unknown (HELO neilpc.config) ([10.252.122.25])\r
25         by orsmga002.jf.intel.com with ESMTP; 07 Jul 2013 04:13:19 -0700\r
26 From: Neil Roberts <neil@linux.intel.com>\r
27 To: notmuch@notmuchmail.org\r
28 Subject: [PATCH 1/2] cli: crypto: Support requesting passwords via FDs from\r
29         the caller\r
30 Date: Sun,  7 Jul 2013 12:14:31 +0100\r
31 Message-Id: <1373195672-9338-2-git-send-email-neil@linux.intel.com>\r
32 X-Mailer: git-send-email 1.7.11.3.g3c3efa5\r
33 In-Reply-To: <1373195672-9338-1-git-send-email-neil@linux.intel.com>\r
34 References: <1373195672-9338-1-git-send-email-neil@linux.intel.com>\r
35 X-BeenThere: notmuch@notmuchmail.org\r
36 X-Mailman-Version: 2.1.13\r
37 Precedence: list\r
38 List-Id: "Use and development of the notmuch mail system."\r
39         <notmuch.notmuchmail.org>\r
40 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
41         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
42 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
43 List-Post: <mailto:notmuch@notmuchmail.org>\r
44 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
45 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
46         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
47 X-List-Received-Date: Sun, 07 Jul 2013 11:13:36 -0000\r
48 \r
49 This adds --status-fd and --command-fd options to the show and reply\r
50 commands which are used to specify a file descriptor for communicating\r
51 with notmuch as it is running. Notmuch will write status messages to\r
52 the status-fd and expect responses to them on the command-fd. These\r
53 options are inspired by similar options for controlling gpg.\r
54 \r
55 Currently the only use for this is to have a way for the CLI program\r
56 to query passwords from the calling application. When the client needs\r
57 a password it will write a string in the following format to the\r
58 status-fd:\r
59 \r
60 [NOTMUCH:] GET_HIDDEN <user_id> <prompt> <reprompt>\r
61 \r
62 The user_id and prompt are string arguments that are encoded like C\r
63 strings. Ie, they will be surrounded by "quotes" and will contain\r
64 backslash escape sequences. These can also be parsed directly by the\r
65 read function in Emacs. The last argument is either a 1 or 0 to\r
66 specify whether the password is being prompted for a second time.\r
67 \r
68 When the application sees this status it is expected to write the\r
69 password followed by a newline to the command-fd.\r
70 ---\r
71  crypto.c         | 184 +++++++++++++++++++++++++++++++++++++++++++++++++++----\r
72  notmuch-client.h |   2 +\r
73  notmuch-reply.c  |   6 +-\r
74  notmuch-show.c   |   6 +-\r
75  4 files changed, 185 insertions(+), 13 deletions(-)\r
76 \r
77 diff --git a/crypto.c b/crypto.c\r
78 index 9736517..eded53f 100644\r
79 --- a/crypto.c\r
80 +++ b/crypto.c\r
81 @@ -22,28 +22,188 @@\r
82  \r
83  #ifdef GMIME_ATLEAST_26\r
84  \r
85 +#define NOTMUCH_PASSWORD_DATA_KEY "notmuch-password-data"\r
86 +\r
87 +typedef struct\r
88 +{\r
89 +    int status_fd;\r
90 +    GIOChannel *command_channel;\r
91 +} notmuch_password_data_t;\r
92 +\r
93 +static void\r
94 +free_password_data_cb (void *data_ptr)\r
95 +{\r
96 +    notmuch_password_data_t *data = data_ptr;\r
97 +\r
98 +    g_io_channel_unref (data->command_channel);\r
99 +    free (data);\r
100 +}\r
101 +\r
102 +static void\r
103 +escape_string (GString *buf,\r
104 +              const char *str)\r
105 +{\r
106 +    const char *p;\r
107 +\r
108 +    g_string_append_c (buf, '"');\r
109 +\r
110 +    for (p = str; *p; p++)\r
111 +       switch (*p) {\r
112 +       case '\n':\r
113 +           g_string_append (buf, "\\n");\r
114 +           break;\r
115 +       case '\t':\r
116 +           g_string_append (buf, "\\t");\r
117 +           break;\r
118 +       case '\r':\r
119 +           g_string_append (buf, "\\t");\r
120 +           break;\r
121 +       case '"':\r
122 +           g_string_append (buf, "\\\"");\r
123 +           break;\r
124 +       default:\r
125 +           if (*p < 32)\r
126 +               g_string_append_printf (buf, "\\0%o", *p);\r
127 +           else\r
128 +               g_string_append_c (buf, *p);\r
129 +           break;\r
130 +       }\r
131 +\r
132 +    g_string_append_c (buf, '"');\r
133 +}\r
134 +\r
135 +static gboolean\r
136 +write_all (const char *buf,\r
137 +          size_t length,\r
138 +          int fd)\r
139 +{\r
140 +    ssize_t wrote;\r
141 +\r
142 +    while (length > 0) {\r
143 +       wrote = write (fd, buf, length);\r
144 +       if (wrote == -1)\r
145 +           return FALSE;\r
146 +       buf += wrote;\r
147 +       length -= wrote;\r
148 +    }\r
149 +\r
150 +    return TRUE;\r
151 +}\r
152 +\r
153 +static gboolean\r
154 +password_request_cb (GMimeCryptoContext *ctx,\r
155 +                    const char *user_id,\r
156 +                    const char *prompt_ctx,\r
157 +                    gboolean reprompt,\r
158 +                    GMimeStream *response,\r
159 +                    GError **err)\r
160 +{\r
161 +    notmuch_password_data_t *data;\r
162 +    GString *buf;\r
163 +    gboolean write_result;\r
164 +    char *line = NULL;\r
165 +    gsize line_length;\r
166 +    ssize_t wrote;\r
167 +\r
168 +    data = g_object_get_data (G_OBJECT (ctx), NOTMUCH_PASSWORD_DATA_KEY);\r
169 +\r
170 +    buf = g_string_new ("[NOTMUCH:] GET_HIDDEN ");\r
171 +    escape_string (buf, user_id);\r
172 +    g_string_append_c (buf, ' ');\r
173 +    escape_string (buf, prompt_ctx);\r
174 +    g_string_append_c (buf, ' ');\r
175 +    g_string_append_c (buf, (!!reprompt) + '0');\r
176 +    g_string_append_c (buf, '\n');\r
177 +\r
178 +    write_result = write_all (buf->str, buf->len, data->status_fd);\r
179 +\r
180 +    g_string_free (buf, TRUE);\r
181 +\r
182 +    if (!write_result) {\r
183 +       g_set_error_literal (err,\r
184 +                            G_FILE_ERROR,\r
185 +                            g_file_error_from_errno (errno),\r
186 +                            strerror (errno));\r
187 +       return FALSE;\r
188 +    }\r
189 +\r
190 +    if (!g_io_channel_read_line (data->command_channel,\r
191 +                                &line,\r
192 +                                &line_length,\r
193 +                                NULL, /* terminator_pos */\r
194 +                                err))\r
195 +       return FALSE;\r
196 +\r
197 +    wrote = g_mime_stream_write (response, line, line_length);\r
198 +\r
199 +    /* TODO: is there a more reliable way to clear the memory\r
200 +     * containing a password? */\r
201 +    memset (line, 0, line_length);\r
202 +\r
203 +    g_free (line);\r
204 +\r
205 +    if (wrote < (ssize_t) line_length) {\r
206 +       g_set_error_literal (err,\r
207 +                            G_FILE_ERROR,\r
208 +                            G_FILE_ERROR_FAILED,\r
209 +                            "Failed to write password response");\r
210 +       return FALSE;\r
211 +    }\r
212 +\r
213 +    return TRUE;\r
214 +}\r
215 +\r
216  /* Create a GPG context (GMime 2.6) */\r
217 -static notmuch_crypto_context_t *\r
218 -create_gpg_context (void)\r
219 +static notmuch_bool_t\r
220 +create_gpg_context (notmuch_crypto_t *crypto)\r
221  {\r
222      notmuch_crypto_context_t *gpgctx;\r
223 +    GMimePasswordRequestFunc password_func;\r
224 +    notmuch_password_data_t *password_data;\r
225 +\r
226 +    /* If the --status-fd and --command-fd options were specified then\r
227 +     * we can handle password requests by forwarding them on to the\r
228 +     * application that invoked notmuch */\r
229 +    if (crypto->status_fd != -1 && crypto->command_fd != -1) {\r
230 +       password_func = password_request_cb;\r
231 +       password_data = malloc (sizeof (*password_data));\r
232 +       if (password_data == NULL)\r
233 +           return FALSE;\r
234 +       password_data->status_fd = crypto->status_fd;\r
235 +       password_data->command_channel =\r
236 +           g_io_channel_unix_new (crypto->command_fd);\r
237 +    } else {\r
238 +       password_func = NULL;\r
239 +       password_data = NULL;\r
240 +    }\r
241  \r
242 -    /* TODO: GMimePasswordRequestFunc */\r
243 -    gpgctx = g_mime_gpg_context_new (NULL, "gpg");\r
244 +    gpgctx = g_mime_gpg_context_new (password_func, "gpg");\r
245      if (! gpgctx)\r
246 -       return NULL;\r
247 +       return FALSE;\r
248 +\r
249 +    /* The password callback for GMime doesn't seem to provide any\r
250 +     * user data pointer so we'll work around it by attaching the data\r
251 +     * to the gpg context, which it does pass */\r
252 +    if (password_data) {\r
253 +       g_object_set_data_full (G_OBJECT (gpgctx),\r
254 +                               NOTMUCH_PASSWORD_DATA_KEY,\r
255 +                               password_data,\r
256 +                               free_password_data_cb);\r
257 +    }\r
258  \r
259      g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE);\r
260      g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);\r
261  \r
262 -    return gpgctx;\r
263 +    crypto->gpgctx = gpgctx;\r
264 +\r
265 +    return TRUE;\r
266  }\r
267  \r
268  #else /* GMIME_ATLEAST_26 */\r
269  \r
270  /* Create a GPG context (GMime 2.4) */\r
271 -static notmuch_crypto_context_t *\r
272 -create_gpg_context (void)\r
273 +static notmuch_bool_t\r
274 +create_gpg_context (notmuch_crypto_t *crypto)\r
275  {\r
276      GMimeSession *session;\r
277      notmuch_crypto_context_t *gpgctx;\r
278 @@ -53,11 +213,13 @@ create_gpg_context (void)\r
279      g_object_unref (session);\r
280  \r
281      if (! gpgctx)\r
282 -       return NULL;\r
283 +       return FALSE;\r
284  \r
285      g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);\r
286  \r
287 -    return gpgctx;\r
288 +    crypto->gpgctx = gpgctx;\r
289 +\r
290 +    return TRUE;\r
291  }\r
292  \r
293  #endif /* GMIME_ATLEAST_26 */\r
294 @@ -78,7 +240,7 @@ notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)\r
295      if (strcasecmp (protocol, "application/pgp-signature") == 0 ||\r
296         strcasecmp (protocol, "application/pgp-encrypted") == 0) {\r
297         if (! crypto->gpgctx) {\r
298 -           crypto->gpgctx = create_gpg_context ();\r
299 +           create_gpg_context (crypto);\r
300             if (! crypto->gpgctx)\r
301                 fprintf (stderr, "Failed to construct gpg context.\n");\r
302         }\r
303 diff --git a/notmuch-client.h b/notmuch-client.h\r
304 index 5f2a6d0..edf47ce 100644\r
305 --- a/notmuch-client.h\r
306 +++ b/notmuch-client.h\r
307 @@ -80,6 +80,8 @@ typedef struct notmuch_crypto {\r
308      notmuch_crypto_context_t* gpgctx;\r
309      notmuch_bool_t verify;\r
310      notmuch_bool_t decrypt;\r
311 +    int status_fd;\r
312 +    int command_fd;\r
313  } notmuch_crypto_t;\r
314  \r
315  typedef struct notmuch_show_params {\r
316 diff --git a/notmuch-reply.c b/notmuch-reply.c\r
317 index e151f78..d46bec4 100644\r
318 --- a/notmuch-reply.c\r
319 +++ b/notmuch-reply.c\r
320 @@ -718,7 +718,9 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])\r
321         .part = -1,\r
322         .crypto = {\r
323             .verify = FALSE,\r
324 -           .decrypt = FALSE\r
325 +           .decrypt = FALSE,\r
326 +           .status_fd = -1,\r
327 +           .command_fd = -1\r
328         }\r
329      };\r
330      int format = FORMAT_DEFAULT;\r
331 @@ -738,6 +740,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])\r
332                                   { "sender", FALSE },\r
333                                   { 0, 0 } } },\r
334         { NOTMUCH_OPT_BOOLEAN, &params.crypto.decrypt, "decrypt", 'd', 0 },\r
335 +       { NOTMUCH_OPT_INT, &params.crypto.status_fd, "status-fd", 0, 0 },\r
336 +       { NOTMUCH_OPT_INT, &params.crypto.command_fd, "command-fd", 0, 0 },\r
337         { 0, 0, 0, 0, 0 }\r
338      };\r
339  \r
340 diff --git a/notmuch-show.c b/notmuch-show.c\r
341 index 62178f7..0a67807 100644\r
342 --- a/notmuch-show.c\r
343 +++ b/notmuch-show.c\r
344 @@ -1076,7 +1076,9 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])\r
345         .output_body = TRUE,\r
346         .crypto = {\r
347             .verify = FALSE,\r
348 -           .decrypt = FALSE\r
349 +           .decrypt = FALSE,\r
350 +           .status_fd = -1,\r
351 +           .command_fd = -1\r
352         }\r
353      };\r
354      int format_sel = NOTMUCH_FORMAT_NOT_SPECIFIED;\r
355 @@ -1104,6 +1106,8 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])\r
356         { NOTMUCH_OPT_INT, &params.part, "part", 'p', 0 },\r
357         { NOTMUCH_OPT_BOOLEAN, &params.crypto.decrypt, "decrypt", 'd', 0 },\r
358         { NOTMUCH_OPT_BOOLEAN, &params.crypto.verify, "verify", 'v', 0 },\r
359 +       { NOTMUCH_OPT_INT, &params.crypto.status_fd, "status-fd", 0, 0 },\r
360 +       { NOTMUCH_OPT_INT, &params.crypto.command_fd, "command-fd", 0, 0 },\r
361         { NOTMUCH_OPT_BOOLEAN, &params.output_body, "body", 'b', 0 },\r
362         { 0, 0, 0, 0, 0 }\r
363      };\r
364 -- \r
365 1.7.11.3.g3c3efa5\r
366 \r