aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/exthdrs_core.c
blob: 49e31e4ae7b7f661d555e73e0515983e9584eec4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
// SPDX-License-Identifier: GPL-2.0-only
/*
 * IPv6 library code, needed by static components when full IPv6 support is
 * not configured or static.
 */
#include <linux/export.h>
#include <net/ipv6.h>

/*
 * find out if nexthdr is a well-known extension header or a protocol
 */

bool ipv6_ext_hdr(u8 nexthdr)
{
	/*
	 * find out if nexthdr is an extension header or a protocol
	 */
	return   (nexthdr == NEXTHDR_HOP)	||
		 (nexthdr == NEXTHDR_ROUTING)	||
		 (nexthdr == NEXTHDR_FRAGMENT)	||
		 (nexthdr == NEXTHDR_AUTH)	||
		 (nexthdr == NEXTHDR_NONE)	||
		 (nexthdr == NEXTHDR_DEST);
}
EXPORT_SYMBOL(ipv6_ext_hdr);

/*
 * Skip any extension headers. This is used by the ICMP module.
 *
 * Note that strictly speaking this conflicts with RFC 2460 4.0:
 * ...The contents and semantics of each extension header determine whether
 * or not to proceed to the next header.  Therefore, extension headers must
 * be processed strictly in the order they appear in the packet; a
 * receiver must not, for example, scan through a packet looking for a
 * particular kind of extension header and process that header prior to
 * processing all preceding ones.
 *
 * We do exactly this. This is a protocol bug. We can't decide after a
 * seeing an unknown discard-with-error flavour TLV option if it's a
 * ICMP error message or not (errors should never be send in reply to
 * ICMP error messages).
 *
 * But I see no other way to do this. This might need to be reexamined
 * when Linux implements ESP (and maybe AUTH) headers.
 * --AK
 *
 * This function parses (probably truncated) exthdr set "hdr".
 * "nexthdrp" initially points to some place,
 * where type of the first header can be found.
 *
 * It skips all well-known exthdrs, and returns pointer to the start
 * of unparsable area i.e. the first header with unknown type.
 * If it is not NULL *nexthdr is updated by type/protocol of this header.
 *
 * NOTES: - if packet terminated with NEXTHDR_NONE it returns NULL.
 *        - it may return pointer pointing beyond end of packet,
 *	    if the last recognized header is truncated in the middle.
 *        - if packet is truncated, so that all parsed headers are skipped,
 *	    it returns NULL.
 *	  - First fragment header is skipped, not-first ones
 *	    are considered as unparsable.
 *	  - Reports the offset field of the final fragment header so it is
 *	    possible to tell whether this is a first fragment, later fragment,
 *	    or not fragmented.
 *	  - ESP is unparsable for now and considered like
 *	    normal payload protocol.
 *	  - Note also special handling of AUTH header. Thanks to IPsec wizards.
 *
 * --ANK (980726)
 */

int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
		     __be16 *frag_offp)
{
	u8 nexthdr = *nexthdrp;

	*frag_offp = 0;

	while (ipv6_ext_hdr(nexthdr)) {
		struct ipv6_opt_hdr _hdr, *hp;
		int hdrlen;

		if (nexthdr == NEXTHDR_NONE)
			return -1;
		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
		if (!hp)
			return -1;
		if (nexthdr == NEXTHDR_FRAGMENT) {
			__be16 _frag_off, *fp;
			fp = skb_header_pointer(skb,
						start+offsetof(struct frag_hdr,
							       frag_off),
						sizeof(_frag_off),
						&_frag_off);
			if (!fp)
				return -1;

			*frag_offp = *fp;
			if (ntohs(*frag_offp) & ~0x7)
				break;
			hdrlen = 8;
		} else if (nexthdr == NEXTHDR_AUTH)
			hdrlen = ipv6_authlen(hp);
		else
			hdrlen = ipv6_optlen(hp);

		nexthdr = hp->nexthdr;
		start += hdrlen;
	}

	*nexthdrp = nexthdr;
	return start;
}
EXPORT_SYMBOL(ipv6_skip_exthdr);

