| skipped 29 lines |
30 | 30 | | * @page crypt_crypt_gpgme Wrapper for PGP/SMIME calls to GPGME |
31 | 31 | | * |
32 | 32 | | * Wrapper for PGP/SMIME calls to GPGME |
33 | | - | * |
34 | | - | * Some code is build conditionally according to the version of the GPGME library. |
35 | | - | * This table relates the hex GPGME_VERSION_NUMBER to its age: |
36 | | - | * |
37 | | - | * | Version | Hex | Release Date | |
38 | | - | * | :------ | :------- | :----------- | |
39 | | - | * | 1.8.0 | 0x010800 | 2016-11-16 | |
40 | | - | * | 1.9.0 | 0x010900 | 2017-03-28 | |
41 | | - | * | 1.11.0 | 0x010b00 | 2018-04-18 | |
42 | 33 | | */ |
43 | 34 | | |
44 | 35 | | #include "config.h" |
| skipped 84 lines |
129 | 120 | | */ |
130 | 121 | | static void redraw_if_needed(gpgme_ctx_t ctx) |
131 | 122 | | { |
132 | | - | #if (GPGME_VERSION_NUMBER < 0x010800) // GPGME < 1.8.0 |
133 | | - | /* gpgme_get_ctx_flag is not available. |
134 | | - | * In this case, stay on the safe side and always redraw. */ |
135 | | - | (void) ctx; |
136 | | - | mutt_need_hard_redraw(); |
137 | | - | #else |
138 | 123 | | const char *s = gpgme_get_ctx_flag(ctx, "redraw"); |
139 | 124 | | if (!s /* flag not known */ || *s /* flag true */) |
140 | 125 | | { |
141 | 126 | | mutt_need_hard_redraw(); |
142 | 127 | | } |
143 | | - | #endif |
144 | 128 | | } |
145 | 129 | | |
146 | | - | #if (GPGRT_VERSION_NUMBER >= 0x012100) // libgpg-error >= 1.33 |
147 | | - | /** |
148 | | - | * cmp_version_strings - Compare version strings |
149 | | - | * @param a First version string |
150 | | - | * @param b Second version string |
151 | | - | * @param level Level to compare (see below) |
152 | | - | * @retval -1 a precedes b |
153 | | - | * @retval 0 a and b are identical |
154 | | - | * @retval 1 b precedes a |
155 | | - | * |
156 | | - | * `level` may be |
157 | | - | * - 0 reserved |
158 | | - | * - 1 format is "<major><patchlevel>" |
159 | | - | * - 2 format is "<major>.<minor><patchlevel>" |
160 | | - | * - 3 format is "<major>.<minor>.<micro><patchlevel>" |
161 | | - | * |
162 | | - | * To ignore the patchlevel in the comparison add 10 to LEVEL. |
163 | | - | * To get a reverse sorting order use a negative number. |
164 | | - | */ |
165 | | - | static int cmp_version_strings(const char *a, const char *b, int level) |
166 | | - | { |
167 | | - | return gpgrt_cmp_version(a, b, level); |
168 | | - | } |
169 | | - | #elif (GPGME_VERSION_NUMBER >= 0x010900) // GPGME >= 1.9.0 |
170 | | - | |
171 | | - | /** |
172 | | - | * parse_version_number - Parse a version string |
173 | | - | * @param[in] s String to parse |
174 | | - | * @param[out] number |
175 | | - | * @retval ptr Remainder of the string |
176 | | - | * @retval NULL Error |
177 | | - | * |
178 | | - | * This function parses the first portion of the version number S and |
179 | | - | * stores it at NUMBER. On success, this function returns a pointer |
180 | | - | * into S starting with the first character, which is not part of the |
181 | | - | * initial number portion; on failure, NULL is returned. |
182 | | - | */ |
183 | | - | static const char *parse_version_number(const char *s, int *number) |
184 | | - | { |
185 | | - | int val = 0; |
186 | | - | |
187 | | - | if ((*s == '0') && isdigit(s[1])) |
188 | | - | return NULL; /* Leading zeros are not allowed. */ |
189 | | - | for (; isdigit(s[0]); s++) |
190 | | - | { |
191 | | - | val *= 10; |
192 | | - | val += *s - '0'; |
193 | | - | } |
194 | | - | *number = val; |
195 | | - | return (val < 0) ? NULL : s; |
196 | | - | } |
197 | | - | |
198 | | - | /** |
199 | | - | * parse_version_string - Parse a version string |
200 | | - | * @param s String to parse |
201 | | - | * @param major Version MAJOR.x.x |
202 | | - | * @param minor Version x.MINOR.x |
203 | | - | * @param micro Version x.x.MICRO |
204 | | - | * @retval ptr Patch level string |
205 | | - | * @retval NULL There are fewer parts |
206 | | - | * |
207 | | - | * Break up the complete string-representation of the version number S, which |
208 | | - | * is of the following structure: <major number>.<minor number>.<micro |
209 | | - | * number><patch level>. The major, minor and micro number components will be |
210 | | - | * stored in *MAJOR, *MINOR and *MICRO. If MINOR or MICRO is NULL the version |
211 | | - | * number is assumed to have just 1 or 2 parts respectively. |
212 | | - | * |
213 | | - | * On success, the last component, the patch level, will be returned; |
214 | | - | * in failure, NULL will be returned. |
215 | | - | */ |
216 | | - | static const char *parse_version_string(const char *s, int *major, int *minor, int *micro) |
217 | | - | { |
218 | | - | s = parse_version_number(s, major); |
219 | | - | if (!s) |
220 | | - | return NULL; |
221 | | - | if (minor) |
222 | | - | { |
223 | | - | if (*s != '.') |
224 | | - | return NULL; |
225 | | - | s++; |
226 | | - | s = parse_version_number(s, minor); |
227 | | - | if (!s) |
228 | | - | return NULL; |
229 | | - | if (micro) |
230 | | - | { |
231 | | - | if (*s != '.') |
232 | | - | return NULL; |
233 | | - | s++; |
234 | | - | s = parse_version_number(s, micro); |
235 | | - | if (!s) |
236 | | - | return NULL; |
237 | | - | } |
238 | | - | else |
239 | | - | { |
240 | | - | if (*s == '.') |
241 | | - | s++; |
242 | | - | } |
243 | | - | } |
244 | | - | else |
245 | | - | { |
246 | | - | if (*s == '.') |
247 | | - | s++; |
248 | | - | } |
249 | | - | return s; /* patchlevel */ |
250 | | - | } |
251 | | - | |
252 | | - | /** |
253 | | - | * cmp_version_strings - Compare two version strings |
254 | | - | * @param a First version string |
255 | | - | * @param b Second version string |
256 | | - | * @param level Level to compare |
257 | | - | * |
258 | | - | * Substitute for the gpgrt based implementation. |
259 | | - | * See above for a description. |
260 | | - | */ |
261 | | - | static int cmp_version_strings(const char *a, const char *b, int level) |
262 | | - | { |
263 | | - | int a_major, a_minor, a_micro; |
264 | | - | int b_major, b_minor, b_micro; |
265 | | - | const char *a_plvl = NULL, *b_plvl = NULL; |
266 | | - | int r; |
267 | | - | int ignore_plvl; |
268 | | - | int positive, negative; |
269 | | - | |
270 | | - | if (level < 0) |
271 | | - | { |
272 | | - | positive = -1; |
273 | | - | negative = 1; |
274 | | - | level = 0 - level; |
275 | | - | } |
276 | | - | else |
277 | | - | { |
278 | | - | positive = 1; |
279 | | - | negative = -1; |
280 | | - | } |
281 | | - | if ((ignore_plvl = (level > 9))) |
282 | | - | level %= 10; |
283 | | - | |
284 | | - | a_major = a_minor = a_micro = 0; |
285 | | - | a_plvl = parse_version_string(a, &a_major, (level > 1) ? &a_minor : NULL, |
286 | | - | (level > 2) ? &a_micro : NULL); |
287 | | - | if (!a_plvl) |
288 | | - | a_major = a_minor = a_micro = 0; /* Error. */ |
289 | | - | |
290 | | - | b_major = b_minor = b_micro = 0; |
291 | | - | b_plvl = parse_version_string(b, &b_major, (level > 1) ? &b_minor : NULL, |
292 | | - | (level > 2) ? &b_micro : NULL); |
293 | | - | if (!b_plvl) |
294 | | - | b_major = b_minor = b_micro = 0; |
295 | | - | |
296 | | - | if (!ignore_plvl) |
297 | | - | { |
298 | | - | if (!a_plvl && !b_plvl) |
299 | | - | return negative; /* Put invalid strings at the end. */ |
300 | | - | if (a_plvl && !b_plvl) |
301 | | - | return positive; |
302 | | - | if (!a_plvl && b_plvl) |
303 | | - | return negative; |
304 | | - | } |
305 | | - | |
306 | | - | if (a_major > b_major) |
307 | | - | return positive; |
308 | | - | if (a_major < b_major) |
309 | | - | return negative; |
310 | | - | |
311 | | - | if (a_minor > b_minor) |
312 | | - | return positive; |
313 | | - | if (a_minor < b_minor) |
314 | | - | return negative; |
315 | | - | |
316 | | - | if (a_micro > b_micro) |
317 | | - | return positive; |
318 | | - | if (a_micro < b_micro) |
319 | | - | return negative; |
320 | | - | |
321 | | - | if (ignore_plvl) |
322 | | - | return 0; |
323 | | - | |
324 | | - | for (; *a_plvl && *b_plvl; a_plvl++, b_plvl++) |
325 | | - | { |
326 | | - | if ((*a_plvl == '.') && (*b_plvl == '.')) |
327 | | - | { |
328 | | - | r = strcmp(a_plvl, b_plvl); |
329 | | - | if (!r) |
330 | | - | return 0; |
331 | | - | if (r > 0) |
332 | | - | return positive; |
333 | | - | return negative; |
334 | | - | } |
335 | | - | if (*a_plvl == '.') |
336 | | - | return negative; /* B is larger. */ |
337 | | - | if (*b_plvl == '.') |
338 | | - | return positive; /* A is larger. */ |
339 | | - | if (*a_plvl != *b_plvl) |
340 | | - | break; |
341 | | - | } |
342 | | - | if (*a_plvl == *b_plvl) |
343 | | - | return 0; |
344 | | - | if ((*(signed char *) a_plvl - *(signed char *) b_plvl) > 0) |
345 | | - | return positive; |
346 | | - | return negative; |
347 | | - | } |
348 | | - | #endif |
349 | | - | |
350 | 130 | | /** |
351 | 131 | | * crypt_keyid - Find the ID for the key |
352 | 132 | | * @param k Key to use |
| skipped 278 lines |
631 | 411 | | return data; |
632 | 412 | | } |
633 | 413 | | |
634 | | - | #if (GPGME_VERSION_NUMBER >= 0x010900) // GPGME >= 1.9.0 |
635 | | - | /** |
636 | | - | * have_gpg_version - Do we have a sufficient GPG version |
637 | | - | * @param version Minimum version |
638 | | - | * @retval true Minimum version is available |
639 | | - | * |
640 | | - | * Return true if the OpenPGP engine's version is at least VERSION. |
641 | | - | */ |
642 | | - | static bool have_gpg_version(const char *version) |
643 | | - | { |
644 | | - | static char *engine_version = NULL; |
645 | | - | |
646 | | - | if (!engine_version) |
647 | | - | { |
648 | | - | gpgme_ctx_t ctx = NULL; |
649 | | - | gpgme_engine_info_t engineinfo = NULL; |
650 | | - | |
651 | | - | ctx = create_gpgme_context(false); |
652 | | - | engineinfo = gpgme_ctx_get_engine_info(ctx); |
653 | | - | while (engineinfo && (engineinfo->protocol != GPGME_PROTOCOL_OpenPGP)) |
654 | | - | engineinfo = engineinfo->next; |
655 | | - | if (engineinfo) |
656 | | - | { |
657 | | - | engine_version = mutt_str_dup(engineinfo->version); |
658 | | - | } |
659 | | - | else |
660 | | - | { |
661 | | - | mutt_debug(LL_DEBUG1, "Error finding GPGME PGP engine\n"); |
662 | | - | engine_version = mutt_str_dup("0.0.0"); |
663 | | - | } |
664 | | - | gpgme_release(ctx); |
665 | | - | } |
666 | | - | |
667 | | - | return cmp_version_strings(engine_version, version, 3) >= 0; |
668 | | - | } |
669 | | - | #endif |
670 | | - | |
671 | 414 | | /** |
672 | 415 | | * body_to_data_object - Create GPGME object from the mail body |
673 | 416 | | * @param a Body to use |
| skipped 191 lines |
865 | 608 | | return rv; |
866 | 609 | | } |
867 | 610 | | |
868 | | - | #if (GPGME_VERSION_NUMBER >= 0x010b00) // GPGME >= 1.11.0 |
869 | 611 | | /** |
870 | 612 | | * create_recipient_string - Create a string of recipients |
871 | 613 | | * @param keylist Keys, space-separated |
| skipped 26 lines |
898 | 640 | | } while (*s != '\0'); |
899 | 641 | | } |
900 | 642 | | |
901 | | - | #else |
902 | | - | /** |
903 | | - | * recipient_set_free - Free a set of recipients |
904 | | - | * @param p_rset Set of GPGME keys |
905 | | - | */ |
906 | | - | static void recipient_set_free(gpgme_key_t **p_rset) |
907 | | - | { |
908 | | - | gpgme_key_t *rset = NULL; |
909 | | - | |
910 | | - | if (!p_rset) |
911 | | - | return; |
912 | | - | |
913 | | - | rset = *p_rset; |
914 | | - | if (!rset) |
915 | | - | return; |
916 | | - | |
917 | | - | while (*rset) |
918 | | - | { |
919 | | - | gpgme_key_t k = *rset; |
920 | | - | gpgme_key_unref(k); |
921 | | - | rset++; |
922 | | - | } |
923 | | - | |
924 | | - | FREE(p_rset); |
925 | | - | } |
926 | | - | |
927 | | - | /** |
928 | | - | * create_recipient_set - Create a GpgmeRecipientSet from a string of keys |
929 | | - | * @param keylist Keys, space-separated |
930 | | - | * @param use_smime Use SMIME |
931 | | - | * @retval ptr GPGME key set |
932 | | - | */ |
933 | | - | static gpgme_key_t *create_recipient_set(const char *keylist, bool use_smime) |
934 | | - | { |
935 | | - | int err; |
936 | | - | const char *s = NULL; |
937 | | - | char buf[100] = { 0 }; |
938 | | - | gpgme_key_t *rset = NULL; |
939 | | - | unsigned int rset_n = 0; |
940 | | - | gpgme_key_t key = NULL; |
941 | | - | |
942 | | - | gpgme_ctx_t context = create_gpgme_context(use_smime); |
943 | | - | s = keylist; |
944 | | - | do |
945 | | - | { |
946 | | - | while (*s == ' ') |
947 | | - | s++; |
948 | | - | int i; |
949 | | - | for (i = 0; *s && *s != ' ' && i < sizeof(buf) - 1;) |
950 | | - | buf[i++] = *s++; |
951 | | - | buf[i] = '\0'; |
952 | | - | if (*buf != '\0') |
953 | | - | { |
954 | | - | if ((i > 1) && (buf[i - 1] == '!')) |
955 | | - | { |
956 | | - | /* The user selected to override the validity of that key. */ |
957 | | - | buf[i - 1] = '\0'; |
958 | | - | |
959 | | - | err = gpgme_get_key(context, buf, &key, 0); |
960 | | - | if (err == 0) |
961 | | - | key->uids->validity = GPGME_VALIDITY_FULL; |
962 | | - | buf[i - 1] = '!'; |
963 | | - | } |
964 | | - | else |
965 | | - | err = gpgme_get_key(context, buf, &key, 0); |
966 | | - | mutt_mem_realloc(&rset, sizeof(*rset) * (rset_n + 1)); |
967 | | - | if (err == 0) |
968 | | - | rset[rset_n++] = key; |
969 | | - | else |
970 | | - | { |
971 | | - | mutt_error(_("error adding recipient '%s': %s"), buf, gpgme_strerror(err)); |
972 | | - | rset[rset_n] = NULL; |
973 | | - | recipient_set_free(&rset); |
974 | | - | gpgme_release(context); |
975 | | - | return NULL; |
976 | | - | } |
977 | | - | } |
978 | | - | } while (*s); |
979 | | - | |
980 | | - | /* NULL terminate. */ |
981 | | - | mutt_mem_realloc(&rset, sizeof(*rset) * (rset_n + 1)); |
982 | | - | rset[rset_n++] = NULL; |
983 | | - | |
984 | | - | gpgme_release(context); |
985 | | - | |
986 | | - | return rset; |
987 | | - | } |
988 | | - | #endif |
989 | | - | |
990 | 643 | | /** |
991 | 644 | | * set_signer_from_address - Try to set the context's signer from the address |
992 | 645 | | * @param ctx GPGME handle |
| skipped 132 lines |
1125 | 778 | | gpgme_data_t ciphertext = NULL; |
1126 | 779 | | char *outfile = NULL; |
1127 | 780 | | |
1128 | | - | #if (GPGME_VERSION_NUMBER >= 0x010b00) // GPGME >= 1.11.0 |
1129 | 781 | | struct Buffer *recpstring = mutt_buffer_pool_get(); |
1130 | 782 | | create_recipient_string(keylist, recpstring, use_smime); |
1131 | 783 | | if (mutt_buffer_is_empty(recpstring)) |
| skipped 1 lines |
1133 | 785 | | mutt_buffer_pool_release(&recpstring); |
1134 | 786 | | return NULL; |
1135 | 787 | | } |
1136 | | - | #else |
1137 | | - | gpgme_key_t *rset = create_recipient_set(keylist, use_smime); |
1138 | | - | if (!rset) |
1139 | | - | return NULL; |
1140 | | - | #endif |
1141 | 788 | | |
1142 | 789 | | ctx = create_gpgme_context(use_smime); |
1143 | 790 | | if (!use_smime) |
| skipped 14 lines |
1158 | 805 | | goto cleanup; |
1159 | 806 | | } |
1160 | 807 | | |
1161 | | - | #if (GPGME_VERSION_NUMBER >= 0x010b00) // GPGME >= 1.11.0 |
1162 | 808 | | err = gpgme_op_encrypt_sign_ext(ctx, NULL, mutt_buffer_string(recpstring), |
1163 | 809 | | GPGME_ENCRYPT_ALWAYS_TRUST, plaintext, ciphertext); |
1164 | | - | #else |
1165 | | - | err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST, plaintext, ciphertext); |
1166 | | - | #endif |
1167 | 810 | | } |
1168 | 811 | | else |
1169 | 812 | | { |
1170 | | - | #if (GPGME_VERSION_NUMBER >= 0x010b00) // GPGME >= 1.11.0 |
1171 | 813 | | err = gpgme_op_encrypt_ext(ctx, NULL, mutt_buffer_string(recpstring), |
1172 | 814 | | GPGME_ENCRYPT_ALWAYS_TRUST, plaintext, ciphertext); |
1173 | | - | #else |
1174 | | - | err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST, plaintext, ciphertext); |
1175 | | - | #endif |
1176 | 815 | | } |
1177 | 816 | | |
1178 | 817 | | redraw_if_needed(ctx); |
| skipped 6 lines |
1185 | 824 | | outfile = data_object_to_tempfile(ciphertext, NULL); |
1186 | 825 | | |
1187 | 826 | | cleanup: |
1188 | | - | #if (GPGME_VERSION_NUMBER >= 0x010b00) // GPGME >= 1.11.0 |
1189 | 827 | | mutt_buffer_pool_release(&recpstring); |
1190 | | - | #else |
1191 | | - | recipient_set_free(&rset); |
1192 | | - | #endif |
1193 | 828 | | gpgme_release(ctx); |
1194 | 829 | | gpgme_data_release(ciphertext); |
1195 | 830 | | return outfile; |
| skipped 1228 lines |
2424 | 2059 | | */ |
2425 | 2060 | | static int pgp_gpgme_extract_keys(gpgme_data_t keydata, FILE **fp) |
2426 | 2061 | | { |
2427 | | - | /* Before GPGME 1.9.0 and gpg 2.1.14 there was no side-effect free |
2428 | | - | * way to view key data in GPGME, so we import the key into a |
2429 | | - | * temporary keyring if we detect an older system. */ |
2430 | | - | bool legacy_api; |
2431 | | - | struct Buffer *tmpdir = NULL; |
2432 | 2062 | | gpgme_ctx_t tmpctx = NULL; |
2433 | 2063 | | gpgme_error_t err; |
2434 | | - | gpgme_engine_info_t engineinfo = NULL; |
2435 | 2064 | | gpgme_key_t key = NULL; |
2436 | 2065 | | gpgme_user_id_t uid = NULL; |
2437 | 2066 | | gpgme_subkey_t subkey = NULL; |
| skipped 4 lines |
2442 | 2071 | | int rc = -1; |
2443 | 2072 | | time_t tt; |
2444 | 2073 | | |
2445 | | - | #if (GPGME_VERSION_NUMBER >= 0x010900) // GPGME >= 1.9.0 |
2446 | | - | legacy_api = !have_gpg_version("2.1.14"); |
2447 | | - | #else |
2448 | | - | legacy_api = true; |
2449 | | - | #endif |
2450 | | - | |
2451 | | - | tmpctx = create_gpgme_context(false); |
2452 | | - | |
2453 | | - | if (legacy_api) |
2454 | | - | { |
2455 | | - | tmpdir = mutt_buffer_pool_get(); |
2456 | | - | const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir"); |
2457 | | - | mutt_buffer_printf(tmpdir, "%s/neomutt-gpgme-XXXXXX", NONULL(c_tmpdir)); |
2458 | | - | if (!mkdtemp(tmpdir->data)) |
2459 | | - | { |
2460 | | - | mutt_debug(LL_DEBUG1, "Error creating temporary GPGME home\n"); |
2461 | | - | goto err_ctx; |
2462 | | - | } |
2463 | | - | |
2464 | | - | engineinfo = gpgme_ctx_get_engine_info(tmpctx); |
2465 | | - | while (engineinfo && (engineinfo->protocol != GPGME_PROTOCOL_OpenPGP)) |
2466 | | - | engineinfo = engineinfo->next; |
2467 | | - | if (!engineinfo) |
2468 | | - | { |
2469 | | - | mutt_debug(LL_DEBUG1, "Error finding GPGME PGP engine\n"); |
2470 | | - | goto err_tmpdir; |
2471 | | - | } |
2472 | | - | |
2473 | | - | err = gpgme_ctx_set_engine_info(tmpctx, GPGME_PROTOCOL_OpenPGP, engineinfo->file_name, |
2474 | | - | mutt_buffer_string(tmpdir)); |
2475 | | - | if (err != GPG_ERR_NO_ERROR) |
2476 | | - | { |
2477 | | - | mutt_debug(LL_DEBUG1, "Error setting GPGME context home\n"); |
2478 | | - | goto err_tmpdir; |
2479 | | - | } |
2480 | | - | } |
2481 | | - | |
2482 | 2074 | | *fp = mutt_file_mkstemp(); |
2483 | 2075 | | if (!*fp) |
2484 | 2076 | | { |
2485 | 2077 | | mutt_perror(_("Can't create temporary file")); |
2486 | | - | goto err_tmpdir; |
| 2078 | + | return -1; |
2487 | 2079 | | } |
2488 | 2080 | | |
2489 | | - | #if (GPGME_VERSION_NUMBER >= 0x010900) // GPGME >= 1.9.0 |
2490 | | - | if (!legacy_api) |
2491 | | - | err = gpgme_op_keylist_from_data_start(tmpctx, keydata, 0); |
2492 | | - | else |
2493 | | - | #endif |
2494 | | - | { |
2495 | | - | err = gpgme_op_keylist_start(tmpctx, NULL, 0); |
2496 | | - | } |
| 2081 | + | tmpctx = create_gpgme_context(false); |
| 2082 | + | |
| 2083 | + | err = gpgme_op_keylist_from_data_start(tmpctx, keydata, 0); |
2497 | 2084 | | while (err == 0) |
2498 | 2085 | | { |
2499 | 2086 | | err = gpgme_op_keylist_next(tmpctx, &key); |
| skipped 37 lines |
2537 | 2124 | | err_fp: |
2538 | 2125 | | if (rc) |
2539 | 2126 | | mutt_file_fclose(fp); |
2540 | | - | err_tmpdir: |
2541 | | - | if (legacy_api) |
2542 | | - | mutt_file_rmtree(mutt_buffer_string(tmpdir)); |
2543 | | - | err_ctx: |
| 2127 | + | |
2544 | 2128 | | gpgme_release(tmpctx); |
2545 | | - | |
2546 | | - | mutt_buffer_pool_release(&tmpdir); |
2547 | 2129 | | |
2548 | 2130 | | return rc; |
2549 | 2131 | | } |
| skipped 1897 lines |