Trace the use of GPG_ERR_INV_ENGINE.
[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 <http://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   int success_seen;
34   int error_seen;
35 } *op_data_t;
36
37
38 \f
39 /* Parse an error status line and return the error code.  */
40 static gpgme_error_t
41 parse_error (char *args)
42 {
43   gpgme_error_t err;
44   char *where = strchr (args, ' ');
45   char *which;
46
47   if (where)
48     {
49       *where = '\0';
50       which = where + 1;
51
52       where = strchr (which, ' ');
53       if (where)
54         *where = '\0';
55
56       where = args;
57     }
58   else
59     return trace_gpg_error (GPG_ERR_INV_ENGINE);
60
61   err = atoi (which);
62
63   if (!strcmp (where, "keyedit.passwd"))
64     return err;
65
66   return 0;
67 }
68
69
70 static gpgme_error_t
71 passwd_status_handler (void *priv, gpgme_status_code_t code, char *args)
72 {
73   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
74   gpgme_error_t err;
75   void *hook;
76   op_data_t opd;
77
78   err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, -1, NULL);
79   opd = hook;
80   if (err)
81     return err;
82
83   switch (code)
84     {
85     case GPGME_STATUS_ERROR:
86       err = parse_error (args);
87       if (err)
88         opd->error_seen = 1;
89       break;
90
91     case GPGME_STATUS_SUCCESS:
92       opd->success_seen = 1;
93       break;
94
95     case GPGME_STATUS_EOF:
96       /* In case the OpenPGP engine does not properly implement the
97          passwd command we won't get a success status back and thus we
98          conclude that this operation is not supported.  This is for
99          example the case for GnuPG < 2.0.16.  Note that this test is
100          obsolete for assuan based engines because they will properly
101          return an error for an unknown command.  */
102       if (ctx->protocol == GPGME_PROTOCOL_OpenPGP
103           && !opd->error_seen && !opd->success_seen)
104         err = gpg_error (GPG_ERR_NOT_SUPPORTED);
105       break;
106
107     default:
108       break;
109     }
110
111   return err;
112 }
113
114
115 static gpgme_error_t
116 passwd_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key,
117               unsigned int flags)
118 {
119   gpgme_error_t err;
120   void *hook;
121   op_data_t opd;
122
123   if (!key)
124     return gpg_error (GPG_ERR_INV_VALUE);
125   if (flags)
126     return gpg_error (GPG_ERR_INV_FLAG);
127
128   err = _gpgme_op_reset (ctx, synchronous);
129   if (err)
130     return err;
131
132   err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, sizeof (*opd), NULL);
133   opd = hook;
134   if (err)
135     return err;
136
137   opd->success_seen = 0;
138   opd->error_seen = 0;
139
140   _gpgme_engine_set_status_handler (ctx->engine, passwd_status_handler, ctx);
141
142   return _gpgme_engine_op_passwd (ctx->engine, key, flags);
143 }
144
145
146
147 /* Change the passphrase for KEY.  FLAGS is reserved for future use
148    and must be passed as 0.  The engine is expected to present a user
149    interface to enter the old and the new passphrase.  This is the
150    asynchronous variant.
151
152    Note that if ever the need arises to supply a passphrase we can do
153    this with a flag value and the passphrase callback feature.  */
154 gpgme_error_t
155 gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags)
156 {
157   gpg_error_t err;
158   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd_start", ctx,
159               "key=%p, flags=0x%x", key, flags);
160
161   if (!ctx)
162     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
163
164   err = passwd_start (ctx, 0, key, flags);
165   return TRACE_ERR (err);
166 }
167
168
169 /* Change the passphrase for KEY.  FLAGS is reserved for future use
170    and must be passed as 0.  This is the synchronous variant.  */
171 gpgme_error_t
172 gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags)
173 {
174   gpgme_error_t err;
175
176   TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd", ctx,
177               "key=%p, flags=0x%x", key, flags);
178
179   if (!ctx)
180     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
181
182   err = passwd_start (ctx, 1, key, flags);
183   if (!err)
184     err = _gpgme_wait_one (ctx);
185   return TRACE_ERR (err);
186 }
187