g10: Add new delete operations that allow more flags.
[gpgme.git] / src / passwd.c
1 /* passwd.c - Passphrase changing function
2    Copyright (C) 2010 g10 Code GmbH
3
4    This file is part of GPGME.
5
6    GPGME is free software; you can redistribute it and/or modify it
7    under the terms of the GNU Lesser General Public License as
8    published by the Free Software Foundation; either version 2.1 of
9    the License, or (at your option) any later version.
10
11    GPGME is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdlib.h>
24
25 #include "gpgme.h"
26 #include "debug.h"
27 #include "context.h"
28 #include "ops.h"
29
30
31 typedef struct
32 {
33   /* The error code from a FAILURE status line or 0.  */
34   gpg_error_t failure_code;
35
36   int success_seen;
37   int error_seen;
38 } *op_data_t;
39
40
41 \f
42 /* Parse an error status line and return the error code.  */
43 static gpgme_error_t
44 parse_error (char *args)
45 {
46   gpgme_error_t err;
47   char *where = strchr (args, ' ');
48   char *which;
49
50   if (where)
51     {
52       *where = '\0';
53       which = where + 1;
54
55       where = strchr (which, ' ');
56       if (where)
57         *where = '\0';
58
59       where = args;
60     }
61   else
62     return trace_gpg_error (GPG_ERR_INV_ENGINE);
63
64   err = atoi (which);
65
66   if (!strcmp (where, "keyedit.passwd"))
67     return err;
68
69   return 0;
70 }
71
72
73 static gpgme_error_t
74 passwd_status_handler (void *priv, gpgme_status_code_t code, char *args)
75 {
76   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
77   gpgme_error_t err;
78   void *hook;
79   op_data_t opd;
80
81   err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, -1, NULL);
82   opd = hook;
83   if (err)
84     return err;
85
86   switch (code)
87     {
88     case GPGME_STATUS_ERROR:
89       err = parse_error (args);
90       if (err)
91         opd->error_seen = 1;
92       break;
93
94     case GPGME_STATUS_SUCCESS:
95       opd->success_seen = 1;
96       break;
97
98     case GPGME_STATUS_FAILURE:
99       opd->failure_code = _gpgme_parse_failure (args);
100       break;
101
102     case GPGME_STATUS_EOF:
103       /* In case the OpenPGP engine does not properly implement the
104          passwd command we won't get a success status back and thus we
105          conclude that this operation is not supported.  This is for
106          example the case for GnuPG < 2.0.16.  Note that this test is
107          obsolete for assuan based engines because they will properly
108          return an error for an unknown command.  */
109       if (ctx->protocol == GPGME_PROTOCOL_OpenPGP
110           && !opd->error_seen && !opd->success_seen)
111         err = gpg_error (GPG_ERR_NOT_SUPPORTED);
112       else if (opd->failure_code)
113         err = opd->failure_code;
114       break;
115
116     default:
117       break;
118     }
119
120   return err;
121 }
122
123
124 static gpgme_error_t
125 passwd_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key,
126               unsigned int flags)
127 {
128   gpgme_error_t err;
129   void *hook;
130   op_data_t opd;
131
132   if (!key)
133     return gpg_error (GPG_ERR_INV_VALUE);
134   if (flags)
135     return gpg_error (GPG_ERR_INV_FLAG);
136
137   err = _gpgme_op_reset (ctx, synchronous);
138   if (err)
139     return err;
140
141   err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, sizeof (*opd), NULL);
142   opd = hook;
143   if (err)
144     return err;
145
146   opd->success_seen = 0;
147   opd->error_seen = 0;
148
149   _gpgme_engine_set_status_handler (ctx->engine, passwd_status_handler, ctx);
150
151   if (ctx->passphrase_cb)
152     {
153       err = _gpgme_engine_set_command_handler
154         (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
155       if (err)
156         return err;
157     }
158
159   return _gpgme_engine_op_passwd (ctx->engine, key, flags);
160 }
161
162
163
164 /* Change the passphrase for KEY.  FLAGS is reserved for future use
165    and must be passed as 0.  The engine is expected to present a user
166    interface to enter the old and the new passphrase.  This is the
167    asynchronous variant.
168
169    Note that if ever the need arises to supply a passphrase we can do
170    this with a flag value and the passphrase callback feature.  */
171 gpgme_error_t
172 gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags)
173 {
174   gpg_error_t err;
175   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd_start", ctx,
176               "key=%p, flags=0x%x", key, flags);
177
178   if (!ctx)
179     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
180
181   err = passwd_start (ctx, 0, key, flags);
182   return TRACE_ERR (err);
183 }
184
185
186 /* Change the passphrase for KEY.  FLAGS is reserved for future use
187    and must be passed as 0.  This is the synchronous variant.  */
188 gpgme_error_t
189 gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags)
190 {
191   gpgme_error_t err;
192
193   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd", ctx,
194               "key=%p, flags=0x%x", key, flags);
195
196   if (!ctx)
197     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
198
199   err = passwd_start (ctx, 1, key, flags);
200   if (!err)
201     err = _gpgme_wait_one (ctx);
202   return TRACE_ERR (err);
203 }
204