1 | /* |
2 | verificationresult.cpp - wraps a gpgme verify result |
3 | Copyright (C) 2004 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 <gpgme++/config-gpgme++.h> |
24 | #include <gpgme++/verificationresult.h> |
25 | #include <gpgme++/notation.h> |
26 | #include "result_p.h" |
27 | #include "util.h" |
28 | |
29 | #include <gpgme.h> |
30 | |
31 | #include <istream> |
32 | #include <algorithm> |
33 | #include <iterator> |
34 | #include <string> |
35 | #include <cstring> |
36 | #include <cstdlib> |
37 | |
38 | #include <string.h> |
39 | |
40 | class GpgME::VerificationResult::Private { |
41 | public: |
42 | explicit Private( const gpgme_verify_result_t r ) { |
43 | if ( !r ) { |
44 | return; |
45 | } |
46 | #ifdef HAVE_GPGME_VERIFY_RESULT_T_FILE_NAME |
47 | if ( r->file_name ) { |
48 | file_name = r->file_name; |
49 | } |
50 | #endif |
51 | // copy recursively, using compiler-generated copy ctor. |
52 | // We just need to handle the pointers in the structs: |
53 | for ( gpgme_signature_t is = r->signatures ; is ; is = is->next ) { |
54 | gpgme_signature_t scopy = new _gpgme_signature( *is ); |
55 | if ( is->fpr ) { |
56 | scopy->fpr = strdup( is->fpr ); |
57 | } |
58 | #ifdef HAVE_GPGME_SIGNATURE_T_PKA_FIELDS |
59 | // PENDING(marc) why does this crash on Windows in strdup()? |
60 | # ifndef _WIN32 |
61 | if ( is->pka_address ) { |
62 | scopy->pka_address = strdup( is->pka_address ); |
63 | } |
64 | # else |
65 | scopy->pka_address = 0; |
66 | # endif |
67 | #endif |
68 | scopy->next = 0; |
69 | sigs.push_back( scopy ); |
70 | // copy notations: |
71 | nota.push_back( std::vector<Nota>() ); |
72 | purls.push_back( 0 ); |
73 | for ( gpgme_sig_notation_t in = is->notations ; in ; in = in->next ) { |
74 | if ( !in->name ) { |
75 | if ( in->value ) { |
76 | purls.back() = strdup( in->value ); // policy url |
77 | } |
78 | continue; |
79 | } |
80 | #ifdef HAVE_GPGME_SIG_NOTATION_FLAGS_T |
81 | Nota n = { 0, 0, in->flags }; |
82 | #else |
83 | Nota n = { 0, 0 }; |
84 | #endif |
85 | n.name = strdup( in->name ); |
86 | if ( in->value ) { |
87 | n.value = strdup( in->value ); |
88 | } |
89 | nota.back().push_back( n ); |
90 | } |
91 | } |
92 | } |
93 | ~Private() { |
94 | for ( std::vector<gpgme_signature_t>::iterator it = sigs.begin() ; it != sigs.end() ; ++it ) { |
95 | std::free( ( *it )->fpr ); |
96 | #ifdef HAVE_GPGME_SIGNATURE_T_PKA_FIELDS |
97 | std::free( ( *it )->pka_address ); |
98 | #endif |
99 | delete *it; *it = 0; |
100 | } |
101 | for ( std::vector< std::vector<Nota> >::iterator it = nota.begin() ; it != nota.end() ; ++it ) { |
102 | for ( std::vector<Nota>::iterator jt = it->begin() ; jt != it->end() ; ++jt ) { |
103 | std::free( jt->name ); jt->name = 0; |
104 | std::free( jt->value ); jt->value = 0; |
105 | } |
106 | } |
107 | std::for_each( purls.begin(), purls.end(), &std::free ); |
108 | } |
109 | |
110 | struct Nota { |
111 | char * name; |
112 | char * value; |
113 | #ifdef HAVE_GPGME_SIG_NOTATION_FLAGS_T |
114 | gpgme_sig_notation_flags_t flags; |
115 | #endif |
116 | }; |
117 | |
118 | std::vector<gpgme_signature_t> sigs; |
119 | std::vector< std::vector<Nota> > nota; |
120 | std::vector<char*> purls; |
121 | std::string file_name; |
122 | }; |
123 | |
124 | GpgME::VerificationResult::VerificationResult( gpgme_ctx_t ctx, int error ) |
125 | : GpgME::Result( error ), d() |
126 | { |
127 | init( ctx ); |
128 | } |
129 | |
130 | GpgME::VerificationResult::VerificationResult( gpgme_ctx_t ctx, const Error & error ) |
131 | : GpgME::Result( error ), d() |
132 | { |
133 | init( ctx ); |
134 | } |
135 | |
136 | void GpgME::VerificationResult::init( gpgme_ctx_t ctx ) { |
137 | if ( !ctx ) { |
138 | return; |
139 | } |
140 | gpgme_verify_result_t res = gpgme_op_verify_result( ctx ); |
141 | if ( !res ) { |
142 | return; |
143 | } |
144 | d.reset( new Private( res ) ); |
145 | } |
146 | |
147 | make_standard_stuff(VerificationResult) |
148 | |
149 | const char * GpgME::VerificationResult::fileName() const { |
150 | return d ? d->file_name.c_str() : 0 ; |
151 | } |
152 | |
153 | unsigned int GpgME::VerificationResult::numSignatures() const { |
154 | return d ? d->sigs.size() : 0 ; |
155 | } |
156 | |
157 | GpgME::Signature GpgME::VerificationResult::signature( unsigned int idx ) const { |
158 | return Signature( d, idx ); |
159 | } |
160 | |
161 | std::vector<GpgME::Signature> GpgME::VerificationResult::signatures() const { |
162 | if ( !d ) { |
163 | return std::vector<Signature>(); |
164 | } |
165 | std::vector<Signature> result; |
166 | result.reserve( d->sigs.size() ); |
167 | for ( unsigned int i = 0 ; i < d->sigs.size() ; ++i ) { |
168 | result.push_back( Signature( d, i ) ); |
169 | } |
170 | return result; |
171 | } |
172 | |
173 | GpgME::Signature::Signature( const boost::shared_ptr<VerificationResult::Private> & parent, unsigned int i ) |
174 | : d( parent ), idx( i ) |
175 | { |
176 | } |
177 | |
178 | GpgME::Signature::Signature() : d(), idx( 0 ) {} |
179 | |
180 | bool GpgME::Signature::isNull() const { |
181 | return !d || idx >= d->sigs.size() ; |
182 | } |
183 | |
184 | |
185 | GpgME::Signature::Summary GpgME::Signature::summary() const { |
186 | if ( isNull() ) { |
187 | return None; |
188 | } |
189 | gpgme_sigsum_t sigsum = d->sigs[idx]->summary; |
190 | unsigned int result = 0; |
191 | if ( sigsum & GPGME_SIGSUM_VALID ) { |
192 | result |= Valid; |
193 | } |
194 | if ( sigsum & GPGME_SIGSUM_GREEN ) { |
195 | result |= Green; |
196 | } |
197 | if ( sigsum & GPGME_SIGSUM_RED ) { |
198 | result |= Red; |
199 | } |
200 | if ( sigsum & GPGME_SIGSUM_KEY_REVOKED ) { |
201 | result |= KeyRevoked; |
202 | } |
203 | if ( sigsum & GPGME_SIGSUM_KEY_EXPIRED ) { |
204 | result |= KeyExpired; |
205 | } |
206 | if ( sigsum & GPGME_SIGSUM_SIG_EXPIRED ) { |
207 | result |= SigExpired; |
208 | } |
209 | if ( sigsum & GPGME_SIGSUM_KEY_MISSING ) { |
210 | result |= KeyMissing; |
211 | } |
212 | if ( sigsum & GPGME_SIGSUM_CRL_MISSING ) { |
213 | result |= CrlMissing; |
214 | } |
215 | if ( sigsum & GPGME_SIGSUM_CRL_TOO_OLD ) { |
216 | result |= CrlTooOld; |
217 | } |
218 | if ( sigsum & GPGME_SIGSUM_BAD_POLICY ) { |
219 | result |= BadPolicy; |
220 | } |
221 | if ( sigsum & GPGME_SIGSUM_SYS_ERROR ) { |
222 | result |= SysError; |
223 | } |
224 | return static_cast<Summary>( result ); |
225 | } |
226 | |
227 | const char * GpgME::Signature::fingerprint() const { |
228 | return isNull() ? 0 : d->sigs[idx]->fpr ; |
229 | } |
230 | |
231 | GpgME::Error GpgME::Signature::status() const { |
232 | return Error( isNull() ? 0 : d->sigs[idx]->status ); |
233 | } |
234 | |
235 | time_t GpgME::Signature::creationTime() const { |
236 | return static_cast<time_t>( isNull() ? 0 : d->sigs[idx]->timestamp ); |
237 | } |
238 | |
239 | time_t GpgME::Signature::expirationTime() const { |
240 | return static_cast<time_t>( isNull() ? 0 : d->sigs[idx]->exp_timestamp ); |
241 | } |
242 | |
243 | bool GpgME::Signature::neverExpires() const { |
244 | return expirationTime() == (time_t)0; |
245 | } |
246 | |
247 | bool GpgME::Signature::isWrongKeyUsage() const { |
248 | return !isNull() && d->sigs[idx]->wrong_key_usage; |
249 | } |
250 | |
251 | bool GpgME::Signature::isVerifiedUsingChainModel() const { |
252 | #ifdef HAVE_GPGME_SIGNATURE_T_CHAIN_MODEL |
253 | return !isNull() && d->sigs[idx]->chain_model; |
254 | #else |
255 | return false; |
256 | #endif |
257 | } |
258 | |
259 | GpgME::Signature::PKAStatus GpgME::Signature::pkaStatus() const { |
260 | #ifdef HAVE_GPGME_SIGNATURE_T_PKA_FIELDS |
261 | if ( !isNull() ) { |
262 | return static_cast<PKAStatus>( d->sigs[idx]->pka_trust ); |
263 | } |
264 | #endif |
265 | return UnknownPKAStatus; |
266 | } |
267 | |
268 | const char * GpgME::Signature::pkaAddress() const { |
269 | #ifdef HAVE_GPGME_SIGNATURE_T_PKA_FIELDS |
270 | if ( !isNull() ) { |
271 | return d->sigs[idx]->pka_address; |
272 | } |
273 | #endif |
274 | return 0; |
275 | } |
276 | |
277 | GpgME::Signature::Validity GpgME::Signature::validity() const { |
278 | if ( isNull() ) { |
279 | return Unknown; |
280 | } |
281 | switch ( d->sigs[idx]->validity ) { |
282 | default: |
283 | case GPGME_VALIDITY_UNKNOWN: return Unknown; |
284 | case GPGME_VALIDITY_UNDEFINED: return Undefined; |
285 | case GPGME_VALIDITY_NEVER: return Never; |
286 | case GPGME_VALIDITY_MARGINAL: return Marginal; |
287 | case GPGME_VALIDITY_FULL: return Full; |
288 | case GPGME_VALIDITY_ULTIMATE: return Ultimate; |
289 | } |
290 | } |
291 | |
292 | |
293 | char GpgME::Signature::validityAsString() const { |
294 | if ( isNull() ) { |
295 | return '?'; |
296 | } |
297 | switch ( d->sigs[idx]->validity ) { |
298 | default: |
299 | case GPGME_VALIDITY_UNKNOWN: return '?'; |
300 | case GPGME_VALIDITY_UNDEFINED: return 'q'; |
301 | case GPGME_VALIDITY_NEVER: return 'n'; |
302 | case GPGME_VALIDITY_MARGINAL: return 'm'; |
303 | case GPGME_VALIDITY_FULL: return 'f'; |
304 | case GPGME_VALIDITY_ULTIMATE: return 'u'; |
305 | } |
306 | } |
307 | |
308 | GpgME::Error GpgME::Signature::nonValidityReason() const { |
309 | return Error( isNull() ? 0 : d->sigs[idx]->validity_reason ); |
310 | } |
311 | |
312 | unsigned int GpgME::Signature::publicKeyAlgorithm() const { |
313 | #ifdef HAVE_GPGME_SIGNATURE_T_ALGORITHM_FIELDS |
314 | if ( !isNull() ) { |
315 | return d->sigs[idx]->pubkey_algo; |
316 | } |
317 | #endif |
318 | return 0; |
319 | } |
320 | |
321 | const char * GpgME::Signature::publicKeyAlgorithmAsString() const { |
322 | #ifdef HAVE_GPGME_SIGNATURE_T_ALGORITHM_FIELDS |
323 | if ( !isNull() ) { |
324 | return gpgme_pubkey_algo_name( d->sigs[idx]->pubkey_algo ); |
325 | } |
326 | #endif |
327 | return 0; |
328 | } |
329 | |
330 | unsigned int GpgME::Signature::hashAlgorithm() const { |
331 | #ifdef HAVE_GPGME_SIGNATURE_T_ALGORITHM_FIELDS |
332 | if ( !isNull() ) { |
333 | return d->sigs[idx]->hash_algo; |
334 | } |
335 | #endif |
336 | return 0; |
337 | } |
338 | |
339 | const char * GpgME::Signature::hashAlgorithmAsString() const { |
340 | #ifdef HAVE_GPGME_SIGNATURE_T_ALGORITHM_FIELDS |
341 | if ( !isNull() ) { |
342 | return gpgme_hash_algo_name( d->sigs[idx]->hash_algo ); |
343 | } |
344 | #endif |
345 | return 0; |
346 | } |
347 | |
348 | const char * GpgME::Signature::policyURL() const { |
349 | return isNull() ? 0 : d->purls[idx] ; |
350 | } |
351 | |
352 | GpgME::Notation GpgME::Signature::notation( unsigned int nidx ) const { |
353 | return GpgME::Notation( d, idx, nidx ); |
354 | } |
355 | |
356 | std::vector<GpgME::Notation> GpgME::Signature::notations() const { |
357 | if ( isNull() ) { |
358 | return std::vector<GpgME::Notation>(); |
359 | } |
360 | std::vector<GpgME::Notation> result; |
361 | result.reserve( d->nota[idx].size() ); |
362 | for ( unsigned int i = 0 ; i < d->nota[idx].size() ; ++i ) { |
363 | result.push_back( GpgME::Notation( d, idx, i ) ); |
364 | } |
365 | return result; |
366 | } |
367 | |
368 | |
369 | class GpgME::Notation::Private { |
370 | public: |
371 | Private() : d(), sidx( 0 ), nidx( 0 ), nota( 0 ) {} |
372 | Private( const boost::shared_ptr<VerificationResult::Private> & priv, unsigned int sindex, unsigned int nindex ) |
373 | : d( priv ), sidx( sindex ), nidx( nindex ), nota( 0 ) |
374 | { |
375 | |
376 | } |
377 | Private( gpgme_sig_notation_t n ) |
378 | : d(), sidx( 0 ), nidx( 0 ), nota( n ? new _gpgme_sig_notation( *n ) : 0 ) |
379 | { |
380 | if ( nota && nota->name ) { |
381 | nota->name = strdup( nota->name ); |
382 | } |
383 | if ( nota && nota->value ) { |
384 | nota->value = strdup( nota->value ); |
385 | } |
386 | } |
387 | Private( const Private & other ) |
388 | : d( other.d ), sidx( other.sidx ), nidx( other.nidx ), nota( other.nota ) |
389 | { |
390 | if ( nota ) { |
391 | nota->name = strdup( nota->name ); |
392 | nota->value = strdup( nota->value ); |
393 | } |
394 | } |
395 | ~Private() { |
396 | if ( nota ) { |
397 | std::free( nota->name ); nota->name = 0; |
398 | std::free( nota->value ); nota->value = 0; |
399 | delete nota; |
400 | } |
401 | } |
402 | |
403 | boost::shared_ptr<VerificationResult::Private> d; |
404 | unsigned int sidx, nidx; |
405 | gpgme_sig_notation_t nota; |
406 | }; |
407 | |
408 | |
409 | GpgME::Notation::Notation( const boost::shared_ptr<VerificationResult::Private> & parent, unsigned int sindex, unsigned int nindex ) |
410 | : d( new Private( parent, sindex, nindex ) ) |
411 | { |
412 | |
413 | } |
414 | |
415 | GpgME::Notation::Notation( gpgme_sig_notation_t nota ) |
416 | : d( new Private( nota ) ) |
417 | { |
418 | |
419 | } |
420 | |
421 | GpgME::Notation::Notation() : d() {} |
422 | |
423 | bool GpgME::Notation::isNull() const { |
424 | if ( !d ) { |
425 | return true; |
426 | } |
427 | if ( d->d ) { |
428 | return d->sidx >= d->d->nota.size() || d->nidx >= d->d->nota[d->sidx].size() ; |
429 | } |
430 | return !d->nota; |
431 | } |
432 | |
433 | |
434 | const char * GpgME::Notation::name() const { |
435 | return |
436 | isNull() ? 0 : |
437 | d->d ? d->d->nota[d->sidx][d->nidx].name : |
438 | d->nota ? d->nota->name : 0 ; |
439 | } |
440 | |
441 | const char * GpgME::Notation::value() const { |
442 | return |
443 | isNull() ? 0 : |
444 | d->d ? d->d->nota[d->sidx][d->nidx].value : |
445 | d->nota ? d->nota->value : 0 ; |
446 | } |
447 | |
448 | GpgME::Notation::Flags GpgME::Notation::flags() const { |
449 | return |
450 | convert_from_gpgme_sig_notation_flags_t( |
451 | #ifdef HAVE_GPGME_SIG_NOTATION_FLAGS_T |
452 | isNull() ? 0 : |
453 | d->d ? d->d->nota[d->sidx][d->nidx].flags : |
454 | d->nota ? d->nota->flags : 0 ); |
455 | #else |
456 | 0 ); |
457 | #endif |
458 | } |
459 | |
460 | bool GpgME::Notation::isHumanReadable() const { |
461 | return flags() & HumanReadable; |
462 | } |
463 | |
464 | bool GpgME::Notation::isCritical() const { |
465 | return flags() & Critical; |
466 | } |
467 | |
468 | std::ostream & GpgME::operator<<( std::ostream & os, const VerificationResult & result ) { |
469 | os << "GpgME::VerificationResult(" ; |
470 | if ( !result.isNull() ) { |
471 | os << "\n error: " << result.error() |
472 | << "\n fileName: " << protect( result.fileName() ) |
473 | << "\n signatures:\n" ; |
474 | const std::vector<Signature> sigs = result.signatures(); |
475 | std::copy( sigs.begin(), sigs.end(), |
476 | std::ostream_iterator<Signature>( os, "\n" ) ); |
477 | } |
478 | return os << ')'; |
479 | } |
480 | |
481 | std::ostream & GpgME::operator<<( std::ostream & os, Signature::PKAStatus pkaStatus ) { |
482 | #define OUTPUT( x ) if ( !(pkaStatus & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0) |
483 | os << "GpgME::Signature::PKAStatus(" ; |
484 | OUTPUT( UnknownPKAStatus ); |
485 | OUTPUT( PKAVerificationFailed ); |
486 | OUTPUT( PKAVerificationSucceeded ); |
487 | #undef OUTPUT |
488 | return os << ')'; |
489 | } |
490 | |
491 | std::ostream & GpgME::operator<<( std::ostream & os, Signature::Summary summary ) { |
492 | #define OUTPUT( x ) if ( !(summary & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0) |
493 | os << "GpgME::Signature::Summary(" ; |
494 | OUTPUT( Valid ); |
495 | OUTPUT( Green ); |
496 | OUTPUT( Red ); |
497 | OUTPUT( KeyRevoked ); |
498 | OUTPUT( KeyExpired ); |
499 | OUTPUT( SigExpired ); |
500 | OUTPUT( KeyMissing ); |
501 | OUTPUT( CrlMissing ); |
502 | OUTPUT( CrlTooOld ); |
503 | OUTPUT( BadPolicy ); |
504 | OUTPUT( SysError ); |
505 | #undef OUTPUT |
506 | return os << ')'; |
507 | } |
508 | |
509 | std::ostream & GpgME::operator<<( std::ostream & os, const Signature & sig ) { |
510 | os << "GpgME::Signature(" ; |
511 | if ( !sig.isNull() ) { |
512 | os << "\n Summary: " << sig.summary() |
513 | << "\n Fingerprint: " << protect( sig.fingerprint() ) |
514 | << "\n Status: " << sig.status() |
515 | << "\n creationTime: " << sig.creationTime() |
516 | << "\n expirationTime: " << sig.expirationTime() |
517 | << "\n isWrongKeyUsage: " << sig.isWrongKeyUsage() |
518 | << "\n isVerifiedUsingChainModel: " << sig.isVerifiedUsingChainModel() |
519 | << "\n pkaStatus: " << sig.pkaStatus() |
520 | << "\n pkaAddress: " << protect( sig.pkaAddress() ) |
521 | << "\n validity: " << sig.validityAsString() |
522 | << "\n nonValidityReason: " << sig.nonValidityReason() |
523 | << "\n publicKeyAlgorithm: " << protect( sig.publicKeyAlgorithmAsString() ) |
524 | << "\n hashAlgorithm: " << protect( sig.hashAlgorithmAsString() ) |
525 | << "\n policyURL: " << protect( sig.policyURL() ) |
526 | << "\n notations:\n" ; |
527 | const std::vector<Notation> nota = sig.notations(); |
528 | std::copy( nota.begin(), nota.end(), |
529 | std::ostream_iterator<Notation>( os, "\n" ) ); |
530 | } |
531 | return os << ')'; |
532 | } |
533 | |
534 | std::ostream & GpgME::operator<<( std::ostream & os, Notation::Flags flags ) { |
535 | os << "GpgME::Notation::Flags(" ; |
536 | #define OUTPUT( x ) if ( !(flags & (GpgME::Notation:: x)) ) {} else do { os << #x " "; } while(0) |
537 | OUTPUT( HumanReadable ); |
538 | OUTPUT( Critical ); |
539 | #undef OUTPUT |
540 | return os << ')'; |
541 | } |
542 | |
543 | std::ostream & GpgME::operator<<( std::ostream & os, const Notation & nota ) { |
544 | os << "GpgME::Signature::Notation(" ; |
545 | if ( !nota.isNull() ) { |
546 | os << "\n name: " << protect( nota.name() ) |
547 | << "\n value: " << protect( nota.value() ) |
548 | << "\n flags: " << nota.flags() |
549 | << '\n'; |
550 | } |
551 | return os << ")" ; |
552 | } |
553 | |