int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type)
{
	const unsigned char *nh = skb_network_header(skb);
	int packet_len = skb_tail_pointer(skb) - skb_network_header(skb);
	struct ipv6_opt_hdr *hdr;
	int len;

	if (offset + 2 > packet_len)
		goto bad;
	hdr = (struct ipv6_opt_hdr *)(nh + offset);
	len = ((hdr->hdrlen + 1) << 3);

	if (offset + len > packet_len)
		goto bad;

	offset += 2;
	len -= 2;

	while (len > 0) {
		int opttype = nh[offset];
		int optlen;

		if (opttype == type)
			return offset;

		switch (opttype) {
		case IPV6_TLV_PAD1:
			optlen = 1;
			break;
		default:
			if (len < 2)
				goto bad;
			optlen = nh[offset + 1] + 2;
			if (optlen > len)
				goto bad;
			break;
		}
		offset += optlen;
		len -= optlen;
	}
	/* not_found */
 bad:
	return -1;
}
EXPORT_SYMBOL_GPL(ipv6_find_tlv);

/*
 * find the offset to specified header or the protocol number of last header
 * if target < 0. "last header" is transport protocol header, ESP, or
 * "No next header".
 *
 * Note that *offset is used as input/output parameter, and if it is not zero,
 * then it must be a valid offset to an inner IPv6 header. This can be used
 * to explore inner IPv6 header, eg. ICMPv6 error messages.
 *
 * If target header is found, its offset is set in *offset and return protocol
 * number. Otherwise, return -1.
 *
 * If the first fragment doesn't contain the final protocol header or
 * NEXTHDR_NONE it is considered invalid.
 *
 * Note that non-1st fragment is special case that "the protocol number
 * of last header" is "next header" field in Fragment header. In this case,
 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
 * isn't NULL.
 *
 * if flags is not NULL and it's a fragment, then the frag flag
 * IP6_FH_F_FRAG will be set. If it's an AH header, the
 * IP6_FH_F_AUTH flag is set and target < 0, then this function will
 * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this
 * function will skip all those routing headers, where segements_left was 0.
 */
