1 /*
2   configuration.cpp - wraps gpgme configuration components
3   Copyright (C) 2010 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 "configuration.h"
30 #include "error.h"
31 #include "util.h"
32 
33 #include <gpgme.h>
34 
35 #include <iterator>
36 #include <algorithm>
37 #include <ostream>
38 #include <cstring>
39 #include <assert.h>
40 
41 using namespace GpgME;
42 using namespace GpgME::Configuration;
43 
44 typedef std::shared_ptr< std::remove_pointer<gpgme_conf_opt_t>::type > shared_gpgme_conf_opt_t;
45 typedef std::weak_ptr< std::remove_pointer<gpgme_conf_opt_t>::type > weak_gpgme_conf_opt_t;
46 
47 typedef std::shared_ptr< std::remove_pointer<gpgme_conf_arg_t>::type > shared_gpgme_conf_arg_t;
48 typedef std::weak_ptr< std::remove_pointer<gpgme_conf_arg_t>::type > weak_gpgme_conf_arg_t;
49 
50 typedef std::shared_ptr< std::remove_pointer<gpgme_ctx_t>::type > shared_gpgme_ctx_t;
51 typedef std::weak_ptr< std::remove_pointer<gpgme_ctx_t>::type > weak_gpgme_ctx_t;
52 
53 namespace
54 {
55 struct nodelete {
operator ()__anon3e2ebe6a0111::nodelete56     template <typename T> void operator()(T *) {}
57 };
58 }
59 
60 // static
load(Error & returnedError)61 std::vector<Component> Component::load(Error &returnedError)
62 {
63 
64     //
65     // 1. get a context:
66     //
67     gpgme_ctx_t ctx_native = nullptr;
68     if (const gpgme_error_t err = gpgme_new(&ctx_native)) {
69         returnedError = Error(err);
70         return std::vector<Component>();
71     }
72     const shared_gpgme_ctx_t ctx(ctx_native, &gpgme_release);
73 
74     //
75     // 2. load the config:
76     //
77     gpgme_conf_comp_t conf_list_native = nullptr;
78     if (const gpgme_error_t err = gpgme_op_conf_load(ctx_native, &conf_list_native)) {
79         returnedError = Error(err);
80         return std::vector<Component>();
81     }
82     shared_gpgme_conf_comp_t head(conf_list_native, &gpgme_conf_release);
83 
84     //
85     // 3. convert to vector<Component>:
86     //
87     std::vector<Component> result;
88 
89     while (head) {
90         // secure 'head->next' (if any) against memleaks:
91         shared_gpgme_conf_comp_t next;
92         if (head->next) {
93             next.reset(head->next, &gpgme_conf_release);
94         }
95 
96         // now prevent double-free of next.get() and following:
97         head->next = nullptr;
98 
99         // now add a new Component to 'result' (may throw):
100         result.resize(result.size() + 1);
101         result.back().comp.swap(head);   // .comp = std::move( head );
102         head.swap(next);                 //  head = std::move( next );
103     }
104 
105     return result;
106 }
107 
save() const108 Error Component::save() const
109 {
110 
111     if (isNull()) {
112         return Error(make_error(GPG_ERR_INV_ARG));
113     }
114 
115     //
116     // 1. get a context:
117     //
118     gpgme_ctx_t ctx_native = nullptr;
119     if (const gpgme_error_t err = gpgme_new(&ctx_native)) {
120         return Error(err);
121     }
122     const shared_gpgme_ctx_t ctx(ctx_native, &gpgme_release);
123 
124     //
125     // 2. save the config:
126     //
127     return Error(gpgme_op_conf_save(ctx.get(), comp.get()));
128 }
129 
name() const130 const char *Component::name() const
131 {
132     return comp ? comp->name : nullptr;
133 }
134 
description() const135 const char *Component::description() const
136 {
137     return comp ? comp->description : nullptr ;
138 }
139 
programName() const140 const char *Component::programName() const
141 {
142     return comp ? comp->program_name : nullptr ;
143 }
144 
option(unsigned int idx) const145 Option Component::option(unsigned int idx) const
146 {
147     gpgme_conf_opt_t opt = nullptr;
148     if (comp) {
149         opt = comp->options;
150     }
151     while (opt && idx) {
152         opt = opt->next;
153         --idx;
154     }
155     if (opt) {
156         return Option(comp, opt);
157     }
158     return Option();
159 }
160 
option(const char * name) const161 Option Component::option(const char *name) const
162 {
163     gpgme_conf_opt_t opt = nullptr;
164     if (comp) {
165         opt = comp->options;
166     }
167     using namespace std; // for strcmp
168     while (opt && strcmp(name, opt->name) != 0) {
169         opt = opt->next;
170     }
171     if (opt) {
172         return Option(comp, opt);
173     }
174     return Option();
175 }
176 
numOptions() const177 unsigned int Component::numOptions() const
178 {
179     unsigned int result = 0;
180     for (gpgme_conf_opt_t opt = comp ? comp->options : nullptr ; opt ; opt = opt->next) {
181         ++result;
182     }
183     return result;
184 }
185 
options() const186 std::vector<Option> Component::options() const
187 {
188     std::vector<Option> result;
189     for (gpgme_conf_opt_t opt = comp ? comp->options : nullptr ; opt ; opt = opt->next) {
190         result.push_back(Option(comp, opt));
191     }
192     return result;
193 }
194 
mygpgme_conf_arg_copy(gpgme_conf_arg_t other,gpgme_conf_type_t type)195 static gpgme_conf_arg_t mygpgme_conf_arg_copy(gpgme_conf_arg_t other, gpgme_conf_type_t type)
196 {
197     gpgme_conf_arg_t result = nullptr, last = nullptr;
198     for (gpgme_conf_arg_t a = other ; a ; a = a->next) {
199         gpgme_conf_arg_t arg = nullptr;
200         const gpgme_error_t err
201             = gpgme_conf_arg_new(&arg, type,
202                                  a->no_arg                 ? nullptr :
203                                  type == GPGME_CONF_STRING ? a->value.string :
204                                  /* else */                  static_cast<void *>(&a->value));
205         if (err) {
206             gpgme_conf_arg_release(result, type);
207             return nullptr;
208         }
209         assert(arg);
210         if (result) {
211             last->next = arg;
212         } else {
213             result = arg;
214         }
215         last = arg;
216     }
217     return result;
218 }
219 
parent() const220 Component Option::parent() const
221 {
222     return Component(comp.lock());
223 }
224 
flags() const225 unsigned int Option::flags() const
226 {
227     return isNull() ? 0 : opt->flags;
228 }
229 
level() const230 Level Option::level() const
231 {
232     return isNull() ? Internal : static_cast<Level>(opt->level) ;
233 }
234 
name() const235 const char *Option::name() const
236 {
237     return isNull() ? nullptr : opt->name ;
238 }
239 
description() const240 const char *Option::description() const
241 {
242     return isNull() ? nullptr : opt->description ;
243 }
244 
argumentName() const245 const char *Option::argumentName() const
246 {
247     return isNull() ? nullptr : opt->argname ;
248 }
249 
type() const250 Type Option::type() const
251 {
252     return isNull() ? NoType : static_cast<Type>(opt->type) ;
253 }
254 
alternateType() const255 Type Option::alternateType() const
256 {
257     return isNull() ? NoType : static_cast<Type>(opt->alt_type) ;
258 }
259 
260 #if 0
261 static Option::Variant argument_to_variant(gpgme_conf_type_t type, bool list, gpgme_conf_arg_t arg)
262 {
263     assert(arg);
264     switch (type) {
265     case GPGME_CONF_NONE:
266         if (list) {
267             // return the count (number of times set):
268             return arg->value.count;
269         } else {
270             return none;
271         }
272     case GPGME_CONF_INT32:
273         if (list) {
274             std::vector<int> result;
275             for (gpgme_conf_arg_t a = arg ; a ; a = a->next) {
276                 result.push_back(a->value.int32);
277             }
278             return result;
279         } else {
280             return arg->value.int32;
281         }
282     case GPGME_CONF_UINT32:
283         if (list) {
284             std::vector<unsigned int> result;
285             for (gpgme_conf_arg_t a = arg ; a ; a = a->next) {
286                 result.push_back(a->value.uint32);
287             }
288             return result;
289         } else {
290             return arg->value.uint32;
291         }
292     case GPGME_CONF_FILENAME:
293     case GPGME_CONF_LDAP_SERVER:
294     case GPGME_CONF_KEY_FPR:
295     case GPGME_CONF_PUB_KEY:
296     case GPGME_CONF_SEC_KEY:
297     case GPGME_CONF_ALIAS_LIST:
298     // these should not happen in alt_type, but fall through
299     case GPGME_CONF_STRING:
300         if (list) {
301             std::vector<const char *> result;
302             for (gpgme_conf_arg_t a = arg ; a ; a = a->next) {
303                 result.push_back(a->value.string);
304             }
305             return result;
306         } else {
307             return arg->value.string;
308         }
309     }
310     assert(!"Option: unknown alt_type!");
311     return Option::Variant();
312 }
313 
314 namespace
315 {
316 inline const void *to_void_star(const char *s)
317 {
318     return s;
319 }
320 inline const void *to_void_star(const std::string &s)
321 {
322     return s.c_str();
323 }
324 inline const void *to_void_star(const int &i)
325 {
326     return &i;    // const-&: sic!
327 }
328 inline const void *to_void_star(const unsigned int &i)
329 {
330     return &i;    // const-&: sic!
331 }
332 
333 struct VariantToArgumentVisitor : boost::static_visitor<gpgme_conf_arg_t> {
334     static gpgme_conf_arg_t make_argument(gpgme_conf_type_t type, const void *value)
335     {
336         gpgme_conf_arg_t arg = 0;
337 #ifdef HAVE_GPGME_CONF_ARG_NEW_WITH_CONST_VALUE
338         if (const gpgme_error_t err = gpgme_conf_arg_new(&arg, type, value)) {
339             return 0;
340         }
341 #else
342         if (const gpgme_error_t err = gpgme_conf_arg_new(&arg, type, const_cast<void *>(value))) {
343             return 0;
344         }
345 #endif
346         else {
347             return arg;
348         }
349     }
350 
351     gpgme_conf_arg_t operator()(bool v) const
352     {
353         return v ? make_argument(0) : 0 ;
354     }
355 
356     gpgme_conf_arg_t operator()(const char *s) const
357     {
358         return make_argument(s ? s : "");
359     }
360 
361     gpgme_conf_arg_t operator()(const std::string &s) const
362     {
363         return operator()(s.c_str());
364     }
365 
366     gpgme_conf_arg_t operator()(int i) const
367     {
368         return make_argument(&i);
369     }
370 
371     gpgme_conf_arg_t operator()(unsigned int i) const
372     {
373         return make_argument(&i);
374     }
375 
376     template <typename T>
377     gpgme_conf_arg_t operator()(const std::vector<T> &value) const
378     {
379         gpgme_conf_arg_t result = 0;
380         gpgme_conf_arg_t last = 0;
381         for (typename std::vector<T>::const_iterator it = value.begin(), end = value.end() ; it != end ; ++it) {
382             if (gpgme_conf_arg_t arg = make_argument(to_void_star(*it))) {
383                 if (last) {
384                     last = last->next = arg;
385                 } else {
386                     result = last = arg;
387                 }
388             }
389         }
390         return result;
391     }
392 
393 };
394 }
395 
396 static gpgme_conf_arg_t variant_to_argument(const Option::Variant &value)
397 {
398     VariantToArgumentVisitor v;
399     return apply_visitor(v, value);
400 }
401 
402 optional<Option::Variant> Option::defaultValue() const
403 {
404     if (isNull()) {
405         return optional<Variant>();
406     } else {
407         return argument_to_variant(opt->alt_type, opt->flags & GPGME_CONF_LIST, opt->default_value);
408     }
409 }
410 #endif
411 
defaultValue() const412 Argument Option::defaultValue() const
413 {
414     if (isNull()) {
415         return Argument();
416     } else {
417         return Argument(comp.lock(), opt, opt->default_value, false);
418     }
419 }
420 
defaultDescription() const421 const char *Option::defaultDescription() const
422 {
423     return isNull() ? nullptr : opt->default_description ;
424 }
425 
noArgumentValue() const426 Argument Option::noArgumentValue() const
427 {
428     if (isNull()) {
429         return Argument();
430     } else {
431         return Argument(comp.lock(), opt, opt->no_arg_value, false);
432     }
433 }
434 
noArgumentDescription() const435 const char *Option::noArgumentDescription() const
436 {
437     return isNull() ? nullptr : opt->no_arg_description ;
438 }
439 
activeValue() const440 Argument Option::activeValue() const
441 {
442     if (isNull()) {
443         return Argument();
444     } else {
445         return Argument(comp.lock(), opt, opt->value, false);
446     }
447 }
448 
currentValue() const449 Argument Option::currentValue() const
450 {
451     if (isNull()) {
452         return Argument();
453     }
454     const gpgme_conf_arg_t arg =
455         opt->change_value ? opt->new_value ? opt->new_value : opt->default_value :
456         opt->value        ? opt->value :
457         /* else */          opt->default_value ;
458     return Argument(comp.lock(), opt, arg, false);
459 }
460 
newValue() const461 Argument Option::newValue() const
462 {
463     if (isNull()) {
464         return Argument();
465     } else {
466         return Argument(comp.lock(), opt, opt->new_value, false);
467     }
468 }
469 
set() const470 bool Option::set() const
471 {
472     if (isNull()) {
473         return false;
474     } else if (opt->change_value) {
475         return opt->new_value;
476     } else {
477         return opt->value;
478     }
479 }
480 
dirty() const481 bool Option::dirty() const
482 {
483     return !isNull() && opt->change_value ;
484 }
485 
setNewValue(const Argument & argument)486 Error Option::setNewValue(const Argument &argument)
487 {
488     if (isNull()) {
489         return Error(make_error(GPG_ERR_INV_ARG));
490     } else if (argument.isNull()) {
491         return resetToDefaultValue();
492     } else if (const gpgme_conf_arg_t arg = mygpgme_conf_arg_copy(argument.arg, opt->alt_type)) {
493         return Error(gpgme_conf_opt_change(opt, 0, arg));
494     } else {
495         return Error(make_error(GPG_ERR_ENOMEM));
496     }
497 }
498 
resetToActiveValue()499 Error Option::resetToActiveValue()
500 {
501     if (isNull()) {
502         return Error(make_error(GPG_ERR_INV_ARG));
503     } else {
504         return Error(gpgme_conf_opt_change(opt, 1, nullptr));
505     }
506 }
507 
resetToDefaultValue()508 Error Option::resetToDefaultValue()
509 {
510     if (isNull()) {
511         return Error(make_error(GPG_ERR_INV_ARG));
512     } else {
513         return Error(gpgme_conf_opt_change(opt, 0, nullptr));
514     }
515 }
516 
make_argument(gpgme_conf_type_t type,const void * value)517 static gpgme_conf_arg_t make_argument(gpgme_conf_type_t type, const void *value)
518 {
519     gpgme_conf_arg_t arg = nullptr;
520     if (const gpgme_error_t err = gpgme_conf_arg_new(&arg, type, value)) {
521         return nullptr;
522     } else {
523         return arg;
524     }
525 }
526 
createNoneArgument(bool set) const527 Argument Option::createNoneArgument(bool set) const
528 {
529     if (isNull() || alternateType() != NoType) {
530         return Argument();
531     } else {
532         if (set) {
533             return createNoneListArgument(1);
534         }
535     }
536     return Argument();
537 }
538 
createStringArgument(const char * value) const539 Argument Option::createStringArgument(const char *value) const
540 {
541     if (isNull() || alternateType() != StringType) {
542         return Argument();
543     } else {
544         return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value), true);
545     }
546 }
547 
createStringArgument(const std::string & value) const548 Argument Option::createStringArgument(const std::string &value) const
549 {
550     if (isNull() || alternateType() != StringType) {
551         return Argument();
552     } else {
553         return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value.c_str()), true);
554     }
555 }
556 
createIntArgument(int value) const557 Argument Option::createIntArgument(int value) const
558 {
559     if (isNull() || alternateType() != IntegerType) {
560         return Argument();
561     } else {
562         return Argument(comp.lock(), opt, make_argument(GPGME_CONF_INT32, &value), true);
563     }
564 }
565 
createUIntArgument(unsigned int value) const566 Argument Option::createUIntArgument(unsigned int value) const
567 {
568     if (isNull() || alternateType() != UnsignedIntegerType) {
569         return Argument();
570     } else {
571         return Argument(comp.lock(), opt, make_argument(GPGME_CONF_UINT32, &value), true);
572     }
573 }
574 
575 namespace
576 {
to_void_star(const char * s)577 const void *to_void_star(const char *s)
578 {
579     return s;
580 }
to_void_star(const std::string & s)581 const void *to_void_star(const std::string &s)
582 {
583     return s.c_str();
584 }
to_void_star(const int & i)585 const void *to_void_star(const int &i)
586 {
587     return &i;    // const-&: sic!
588 }
to_void_star(const unsigned int & i)589 const void *to_void_star(const unsigned int &i)
590 {
591     return &i;    // const-&: sic!
592 }
593 
594 template <typename T>
make_argument(gpgme_conf_type_t type,const std::vector<T> & value)595 gpgme_conf_arg_t make_argument(gpgme_conf_type_t type, const std::vector<T> &value)
596 {
597     gpgme_conf_arg_t result = nullptr;
598     gpgme_conf_arg_t last = nullptr;
599     for (typename std::vector<T>::const_iterator it = value.begin(), end = value.end() ; it != end ; ++it) {
600         if (gpgme_conf_arg_t arg = make_argument(type, to_void_star(*it))) {
601             if (last) {
602                 last = last->next = arg;
603             } else {
604                 result = last = arg;
605             }
606         }
607     }
608     return result;
609 }
610 }
611 
createNoneListArgument(unsigned int value) const612 Argument Option::createNoneListArgument(unsigned int value) const
613 {
614     if (value) {
615         return Argument(comp.lock(), opt, make_argument(GPGME_CONF_NONE, &value), true);
616     }
617     return Argument();
618 }
619 
createStringListArgument(const std::vector<const char * > & value) const620 Argument Option::createStringListArgument(const std::vector<const char *> &value) const
621 {
622     return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value), true);
623 }
624 
createStringListArgument(const std::vector<std::string> & value) const625 Argument Option::createStringListArgument(const std::vector<std::string> &value) const
626 {
627     return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value), true);
628 }
629 
createIntListArgument(const std::vector<int> & value) const630 Argument Option::createIntListArgument(const std::vector<int> &value) const
631 {
632     return Argument(comp.lock(), opt, make_argument(GPGME_CONF_INT32, value), true);
633 }
634 
createUIntListArgument(const std::vector<unsigned int> & value) const635 Argument Option::createUIntListArgument(const std::vector<unsigned int> &value) const
636 {
637     return Argument(comp.lock(), opt, make_argument(GPGME_CONF_UINT32, value), true);
638 }
639 
Argument(const shared_gpgme_conf_comp_t & comp,gpgme_conf_opt_t opt,gpgme_conf_arg_t arg,bool owns)640 Argument::Argument(const shared_gpgme_conf_comp_t &comp, gpgme_conf_opt_t opt, gpgme_conf_arg_t arg, bool owns)
641     : comp(comp),
642       opt(opt),
643       arg(owns ? arg : mygpgme_conf_arg_copy(arg, opt ? opt->alt_type : GPGME_CONF_NONE))
644 {
645 
646 }
647 
648 #if 0
649 Argument::Argument(const shared_gpgme_conf_comp_t &comp, gpgme_conf_opt_t opt, gpgme_conf_arg_t arg)
650     : comp(comp),
651       opt(opt),
652       arg(mygpgme_conf_arg_copy(arg, opt ? opt->alt_type : GPGME_CONF_NONE))
653 {
654 
655 }
656 #endif
657 
Argument(const Argument & other)658 Argument::Argument(const Argument &other)
659     : comp(other.comp),
660       opt(other.opt),
661       arg(mygpgme_conf_arg_copy(other.arg, opt ? opt->alt_type : GPGME_CONF_NONE))
662 {
663 
664 }
665 
~Argument()666 Argument::~Argument()
667 {
668     gpgme_conf_arg_release(arg, opt ? opt->alt_type : GPGME_CONF_NONE);
669 }
670 
parent() const671 Option Argument::parent() const
672 {
673     return Option(comp.lock(), opt);
674 }
675 
boolValue() const676 bool Argument::boolValue() const
677 {
678     return numberOfTimesSet();
679 }
680 
numElements() const681 unsigned int Argument::numElements() const
682 {
683     if (isNull()) {
684         return 0;
685     }
686     unsigned int result = 0;
687     for (gpgme_conf_arg_t a = arg ; a ; a = a->next) {
688         ++result;
689     }
690     return result;
691 }
692 
stringValue(unsigned int idx) const693 const char *Argument::stringValue(unsigned int idx) const
694 {
695     if (isNull() || opt->alt_type != GPGME_CONF_STRING) {
696         return nullptr;
697     }
698     gpgme_conf_arg_t a = arg;
699     while (a && idx) {
700         a = a->next;
701         --idx;
702     }
703     return a ? a->value.string : nullptr ;
704 }
705 
intValue(unsigned int idx) const706 int Argument::intValue(unsigned int idx) const
707 {
708     if (isNull() || opt->alt_type != GPGME_CONF_INT32) {
709         return 0;
710     }
711     gpgme_conf_arg_t a = arg;
712     while (a && idx) {
713         a = a->next;
714         --idx;
715     }
716     return a ? a->value.int32 : 0 ;
717 }
718 
uintValue(unsigned int idx) const719 unsigned int Argument::uintValue(unsigned int idx) const
720 {
721     if (isNull() || opt->alt_type != GPGME_CONF_UINT32) {
722         return 0;
723     }
724     gpgme_conf_arg_t a = arg;
725     while (a && idx) {
726         a = a->next;
727         --idx;
728     }
729     return a ? a->value.uint32 : 0 ;
730 }
731 
numberOfTimesSet() const732 unsigned int Argument::numberOfTimesSet() const
733 {
734     if (isNull() || opt->alt_type != GPGME_CONF_NONE) {
735         return 0;
736     }
737     return arg->value.count;
738 }
739 
stringValues() const740 std::vector<const char *> Argument::stringValues() const
741 {
742     if (isNull() || opt->alt_type != GPGME_CONF_STRING) {
743         return std::vector<const char *>();
744     }
745     std::vector<const char *> result;
746     for (gpgme_conf_arg_t a = arg ; a ; a = a->next) {
747         result.push_back(a->value.string);
748     }
749     return result;
750 }
751 
intValues() const752 std::vector<int> Argument::intValues() const
753 {
754     if (isNull() || opt->alt_type != GPGME_CONF_INT32) {
755         return std::vector<int>();
756     }
757     std::vector<int> result;
758     for (gpgme_conf_arg_t a = arg ; a ; a = a->next) {
759         result.push_back(a->value.int32);
760     }
761     return result;
762 }
763 
uintValues() const764 std::vector<unsigned int> Argument::uintValues() const
765 {
766     if (isNull() || opt->alt_type != GPGME_CONF_UINT32) {
767         return std::vector<unsigned int>();
768     }
769     std::vector<unsigned int> result;
770     for (gpgme_conf_arg_t a = arg ; a ; a = a->next) {
771         result.push_back(a->value.uint32);
772     }
773     return result;
774 }
775 
operator <<(std::ostream & os,Level level)776 std::ostream &Configuration::operator<<(std::ostream &os, Level level)
777 {
778     switch (level) {
779     case Basic:     return os << "Basic";
780     case Advanced:  return os << "Advanced";
781     case Expert:    return os << "Expert";
782     case Invisible: return os << "Invisible";
783     case Internal:  return os << "Internal";
784     case NumLevels: ;
785     }
786     return os << "<unknown>";
787 }
788 
operator <<(std::ostream & os,Type type)789 std::ostream &Configuration::operator<<(std::ostream &os, Type type)
790 {
791     switch (type) {
792     case NoType:              return os << "None";
793     case StringType:          return os << "String";
794     case IntegerType:         return os << "Integer";
795     case UnsignedIntegerType: return os << "UnsignedInteger";
796     case FilenameType:        return os << "Filename";
797     case LdapServerType:      return os << "LdapServer";
798     case KeyFingerprintType:  return os << "KeyFingerprint";
799     case PublicKeyType:       return os << "PublicKey";
800     case SecretKeyType:       return os << "SecretKey";
801     case AliasListType:       return os << "AliasList";
802     case MaxType: ;
803     }
804     return os << "<unknown>";
805 }
806 
operator <<(std::ostream & os,Flag f)807 std::ostream &Configuration::operator<<(std::ostream &os, Flag f)
808 {
809     unsigned int flags = f;
810     std::vector<const char *> s;
811     if (flags & Group) {
812         s.push_back("Group");
813     }
814     if (flags & Optional) {
815         s.push_back("Optional");
816     }
817     if (flags & List) {
818         s.push_back("List");
819     }
820     if (flags & Runtime) {
821         s.push_back("Runtime");
822     }
823     if (flags & Default) {
824         s.push_back("Default");
825     }
826     if (flags & DefaultDescription) {
827         s.push_back("DefaultDescription");
828     }
829     if (flags & NoArgumentDescription) {
830         s.push_back("NoArgumentDescription");
831     }
832     if (flags & NoChange) {
833         s.push_back("NoChange");
834     }
835     flags &= ~(Group | Optional | List | Runtime | Default | DefaultDescription | NoArgumentDescription | NoChange);
836     if (flags) {
837         s.push_back("other flags(");
838     }
839     std::copy(s.begin(), s.end(),
840               std::ostream_iterator<const char *>(os, "|"));
841     if (flags) {
842         os << flags << ')';
843     }
844     return os;
845 }
846 
operator <<(std::ostream & os,const Component & c)847 std::ostream &Configuration::operator<<(std::ostream &os, const Component &c)
848 {
849     os << "Component["
850        << "\n  name       : " << protect(c.name())
851        << "\n  description: " << protect(c.description())
852        << "\n  programName: " << protect(c.programName())
853        << "\n  options    : \n";
854     const std::vector<Option> options = c.options();
855     std::copy(options.begin(), options.end(),
856               std::ostream_iterator<Option>(os, "\n"));
857     os << "\n]";
858     return os;
859 }
860 
operator <<(std::ostream & os,const Option & o)861 std::ostream &Configuration::operator<<(std::ostream &os, const Option &o)
862 {
863     return os << "Option["
864            << "\n  name:       : " << protect(o.name())
865            << "\n  description : " << protect(o.description())
866            << "\n  argName     : " << protect(o.argumentName())
867            << "\n  flags       : " << static_cast<Flag>(o.flags())
868            << "\n  level       : " << o.level()
869            << "\n  type        : " << o.type()
870            << "\n  alt_type    : " << o.alternateType()
871            << "\n  default_val : " << o.defaultValue()
872            << "\n  default_desc: " << protect(o.defaultDescription())
873            << "\n  no_arg_value: " << o.noArgumentValue()
874            << "\n  no_arg_desc : " << protect(o.noArgumentDescription())
875            << "\n  active_value: " << o.activeValue()
876            << "\n  new_value   : " << o.newValue()
877            << "\n  --> cur_val : " << o.currentValue()
878            << "\n  set         : " << o.set()
879            << "\n  dirty       : " << o.dirty()
880            << "\n]"
881            ;
882 }
883 
operator <<(std::ostream & os,const Argument & a)884 std::ostream &Configuration::operator<<(std::ostream &os, const Argument &a)
885 {
886     const Option o = a.parent();
887     const bool list = o.flags() & List;
888     os << "Argument[";
889     if (a) {
890         switch (o.alternateType()) {
891         case NoType:
892             if (list) {
893                 os << a.numberOfTimesSet() << 'x';
894             } else {
895                 os << a.boolValue();
896             }
897             break;
898         default:
899         case StringType:
900             if (list) {
901                 const std::vector<const char *> v = a.stringValues();
902                 os << v.size() << ':';
903                 // can't use std::copy + ostream_iterator here, since we need the protect() call
904                 bool first = true;
905                 std::for_each(v.begin(), v.end(), [&first, &os](const char *s) {
906                     if (first) {
907                         first = false;
908                     } else {
909                         os << ',';
910                     }
911                     os << protect(s);
912                 });
913             } else {
914                 os << protect(a.stringValue());
915             }
916             break;
917         case IntegerType:
918             if (list) {
919                 const std::vector<int> v = a.intValues();
920                 os << v.size() << ':';
921                 std::copy(v.begin(), v.end(),
922                           std::ostream_iterator<int>(os, ","));
923             } else {
924                 os << a.intValue();
925             }
926             break;
927         case UnsignedIntegerType:
928             if (list) {
929                 const std::vector<unsigned int> v = a.uintValues();
930                 os << v.size() << ':';
931                 std::copy(v.begin(), v.end(),
932                           std::ostream_iterator<unsigned int>(os, ","));
933             } else {
934                 os << a.intValue();
935             }
936             break;
937         }
938     }
939     return os << ']';
940 }
941