Cpp / Qt: Reduce boost usage (memory and tuple)
[gpgme.git] / lang / cpp / src / gpgsignkeyeditinteractor.cpp
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 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     const char *command() const
63     {
64         const bool local = (options & Exportable) == 0;
65         const bool nonRevoc = options & NonRevocable;
66         const bool trust = options & Trust;
67         //TODO: check if all combinations are valid
68         if (local && nonRevoc && trust) {
69             return "ltnrsign";
70         }
71         if (local && nonRevoc) {
72             return "lnrsign";
73         }
74         if (local && trust) {
75             return "ltsign";
76         }
77         if (local) {
78             return "lsign";
79         }
80         if (nonRevoc && trust) {
81             return "tnrsign";
82         }
83         if (nonRevoc) {
84             return "nrsign";
85         }
86         if (trust) {
87             return "tsign";
88         }
89         return "sign";
90     }
91
92     bool signAll() const
93     {
94         return userIDs.empty();
95     }
96     unsigned int nextUserID()
97     {
98         assert(nextId != userIDs.end());
99         currentId = nextId++;
100         return currentUserID();
101     }
102
103     bool allUserIDsListed() const
104     {
105         return nextId == userIDs.end();
106     }
107
108     unsigned int currentUserID() const
109     {
110         assert(currentId != userIDs.end());
111         return *currentId + 1;
112     }
113
114 };
115
116 GpgSignKeyEditInteractor::Private::Private()
117     :
118     started(false),
119     options(0),
120     userIDs(),
121     currentId(),
122     nextId(),
123     checkLevel(0)
124 {
125 }
126
127 GpgSignKeyEditInteractor::GpgSignKeyEditInteractor()
128     : EditInteractor(), d(new Private)
129 {
130
131 }
132
133 GpgSignKeyEditInteractor::~GpgSignKeyEditInteractor()
134 {
135     delete d;
136 }
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 static const char *answer(bool b)
165 {
166     return b ? "Y" : "N";
167 }
168
169 static GpgSignKeyEditInteractor_Private::TransitionMap makeTable()
170 {
171     using namespace GpgSignKeyEditInteractor_Private;
172     TransitionMap tab;
173     const unsigned int GET_BOOL = GPGME_STATUS_GET_BOOL;
174     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     addEntry(START, GET_LINE, "keyedit.prompt", COMMAND);
177     addEntry(COMMAND, GET_BOOL, "keyedit.sign_all.okay", UIDS_ANSWER_SIGN_ALL);
178     addEntry(COMMAND, GET_BOOL, "sign_uid.okay", CONFIRM);
179     addEntry(UIDS_ANSWER_SIGN_ALL, GET_BOOL, "sign_uid.okay", CONFIRM);
180     addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.expire", SET_EXPIRE);
181     addEntry(UIDS_ANSWER_SIGN_ALL, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
182     addEntry(SET_TRUST_VALUE, GET_LINE, "trustsign_prompt.trust_depth", SET_TRUST_DEPTH);
183     addEntry(SET_TRUST_DEPTH, GET_LINE, "trustsign_prompt.trust_regexp", SET_TRUST_REGEXP);
184     addEntry(SET_TRUST_REGEXP, GET_LINE, "sign_uid.okay", CONFIRM);
185     addEntry(SET_CHECK_LEVEL, GET_BOOL, "sign_uid.okay", CONFIRM);
186     addEntry(SET_EXPIRE, GET_BOOL, "sign_uid.class", SET_CHECK_LEVEL);
187     addEntry(CONFIRM, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
188     addEntry(CONFIRM, GET_BOOL, "sign_uid.okay", CONFIRM);
189     addEntry(CONFIRM, GET_LINE, "keyedit.prompt", COMMAND);
190     addEntry(CONFIRM, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
191     addEntry(CONFIRM, GET_LINE, "sign_uid.expire", SET_EXPIRE);
192     addEntry(CONFIRM, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
193     addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.local_promote_okay", CONFIRM);
194     addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "keyedit.prompt", COMMAND);
195     addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "trustsign_prompt.trust_value", SET_TRUST_VALUE);
196     addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.expire", SET_EXPIRE);
197     addEntry(UIDS_LIST_SEPARATELY_DONE, GET_LINE, "sign_uid.class", SET_CHECK_LEVEL);
198     addEntry(UIDS_LIST_SEPARATELY_DONE, GET_BOOL, "sign_uid.okay", CONFIRM);
199     addEntry(CONFIRM, GET_LINE, "keyedit.prompt", QUIT);
200     addEntry(ERROR, GET_LINE, "keyedit.prompt", QUIT);
201     addEntry(QUIT, GET_BOOL, "keyedit.save.okay", SAVE);
202 #undef addEntry
203     return tab;
204 }
205
206 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     switch (const unsigned int st = state()) {
213     case COMMAND:
214         return d->command();
215     case UIDS_ANSWER_SIGN_ALL:
216         return answer(d->signAll());
217     case UIDS_LIST_SEPARATELY_DONE:
218         return d->command();
219     case SET_EXPIRE:
220         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         return 0;
228     case SET_CHECK_LEVEL:
229         return check_level_strings[d->checkLevel];
230     case CONFIRM:
231         return answer(true);
232     case QUIT:
233         return "quit";
234     case SAVE:
235         return answer(true);
236     default:
237         if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) {
238             std::stringstream ss;
239             ss << d->nextUserID();
240             d->scratch = ss.str();
241             return d->scratch.c_str();
242         }
243     // fall through
244     case ERROR:
245         err = Error::fromCode(GPG_ERR_GENERAL);
246         return 0;
247     }
248 }
249
250 unsigned int GpgSignKeyEditInteractor::nextState(unsigned int status, const char *args, Error &err) const
251 {
252     d->started = true;
253     using namespace GpgSignKeyEditInteractor_Private;
254     static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL);
255     //static const Error INV_TIME_ERROR = Error::fromCode( GPG_ERR_INV_TIME );
256     static const TransitionMap table(makeTable());
257     if (needsNoResponse(status)) {
258         return state();
259     }
260
261     using namespace GpgSignKeyEditInteractor_Private;
262
263     //lookup transition in map
264     const TransitionMap::const_iterator it = table.find(std::make_tuple(static_cast<SignKeyState>(state()), status, std::string(args)));
265     if (it != table.end()) {
266         return it->second;
267     }
268
269     //handle cases that cannot be handled via the map
270     switch (const unsigned int st = state()) {
271     case UIDS_ANSWER_SIGN_ALL:
272         if (status == GPGME_STATUS_GET_LINE &&
273                 strcmp(args, "keyedit.prompt") == 0) {
274             if (!d->signAll()) {
275                 return UIDS_LIST_SEPARATELY;
276             }
277             err = Error::fromCode(GPG_ERR_UNUSABLE_PUBKEY);
278             return ERROR;
279         }
280         break;
281     default:
282         if (st >= UIDS_LIST_SEPARATELY && st < UIDS_LIST_SEPARATELY_DONE) {
283             if (status == GPGME_STATUS_GET_LINE &&
284                     strcmp(args, "keyedit.prompt") == 0) {
285                 return d->allUserIDsListed() ? UIDS_LIST_SEPARATELY_DONE : st + 1 ;
286             }
287         }
288         break;
289     case CONFIRM:
290     case ERROR:
291         err = lastError();
292         return ERROR;
293     }
294
295     err = GENERAL_ERROR;
296     return ERROR;
297 }
298
299 void GpgSignKeyEditInteractor::setCheckLevel(unsigned int checkLevel)
300 {
301     assert(!d->started);
302     assert(checkLevel <= 3);
303     d->checkLevel = checkLevel;
304 }
305
306 void GpgSignKeyEditInteractor::setUserIDsToSign(const std::vector<unsigned int> &userIDsToSign)
307 {
308     assert(!d->started);
309     d->userIDs = userIDsToSign;
310     d->nextId = d->userIDs.begin();
311     d->currentId = d->userIDs.end();
312
313 }
314 void GpgSignKeyEditInteractor::setSigningOptions(int options)
315 {
316     assert(!d->started);
317     d->options = options;
318 }