Line data Source code
1 : /*
2 : gpgsignkeyeditinteractor.cpp - Edit Interactor to change the expiry time of an OpenPGP key
3 : Copyright (C) 2007 Klarälvdalens Datakonsult AB
4 :
5 : This file is part of GPGME++.
6 :
7 : GPGME++ is free software; you can redistribute it and/or
8 : modify it under the terms of the GNU Library General Public
9 : License as published by the Free Software Foundation; either
10 : version 2 of the License, or (at your option) any later version.
11 :
12 : GPGME++ is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU Library General Public License for more details.
16 :
17 : You should have received a copy of the GNU Library General Public License
18 : along with GPGME++; see the file COPYING.LIB. If not, write to the
19 : Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 : Boston, MA 02110-1301, USA.
21 : */
22 :
23 : #ifdef HAVE_CONFIG_H
24 : #include "config.h"
25 : #endif
26 :
27 : #include "gpgsignkeyeditinteractor.h"
28 : #include "error.h"
29 : #include "key.h"
30 :
31 : #include <gpgme.h>
32 :
33 : #include <map>
34 : #include <string>
35 : #include <sstream>
36 :
37 : #include <cassert>
38 : #include <cstring>
39 :
40 : using std::strcmp;
41 :
42 : // avoid conflict (msvc)
43 : #ifdef ERROR
44 : # undef ERROR
45 : #endif
46 :
47 : #ifdef _MSC_VER
48 : #undef snprintf
49 : #define snprintf _snprintf
50 : #endif
51 :
52 : using namespace GpgME;
53 :
54 0 : class GpgSignKeyEditInteractor::Private
55 : {
56 : public:
57 : Private();
58 :
59 : std::string scratch;
60 : bool started;
61 : int options;
62 : std::vector<unsigned int> userIDs;
63 : std::vector<unsigned int>::const_iterator currentId, nextId;
64 : unsigned int checkLevel;
65 :
66 0 : const char *command() const
67 : {
68 0 : const bool local = (options & Exportable) == 0;
69 0 : const bool nonRevoc = options & NonRevocable;
70 0 : const bool trust = options & Trust;
71 : //TODO: check if all combinations are valid
72 0 : if (local && nonRevoc && trust) {
73 0 : return "ltnrsign";
74 : }
75 0 : if (local && nonRevoc) {
76 0 : return "lnrsign";
77 : }
78 0 : if (local && trust) {
79 0 : return "ltsign";
80 : }
81 0 : if (local) {
82 0 : return "lsign";
83 : }
84 0 : if (nonRevoc && trust) {
85 0 : return "tnrsign";
86 : }
87 0 : if (nonRevoc) {
88 0 : return "nrsign";
89 : }
90 0 : if (trust) {
91 0 : return "tsign";
92 : }
93 0 : return "sign";
94 : }
95 :
96 0 : bool signAll() const
97 : {
98 0 : return userIDs.empty();
99 : }
100 0 : unsigned int nextUserID()
101 : {
102 0 : assert(nextId != userIDs.end());
103 0 : currentId = nextId++;
104 0 : return currentUserID();
105 : }
106 :
107 0 : bool allUserIDsListed() const
108 : {
109 0 : return nextId == userIDs.end();
110 : }
111 :
112 0 : unsigned int currentUserID() const
113 : {
114 0 : assert(currentId != userIDs.end());
115 0 : return *currentId + 1;
116 : }
117 :
118 : };
119 :
120 0 : GpgSignKeyEditInteractor::Private::Private()
121 : :
122 : started(false),
123 : options(0),
124 : userIDs(),
125 : currentId(),
126 : nextId(),
127 0 : checkLevel(0)
128 : {
129 0 : }
130 :
131 0 : GpgSignKeyEditInteractor::GpgSignKeyEditInteractor()
132 0 : : EditInteractor(), d(new Private)
133 : {
134 :
135 0 : }
136 :
137 0 : GpgSignKeyEditInteractor::~GpgSignKeyEditInteractor()
138 : {
139 0 : delete d;
140 0 : }
141 :
142 : // work around --enable-final
143 : namespace GpgSignKeyEditInteractor_Private
144 : {
145 : enum SignKeyState {
146 : START = EditInteractor::StartState,
147 : COMMAND,
148 : UIDS_ANSWER_SIGN_ALL,
149 : UIDS_LIST_SEPARATELY,
150 : // all these free slots belong to UIDS_LIST_SEPARATELY, too
151 : // (we increase state() by one for each UID, so that action() is called)
152 : UIDS_LIST_SEPARATELY_DONE = 1000000,
153 : SET_EXPIRE,
154 : SET_CHECK_LEVEL,
155 : SET_TRUST_VALUE,
156 : SET_TRUST_DEPTH,
157 : SET_TRUST_REGEXP,
158 : CONFIRM,
159 : QUIT,
160 : SAVE,
161 : ERROR = EditInteractor::ErrorState
162 : };
163 :
164 : typedef std::map<std::tuple<SignKeyState, unsigned int, std::string>, SignKeyState> TransitionMap;
165 :
166 : }
167 :
168 0 : static const char *answer(bool b)
169 : {
170 0 : return b ? "Y" : "N";
171 : }
172 :
173 0 : static GpgSignKeyEditInteractor_Private::TransitionMap makeTable()
174 : {
175 : using namespace GpgSignKeyEditInteractor_Private;
176 0 : TransitionMap tab;
177 0 : const unsigned int GET_BOOL = GPGME_STATUS_GET_BOOL;
178 0 : const unsigned int GET_LINE = GPGME_STATUS_GET_LINE;
179 : #define addEntry( s1, status, str, s2 ) tab[std::make_tuple( s1, status, str)] = s2
180 0 : addEntry(START, GET_LINE, "keyedit.prompt", COMMAND);
181 0 : addEntry(COMMAND, GET_BOOL, "keyedit.sign_all.okay", UIDS_ANSWER_SIGN_ALL);
182 0 : addEntry(COMMAND, GET_BOOL, "sign_uid.okay", CONFIRM);
183 0 : addEntry(UIDS_ANSWER_SIGN_ALL, GET_BOOL, "sign_uid.okay", CONFIRM);
184 0 : addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.expire", SET_EXPIRE);
185 0 : addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
186 0 : addEntry(SET_TRUST_VALUE, GET_LINE, "trustsign_prompt.trust_depth", SET_TRUST_DEPTH);
187 0 : addEntry(SET_TRUST_DEPTH, GET_LINE, "trustsign_prompt.trust_regexp", SET_TRUST_REGEXP);
188 0 : addEntry(SET_TRUST_REGEXP, GET_LINE, "sign_uid.okay", CONFIRM);
189 0 : addEntry(SET_CHECK_LEVEL, GET_BOOL, "sign_uid.okay", CONFIRM);
190 0 : addEntry(SET_EXPIRE, GET_BOOL, "sign_uid.class", SET_CHECK_LEVEL);
191 0 : addEntry(CONFIRM, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
192 0 : addEntry(CONFIRM, GET_BOOL, "sign_uid.okay", CONFIRM);
193 0 : addEntry(CONFIRM, GET_LINE, "keyedit.prompt", COMMAND);
194 0 : addEntry(CONFIRM, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
195 0 : addEntry(CONFIRM, GET_LINE, "sign_uid.expire", SET_EXPIRE);
196 0 : addEntry(CONFIRM, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
197 0 : addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
198 0 : addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "keyedit.prompt", COMMAND);
199 0 : addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
200 0 : addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.expire", SET_EXPIRE);
201 0 : addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
202 0 : addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.okay", CONFIRM);
203 0 : addEntry(CONFIRM, GET_LINE, "keyedit.prompt", QUIT);
204 0 : addEntry(ERROR, GET_LINE, "keyedit.prompt", QUIT);
205 0 : addEntry(QUIT, GET_BOOL, "keyedit.save.okay", SAVE);
206 : #undef addEntry
207 0 : return tab;
208 : }
209 :
210 0 : const char *GpgSignKeyEditInteractor::action(Error &err) const
211 : {
212 : static const char check_level_strings[][2] = { "0", "1", "2", "3" };
213 : using namespace GpgSignKeyEditInteractor_Private;
214 : using namespace std;
215 :
216 0 : switch (const unsigned int st = state()) {
217 : case COMMAND:
218 0 : return d->command();
219 : case UIDS_ANSWER_SIGN_ALL:
220 0 : return answer(d->signAll());
221 : case UIDS_LIST_SEPARATELY_DONE:
222 0 : return d->command();
223 : case SET_EXPIRE:
224 0 : return answer(true);
225 : case SET_TRUST_VALUE:
226 : // TODO
227 : case SET_TRUST_DEPTH:
228 : //TODO
229 : case SET_TRUST_REGEXP:
230 : //TODO
231 0 : return 0;
232 : case SET_CHECK_LEVEL:
233 0 : return check_level_strings[d->checkLevel];
234 : case CONFIRM:
235 0 : return answer(true);
236 : case QUIT:
237 0 : return "quit";
238 : case SAVE:
239 0 : return answer(true);
240 : default:
241 0 : if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) {
242 0 : std::stringstream ss;
243 0 : ss << d->nextUserID();
244 0 : d->scratch = ss.str();
245 0 : return d->scratch.c_str();
246 : }
247 : // fall through
248 : case ERROR:
249 0 : err = Error::fromCode(GPG_ERR_GENERAL);
250 0 : return 0;
251 : }
252 : }
253 :
254 0 : unsigned int GpgSignKeyEditInteractor::nextState(unsigned int status, const char *args, Error &err) const
255 : {
256 0 : d->started = true;
257 : using namespace GpgSignKeyEditInteractor_Private;
258 0 : static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL);
259 : //static const Error INV_TIME_ERROR = Error::fromCode( GPG_ERR_INV_TIME );
260 0 : static const TransitionMap table(makeTable());
261 0 : if (needsNoResponse(status)) {
262 0 : return state();
263 : }
264 :
265 : using namespace GpgSignKeyEditInteractor_Private;
266 :
267 : //lookup transition in map
268 0 : const TransitionMap::const_iterator it = table.find(std::make_tuple(static_cast<SignKeyState>(state()), status, std::string(args)));
269 0 : if (it != table.end()) {
270 0 : return it->second;
271 : }
272 :
273 : //handle cases that cannot be handled via the map
274 0 : switch (const unsigned int st = state()) {
275 : case UIDS_ANSWER_SIGN_ALL:
276 0 : if (status == GPGME_STATUS_GET_LINE &&
277 0 : strcmp(args, "keyedit.prompt") == 0) {
278 0 : if (!d->signAll()) {
279 0 : return UIDS_LIST_SEPARATELY;
280 : }
281 0 : err = Error::fromCode(GPG_ERR_UNUSABLE_PUBKEY);
282 0 : return ERROR;
283 : }
284 0 : break;
285 : default:
286 0 : if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) {
287 0 : if (status == GPGME_STATUS_GET_LINE &&
288 0 : strcmp(args, "keyedit.prompt") == 0) {
289 0 : return d->allUserIDsListed() ? UIDS_LIST_SEPARATELY_DONE : st + 1 ;
290 : }
291 : }
292 0 : break;
293 : case CONFIRM:
294 : case ERROR:
295 0 : err = lastError();
296 0 : return ERROR;
297 : }
298 :
299 0 : err = GENERAL_ERROR;
300 0 : return ERROR;
301 : }
302 :
303 0 : void GpgSignKeyEditInteractor::setCheckLevel(unsigned int checkLevel)
304 : {
305 0 : assert(!d->started);
306 0 : assert(checkLevel <= 3);
307 0 : d->checkLevel = checkLevel;
308 0 : }
309 :
310 0 : void GpgSignKeyEditInteractor::setUserIDsToSign(const std::vector<unsigned int> &userIDsToSign)
311 : {
312 0 : assert(!d->started);
313 0 : d->userIDs = userIDsToSign;
314 0 : d->nextId = d->userIDs.begin();
315 0 : d->currentId = d->userIDs.end();
316 :
317 0 : }
318 0 : void GpgSignKeyEditInteractor::setSigningOptions(int options)
319 : {
320 0 : assert(!d->started);
321 0 : d->options = options;
322 0 : }
|