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