int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
		  int target, unsigned short *fragoff, int *flags)
{
	unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
	u8 nexthdr = ipv6_hdr(skb)->nexthdr;
	bool found;

	if (fragoff)
		*fragoff = 0;

	if (*offset) {
		struct ipv6hdr _ip6, *ip6;

		ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
		if (!ip6 || (ip6->version != 6))
			return -EBADMSG;
		start = *offset + sizeof(struct ipv6hdr);
		nexthdr = ip6->nexthdr;
	}

	do {
		struct ipv6_opt_hdr _hdr, *hp;
		unsigned int hdrlen;
		found = (nexthdr == target);

		if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
			if (target < 0 || found)
				break;
			return -ENOENT;
		}

		hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
		if (!hp)
			return -EBADMSG;

		if (nexthdr == NEXTHDR_ROUTING) {
			struct ipv6_rt_hdr _rh, *rh;

			rh = skb_header_pointer(skb, start, sizeof(_rh),
						&_rh);
			if (!rh)
				return -EBADMSG;

			if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
			    rh->segments_left == 0)
				found = false;
		}

		if (nexthdr == NEXTHDR_FRAGMENT) {
			unsigned short _frag_off;
			__be16 *fp;

			if (flags)	/* Indicate that this is a fragment */
				*flags |= IP6_FH_F_FRAG;
			fp = skb_header_pointer(skb,
						start+offsetof(struct frag_hdr,
							       frag_off),
						sizeof(_frag_off),
						&_frag_off);
			if (!fp)
				return -EBADMSG;

			_frag_off = ntohs(*fp) & ~0x7;
			if (_frag_off) {
				if (target < 0 &&
				    ((!ipv6_ext_hdr(hp->nexthdr)) ||
				     hp->nexthdr == NEXTHDR_NONE)) {
					if (fragoff)
						*fragoff = _frag_off;
					return hp->nexthdr;
				}
				if (!found)
					return -ENOENT;
				if (fragoff)
					*fragoff = _frag_off;
				break;
			}
			hdrlen = 8;
		} else if (nexthdr == NEXTHDR_AUTH) {
			if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
				break;
			hdrlen = ipv6_authlen(hp);
		} else
			hdrlen = ipv6_optlen(hp);

		if (!found) {
			nexthdr = hp->nexthdr;
			start += hdrlen;
		}
	} while (!found);

	*offset = start;
	return nexthdr;
}
EXPORT_SYMBOL(ipv6_find_hdr);
r<gpgme_conf_arg_t>::type > weak_gpgme_conf_arg_t; typedef std::shared_ptr< std::remove_pointer<gpgme_ctx_t>::type > shared_gpgme_ctx_t; typedef std::weak_ptr< std::remove_pointer<gpgme_ctx_t>::type > weak_gpgme_ctx_t; namespace { struct nodelete { template <typename T> void operator()(T *) {} }; } // static std::vector<Component> Component::load(Error &returnedError) { // // 1. get a context: // gpgme_ctx_t ctx_native = 0; if (const gpgme_error_t err = gpgme_new(&ctx_native)) { returnedError = Error(err); return std::vector<Component>(); } const shared_gpgme_ctx_t ctx(ctx_native, &gpgme_release); // // 2. load the config: // gpgme_conf_comp_t conf_list_native = 0; if (const gpgme_error_t err = gpgme_op_conf_load(ctx_native, &conf_list_native)) { returnedError = Error(err); return std::vector<Component>(); } shared_gpgme_conf_comp_t head(conf_list_native, &gpgme_conf_release); // // 3. convert to vector<Component>: // std::vector<Component> result; while (head) { // secure 'head->next' (if any) against memleaks: shared_gpgme_conf_comp_t next; if (head->next) { next.reset(head->next, &gpgme_conf_release); } // now prevent double-free of next.get() and following: head->next = 0; // now add a new Component to 'result' (may throw): result.resize(result.size() + 1); result.back().comp.swap(head); // .comp = std::move( head ); head.swap(next); // head = std::move( next ); } return result; } Error Component::save() const { if (isNull()) { return Error(make_error(GPG_ERR_INV_ARG)); } // // 1. get a context: // gpgme_ctx_t ctx_native = 0; if (const gpgme_error_t err = gpgme_new(&ctx_native)) { return Error(err); } const shared_gpgme_ctx_t ctx(ctx_native, &gpgme_release); // // 2. save the config: // return Error(gpgme_op_conf_save(ctx.get(), comp.get())); } const char *Component::name() const { return comp ? comp->name : 0 ; } const char *Component::description() const { return comp ? comp->description : 0 ; } const char *Component::programName() const { return comp ? comp->program_name : 0 ; } Option Component::option(unsigned int idx) const { gpgme_conf_opt_t opt = 0; if (comp) { opt = comp->options; } while (opt && idx) { opt = opt->next; --idx; } if (opt) { return Option(comp, opt); } return Option(); } Option Component::option(const char *name) const { gpgme_conf_opt_t opt = 0; if (comp) { opt = comp->options; } using namespace std; // for strcmp while (opt && strcmp(name, opt->name) != 0) { opt = opt->next; } if (opt) { return Option(comp, opt); } return Option(); } unsigned int Component::numOptions() const { unsigned int result = 0; for (gpgme_conf_opt_t opt = comp ? comp->options : 0 ; opt ; opt = opt->next) { ++result; } return result; } std::vector<Option> Component::options() const { std::vector<Option> result; for (gpgme_conf_opt_t opt = comp ? comp->options : 0 ; opt ; opt = opt->next) { result.push_back(Option(comp, opt)); } return result; } static gpgme_conf_arg_t mygpgme_conf_arg_copy(gpgme_conf_arg_t other, gpgme_conf_type_t type) { gpgme_conf_arg_t result = 0, last = 0; for (gpgme_conf_arg_t a = other ; a ; a = a->next) { gpgme_conf_arg_t arg = 0; const gpgme_error_t err = gpgme_conf_arg_new(&arg, type, a->no_arg ? 0 : type == GPGME_CONF_STRING ? a->value.string : /* else */ static_cast<void *>(&a->value)); if (err) { gpgme_conf_arg_release(result, type); return 0; } assert(arg); if (result) { last->next = arg; } else { result = arg; } last = arg; } return result; } Component Option::parent() const { return Component(comp.lock()); } unsigned int Option::flags() const { return isNull() ? 0 : opt->flags; } Level Option::level() const { return isNull() ? Internal : static_cast<Level>(opt->level) ; } const char *Option::name() const { return isNull() ? 0 : opt->name ; } const char *Option::description() const { return isNull() ? 0 : opt->description ; } const char *Option::argumentName() const { return isNull() ? 0 : opt->argname ; } Type Option::type() const { return isNull() ? NoType : static_cast<Type>(opt->type) ; } Type Option::alternateType() const { return isNull() ? NoType : static_cast<Type>(opt->alt_type) ; } #if 0 static Option::Variant argument_to_variant(gpgme_conf_type_t type, bool list, gpgme_conf_arg_t arg) { assert(arg); switch (type) { case GPGME_CONF_NONE: if (list) { // return the count (number of times set): return arg->value.count; } else { return none; } case GPGME_CONF_INT32: if (list) { std::vector<int> result; for (gpgme_conf_arg_t a = arg ; a ; a = a->next) { result.push_back(a->value.int32); } return result; } else { return arg->value.int32; } case GPGME_CONF_UINT32: if (list) { std::vector<unsigned int> result; for (gpgme_conf_arg_t a = arg ; a ; a = a->next) { result.push_back(a->value.uint32); } return result; } else { return arg->value.uint32; } case GPGME_CONF_FILENAME: case GPGME_CONF_LDAP_SERVER: case GPGME_CONF_KEY_FPR: case GPGME_CONF_PUB_KEY: case GPGME_CONF_SEC_KEY: case GPGME_CONF_ALIAS_LIST: // these should not happen in alt_type, but fall through case GPGME_CONF_STRING: if (list) { std::vector<const char *> result; for (gpgme_conf_arg_t a = arg ; a ; a = a->next) { result.push_back(a->value.string); } return result; } else { return arg->value.string; } } assert(!"Option: unknown alt_type!"); return Option::Variant(); } namespace { inline const void *to_void_star(const char *s) { return s; } inline const void *to_void_star(const std::string &s) { return s.c_str(); } inline const void *to_void_star(const int &i) { return &i; // const-&: sic! } inline const void *to_void_star(const unsigned int &i) { return &i; // const-&: sic! } struct VariantToArgumentVisitor : boost::static_visitor<gpgme_conf_arg_t> { static gpgme_conf_arg_t make_argument(gpgme_conf_type_t type, const void *value) { gpgme_conf_arg_t arg = 0; #ifdef HAVE_GPGME_CONF_ARG_NEW_WITH_CONST_VALUE if (const gpgme_error_t err = gpgme_conf_arg_new(&arg, type, value)) { return 0; } #else if (const gpgme_error_t err = gpgme_conf_arg_new(&arg, type, const_cast<void *>(value))) { return 0; } #endif else { return arg; } } gpgme_conf_arg_t operator()(bool v) const { return v ? make_argument(0) : 0 ; } gpgme_conf_arg_t operator()(const char *s) const { return make_argument(s ? s : ""); } gpgme_conf_arg_t operator()(const std::string &s) const { return operator()(s.c_str()); } gpgme_conf_arg_t operator()(int i) const { return make_argument(&i); } gpgme_conf_arg_t operator()(unsigned int i) const { return make_argument(&i); } template <typename T> gpgme_conf_arg_t operator()(const std::vector<T> &value) const { gpgme_conf_arg_t result = 0; gpgme_conf_arg_t last = 0; for (typename std::vector<T>::const_iterator it = value.begin(), end = value.end() ; it != end ; ++it) { if (gpgme_conf_arg_t arg = make_argument(to_void_star(*it))) { if (last) { last = last->next = arg; } else { result = last = arg; } } } return result; } }; } static gpgme_conf_arg_t variant_to_argument(const Option::Variant &value) { VariantToArgumentVisitor v; return apply_visitor(v, value); } optional<Option::Variant> Option::defaultValue() const { if (isNull()) { return optional<Variant>(); } else { return argument_to_variant(opt->alt_type, opt->flags & GPGME_CONF_LIST, opt->default_value); } } #endif Argument Option::defaultValue() const { if (isNull()) { return Argument(); } else { return Argument(comp.lock(), opt, opt->default_value, false); } } const char *Option::defaultDescription() const { return isNull() ? 0 : opt->default_description ; } Argument Option::noArgumentValue() const { if (isNull()) { return Argument(); } else { return Argument(comp.lock(), opt, opt->no_arg_value, false); } } const char *Option::noArgumentDescription() const { return isNull() ? 0 : opt->no_arg_description ; } Argument Option::activeValue() const { if (isNull()) { return Argument(); } else { return Argument(comp.lock(), opt, opt->value, false); } } Argument Option::currentValue() const { if (isNull()) { return Argument(); } const gpgme_conf_arg_t arg = opt->change_value ? opt->new_value ? opt->new_value : opt->default_value : opt->value ? opt->value : /* else */ opt->default_value ; return Argument(comp.lock(), opt, arg, false); } Argument Option::newValue() const { if (isNull()) { return Argument(); } else { return Argument(comp.lock(), opt, opt->new_value, false); } } bool Option::set() const { if (isNull()) { return false; } else if (opt->change_value) { return opt->new_value; } else { return opt->value; } } bool Option::dirty() const { return !isNull() && opt->change_value ; } Error Option::setNewValue(const Argument &argument) { if (isNull()) { return Error(make_error(GPG_ERR_INV_ARG)); } else if (argument.isNull()) { return resetToDefaultValue(); } else if (const gpgme_conf_arg_t arg = mygpgme_conf_arg_copy(argument.arg, opt->alt_type)) { return Error(gpgme_conf_opt_change(opt, 0, arg)); } else { return Error(make_error(GPG_ERR_ENOMEM)); } } Error Option::resetToActiveValue() { if (isNull()) { return Error(make_error(GPG_ERR_INV_ARG)); } else { return Error(gpgme_conf_opt_change(opt, 1, 0)); } } Error Option::resetToDefaultValue() { if (isNull()) { return Error(make_error(GPG_ERR_INV_ARG)); } else { return Error(gpgme_conf_opt_change(opt, 0, 0)); } } static gpgme_conf_arg_t make_argument(gpgme_conf_type_t type, const void *value) { gpgme_conf_arg_t arg = 0; if (const gpgme_error_t err = gpgme_conf_arg_new(&arg, type, value)) { return 0; } else { return arg; } } Argument Option::createNoneArgument(bool set) const { if (isNull() || alternateType() != NoType) { return Argument(); } else { if (set) { return createNoneListArgument(1); } } return Argument(); } Argument Option::createStringArgument(const char *value) const { if (isNull() || alternateType() != StringType) { return Argument(); } else { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value), true); } } Argument Option::createStringArgument(const std::string &value) const { if (isNull() || alternateType() != StringType) { return Argument(); } else { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value.c_str()), true); } } Argument Option::createIntArgument(int value) const { if (isNull() || alternateType() != IntegerType) { return Argument(); } else { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_INT32, &value), true); } } Argument Option::createUIntArgument(unsigned int value) const { if (isNull() || alternateType() != UnsignedIntegerType) { return Argument(); } else { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_UINT32, &value), true); } } namespace { const void *to_void_star(const char *s) { return s; } const void *to_void_star(const std::string &s) { return s.c_str(); } const void *to_void_star(const int &i) { return &i; // const-&: sic! } const void *to_void_star(const unsigned int &i) { return &i; // const-&: sic! } template <typename T> gpgme_conf_arg_t make_argument(gpgme_conf_type_t type, const std::vector<T> &value) { gpgme_conf_arg_t result = 0; gpgme_conf_arg_t last = 0; for (typename std::vector<T>::const_iterator it = value.begin(), end = value.end() ; it != end ; ++it) { if (gpgme_conf_arg_t arg = make_argument(type, to_void_star(*it))) { if (last) { last = last->next = arg; } else { result = last = arg; } } } return result; } } Argument Option::createNoneListArgument(unsigned int value) const { if (value) { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_NONE, &value), true); } return Argument(); } Argument Option::createStringListArgument(const std::vector<const char *> &value) const { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value), true); } Argument Option::createStringListArgument(const std::vector<std::string> &value) const { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_STRING, value), true); } Argument Option::createIntListArgument(const std::vector<int> &value) const { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_INT32, value), true); } Argument Option::createUIntListArgument(const std::vector<unsigned int> &value) const { return Argument(comp.lock(), opt, make_argument(GPGME_CONF_UINT32, value), true); } Argument::Argument(const shared_gpgme_conf_comp_t &comp, gpgme_conf_opt_t opt, gpgme_conf_arg_t arg, bool owns) : comp(comp), opt(opt), arg(owns ? arg : mygpgme_conf_arg_copy(arg, opt ? opt->alt_type : GPGME_CONF_NONE)) { } #if 0 Argument::Argument(const shared_gpgme_conf_comp_t &comp, gpgme_conf_opt_t opt, gpgme_conf_arg_t arg) : comp(comp), opt(opt), arg(mygpgme_conf_arg_copy(arg, opt ? opt->alt_type : GPGME_CONF_NONE)) { } #endif Argument::Argument(const Argument &other) : comp(other.comp), opt(other.opt), arg(mygpgme_conf_arg_copy(other.arg, opt ? opt->alt_type : GPGME_CONF_NONE)) { } Argument::~Argument() { gpgme_conf_arg_release(arg, opt ? opt->alt_type : GPGME_CONF_NONE); } Option Argument::parent() const { return Option(comp.lock(), opt); } bool Argument::boolValue() const { return numberOfTimesSet(); } unsigned int Argument::numElements() const { if (isNull()) { return 0; } unsigned int result = 0; for (gpgme_conf_arg_t a = arg ; a ; a = a->next) { ++result; } return result; } const char *Argument::stringValue(unsigned int idx) const { if (isNull() || opt->alt_type != GPGME_CONF_STRING) { return 0; } gpgme_conf_arg_t a = arg; while (a && idx) { a = a->next; --idx; } return a ? a->value.string : 0 ; } int Argument::intValue(unsigned int idx) const { if (isNull() || opt->alt_type != GPGME_CONF_INT32) { return 0; } gpgme_conf_arg_t a = arg; while (a && idx) { a = a->next; --idx; } return a ? a->value.int32 : 0 ; } unsigned int Argument::uintValue(unsigned int idx) const { if (isNull() || opt->alt_type != GPGME_CONF_UINT32) { return 0; } gpgme_conf_arg_t a = arg; while (a && idx) { a = a->next; --idx; } return a ? a->value.uint32 : 0 ; } unsigned int Argument::numberOfTimesSet() const { if (isNull() || opt->alt_type != GPGME_CONF_NONE) { return 0; } return arg->value.count; } std::vector<const char *> Argument::stringValues() const { if (isNull() || opt->alt_type != GPGME_CONF_STRING) { return std::vector<const char *>(); } std::vector<const char *> result; for (gpgme_conf_arg_t a = arg ; a ; a = a->next) { result.push_back(a->value.string); } return result; } std::vector<int> Argument::intValues() const { if (isNull() || opt->alt_type != GPGME_CONF_INT32) { return std::vector<int>(); } std::vector<int> result; for (gpgme_conf_arg_t a = arg ; a ; a = a->next) { result.push_back(a->value.int32); } return result; } std::vector<unsigned int> Argument::uintValues() const { if (isNull() || opt->alt_type != GPGME_CONF_UINT32) { return std::vector<unsigned int>(); } std::vector<unsigned int> result; for (gpgme_conf_arg_t a = arg ; a ; a = a->next) { result.push_back(a->value.uint32); } return result; } std::ostream &Configuration::operator<<(std::ostream &os, Level level) { switch (level) { case Basic: return os << "Basic"; case Advanced: return os << "Advanced"; case Expert: return os << "Expert"; case Invisible: return os << "Invisible"; case Internal: return os << "Internal"; case NumLevels: ; } return os << "<unknown>"; } std::ostream &Configuration::operator<<(std::ostream &os, Type type) { switch (type) { case NoType: return os << "None"; case StringType: return os << "String"; case IntegerType: return os << "Integer"; case UnsignedIntegerType: return os << "UnsignedInteger"; case FilenameType: return os << "Filename"; case LdapServerType: return os << "LdapServer"; case KeyFingerprintType: return os << "KeyFingerprint"; case PublicKeyType: return os << "PublicKey"; case SecretKeyType: return os << "SecretKey"; case AliasListType: return os << "AliasList"; case MaxType: ; } return os << "<unknown>"; } std::ostream &Configuration::operator<<(std::ostream &os, Flag f) { unsigned int flags = f; std::vector<const char *> s; if (flags & Group) { s.push_back("Group"); } if (flags & Optional) { s.push_back("Optional"); } if (flags & List) { s.push_back("List"); } if (flags & Runtime) { s.push_back("Runtime"); } if (flags & Default) { s.push_back("Default"); } if (flags & DefaultDescription) { s.push_back("DefaultDescription"); } if (flags & NoArgumentDescription) { s.push_back("NoArgumentDescription"); } if (flags & NoChange) { s.push_back("NoChange"); } flags &= ~(Group | Optional | List | Runtime | Default | DefaultDescription | NoArgumentDescription | NoChange); if (flags) { s.push_back("other flags("); } std::copy(s.begin(), s.end(), std::ostream_iterator<const char *>(os, "|")); if (flags) { os << flags << ')'; } return os; } std::ostream &Configuration::operator<<(std::ostream &os, const Component &c) { os << "Component[" << "\n name : " << protect(c.name()) << "\n description: " << protect(c.description()) << "\n programName: " << protect(c.programName()) << "\n options : \n"; const std::vector<Option> options = c.options(); std::copy(options.begin(), options.end(), std::ostream_iterator<Option>(os, "\n")); os << "\n]"; return os; } std::ostream &Configuration::operator<<(std::ostream &os, const Option &o) { return os << "Option[" << "\n name: : " << protect(o.name()) << "\n description : " << protect(o.description()) << "\n argName : " << protect(o.argumentName()) << "\n flags : " << static_cast<Flag>(o.flags()) << "\n level : " << o.level() << "\n type : " << o.type() << "\n alt_type : " << o.alternateType() << "\n default_val : " << o.defaultValue() << "\n default_desc: " << protect(o.defaultDescription()) << "\n no_arg_value: " << o.noArgumentValue() << "\n no_arg_desc : " << protect(o.noArgumentDescription()) << "\n active_value: " << o.activeValue() << "\n new_value : " << o.newValue() << "\n --> cur_val : " << o.currentValue() << "\n set : " << o.set() << "\n dirty : " << o.dirty() << "\n]" ; } std::ostream &Configuration::operator<<(std::ostream &os, const Argument &a) { const Option o = a.parent(); const bool list = o.flags() & List; os << "Argument["; if (a) { switch (o.alternateType()) { case NoType: if (list) { os << a.numberOfTimesSet() << 'x'; } else { os << a.boolValue(); } break; default: case StringType: if (list) { const std::vector<const char *> v = a.stringValues(); os << v.size() << ':'; // can't use std::copy + ostream_iterator here, since we need the protect() call bool first = true; std::for_each(v.begin(), v.end(), [&first, &os](const char *s) { if (first) { first = false; } else { os << ','; } os << protect(s); }); } else { os << protect(a.stringValue()); } break; case IntegerType: if (list) { const std::vector<int> v = a.intValues(); os << v.size() << ':'; std::copy(v.begin(), v.end(), std::ostream_iterator<int>(os, ",")); } else { os << a.intValue(); } break; case UnsignedIntegerType: if (list) { const std::vector<unsigned int> v = a.uintValues(); os << v.size() << ':'; std::copy(v.begin(), v.end(), std::ostream_iterator<unsigned int>(os, ",")); } else { os << a.intValue(); } break; } } return os << ']'; }