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