1 /* zxlibenc.c  -  XML encoder
2  * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3  * Copyright (c) 2006-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4  * Author: Sampo Kellomaki (sampo@iki.fi)
5  * This is confidential unpublished proprietary source code of the author.
6  * NO WARRANTY, not even implied warranties. Contains trade secrets.
7  * Distribution prohibited unless authorized in writing.
8  * Licensed under Apache License 2.0, see file COPYING.
9  * $Id: zxlib.c,v 1.41 2009-11-24 23:53:40 sampo Exp $
10  *
11  * 28.5.2006, created --Sampo
12  * 8.8.2006,  moved lookup functions to generated code --Sampo
13  * 12.8.2006, added special scanning of xmlns to avoid backtracking elem recognition --Sampo
14  * 26.8.2006, significant Common Subexpression Elimination (CSE) --Sampo
15  * 30.9.2007, more CSE --Sampo
16  * 7.10.2008, added documentation --Sampo
17  * 26.5.2010, added XML parse error reporting --Sampo
18  * 27.10.2010, re-engineered namespace handling --Sampo
19  */
20 
21 #include "platform.h"  /* needed on Win32 for snprintf(), va_copy() et al. */
22 
23 #include <memory.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 
29 #include "errmac.h"
30 #include "zx.h"
31 #include "c/zx-ns.h"
32 #include "c/zx-data.h"
33 
34 /* Add inclusive namespaces. */
35 
36 /* Called by:  TXLEN_SO_ELNAME, zx_LEN_WO_any_elem x2 */
zx_len_inc_ns(struct zx_ctx * c,struct zx_ns_s ** pop_seenp)37 static int zx_len_inc_ns(struct zx_ctx* c, struct zx_ns_s** pop_seenp) {
38   int len = 0;
39   struct zx_ns_s* ns;
40   for (ns = c->inc_ns; ns; ns = ns->inc_n)
41     len += zx_len_xmlns_if_not_seen(c, ns, pop_seenp);
42   /*c->inc_ns_len = 0;  needs to be processed at every level */
43   return len;
44 }
45 
46 /* Called by:  TXENC_SO_ELNAME, zx_ENC_WO_any_elem x2 */
zx_add_inc_ns(struct zx_ctx * c,struct zx_ns_s ** pop_seenp)47 static void zx_add_inc_ns(struct zx_ctx* c, struct zx_ns_s** pop_seenp) {
48   struct zx_ns_s* ns;
49   for (ns = c->inc_ns; ns; ns = ns->inc_n)
50     zx_add_xmlns_if_not_seen(c, ns, pop_seenp);
51   /*c->inc_ns = 0;  needs to be processed at every level */
52 }
53 
54 /* Called by:  TXENC_SO_ELNAME, zx_ENC_WO_any_elem */
zx_see_attr_ns(struct zx_ctx * c,struct zx_attr_s * aa,struct zx_ns_s ** pop_seenp)55 static void zx_see_attr_ns(struct zx_ctx* c, struct zx_attr_s* aa, struct zx_ns_s** pop_seenp) {
56   for (; aa; aa = (struct zx_attr_s*)aa->g.n)
57     zx_add_xmlns_if_not_seen(c, aa->ns, pop_seenp);
58 }
59 
60 /*() Check if a namespace is already in inclusive namespaces so we do not need to add it again. */
61 
62 /* Called by:  zxsig_validate */
zx_in_inc_ns(struct zx_ctx * c,struct zx_ns_s * new_ns)63 int zx_in_inc_ns(struct zx_ctx* c, struct zx_ns_s* new_ns) {
64   struct zx_ns_s* ns;
65   for (ns = c->inc_ns; ns; ns = ns->inc_n)
66     if (new_ns == ns)
67       return 1;
68   return 0;
69 }
70 
71 /*() Convert a tok integer to namespace and el_tok descriptor from zx_el_tab[] table. */
72 
zx_get_el_tok(struct zx_elem_s * x)73 struct zx_el_tok* zx_get_el_tok(struct zx_elem_s* x)
74 {
75   int ix;
76   if (!x->ns) {
77     ix = (x->g.tok >> ZX_TOK_NS_SHIFT)&(ZX_TOK_NS_MASK >> ZX_TOK_NS_SHIFT);
78     if (ix >= zx__NS_MAX) {
79       ERR("Namespace index of token(0x%06x) out of range(0x%02x)", x->g.tok, zx__NS_MAX);
80       return 0;
81     }
82     x->ns = zx_ns_tab + ix;
83   }
84   ix = x->g.tok & ZX_TOK_TOK_MASK;
85   if (ix >= zx__ELEM_MAX) {
86     ERR("Element token(0x%06x) out of range(0x%04x)", x->g.tok, zx__ELEM_MAX);
87     return 0;
88   }
89   return zx_el_tab + ix;
90 }
91 
92 /*() Convert a tok integer to namespace and at_tok descriptor from zx_at_tab[] table. */
93 
zx_get_at_tok(struct zx_attr_s * attr)94 static struct zx_at_tok* zx_get_at_tok(struct zx_attr_s* attr)
95 {
96   int ix;
97   if (!attr->ns && IN_RANGE((attr->g.tok & ZX_TOK_NS_MASK) >> ZX_TOK_NS_SHIFT, 1, zx__NS_MAX))
98     attr->ns = zx_ns_tab + ((attr->g.tok & ZX_TOK_NS_MASK) >> ZX_TOK_NS_SHIFT);
99   ix = attr->g.tok & ZX_TOK_TOK_MASK;
100   if (ix >= zx__ATTR_MAX) {
101     ERR("Attribute token(0x%06x) out of range(0x%04x)", attr->g.tok, zx__ATTR_MAX);
102     return 0;
103   }
104   return zx_at_tab + ix;
105 }
106 
107 #define D_LEN_ENA 0
108 #if D_LEN_ENA
109 #define D_LEN(f,t,l) D(f,t,l)
110 #else
111 #define D_LEN(f,t,l)
112 #endif
113 
114 /*() Compute length of an element (and its subelements). The XML attributes
115  * and elements are processed in wire order and no assumptions
116  * are made about namespace prefixes. */
117 
118 /* Called by:  main x2, zx_EASY_ENC_elem, zx_LEN_WO_any_elem x2 */
zx_LEN_WO_any_elem(struct zx_ctx * c,struct zx_elem_s * x)119 int zx_LEN_WO_any_elem(struct zx_ctx* c, struct zx_elem_s* x)
120 {
121   //const struct zx_el_desc* ed;
122   struct zx_at_tok* at_tok;
123   struct zx_el_tok* el_tok;
124   struct zx_ns_s* pop_seen = 0;
125   struct zx_attr_s* attr;
126   struct zx_elem_s* kid;
127   int len;
128   //struct zx_elem_s* kid;
129   switch (x->g.tok) {
130   case zx_root_ELEM:
131     len = 0;
132     if (c->inc_ns_len)
133       len += zx_len_inc_ns(c, &pop_seen);
134     for (kid = x->kids; kid; kid = ((struct zx_elem_s*)(kid->g.n)))
135       len += zx_LEN_WO_any_elem(c, kid);
136     break;
137   case ZX_TOK_DATA:
138     return x->g.len;
139   case zx_ds_Signature_ELEM:
140     if (x == c->exclude_sig)
141       return 0;
142     /* fall thru */
143   default:
144     if (x->g.s) {
145       /*    <   ns:elem    >                                    </  ns:elem    >    / */
146       len = 1 + x->g.len + 1 + ((x->kids || !c->enc_tail_opt) ? (2 + x->g.len + 1) : 1);
147     } else { /* Construct elem string from tok */
148       if (!(el_tok = zx_get_el_tok(x)))
149 	return 0;
150       len = strlen(el_tok->name);
151       DD("ns prefix_len=%d el_len=%d", x->ns->prefix_len, len);
152       /*    <   ns                  :   elem  >                                    </  ns                  :   elem  >    / */
153       len = 1 + x->ns->prefix_len + 1 + len + 1 + ((x->kids || !c->enc_tail_opt) ? (2 + x->ns->prefix_len + 1 + len + 1) : 1);
154     }
155     D_LEN("%06x ** tag start: %d", x->g.tok, len);
156     len += zx_len_xmlns_if_not_seen(c, x->ns, &pop_seen);
157     D_LEN("%06x after xmlns: %d", x->g.tok, len);
158 
159     if (c->inc_ns_len)
160       len += zx_len_inc_ns(c, &pop_seen);
161     D_LEN("%06x after inc_ns: %d", x->g.tok, len);
162 
163     for (attr = x->attr; attr; attr = (struct zx_attr_s*)attr->g.n) {
164       if (attr->name) {
165 	/*    sp   name             ="                "   */
166 	len += 1 + attr->name_len + 2 + attr->g.len + 1;
167       } else { /* Construct elem string from tok */
168 	if (!(at_tok = zx_get_at_tok(attr)))
169 	  return 0;
170 	if (attr->ns)
171 	  len += attr->ns->prefix_len + 1;
172 	len += strlen(at_tok->name);
173 	/*     sp ="                "   */
174 	len += 1+ 2 + attr->g.len + 1;
175       }
176       len += zx_len_xmlns_if_not_seen(c, attr->ns, &pop_seen);
177     }
178     D_LEN("%06x after attrs: %d", x->g.tok, len);
179 
180     for (kid = x->kids; kid; kid = ((struct zx_elem_s*)(kid->g.n)))
181       len += zx_LEN_WO_any_elem(c, kid);
182 
183     break;
184   }
185   zx_pop_seen(pop_seen);
186   D_LEN("%06x final: %d", x->g.tok, len);
187   return len;
188 }
189 
190 /* Called by:  TXENC_SO_ELNAME, zx_ENC_WO_any_elem */
zx_attr_wo_enc(char * p,struct zx_attr_s * attr)191 static char* zx_attr_wo_enc(char* p, struct zx_attr_s* attr)
192 {
193   struct zx_at_tok* at_tok;
194   ZX_OUT_CH(p, ' ');
195   if (attr->name) {
196     ZX_OUT_MEM(p, attr->name, attr->name_len);
197   } else { /* Construct elem string from tok */
198     if (!(at_tok = zx_get_at_tok(attr)))
199       return p;
200     if (attr->ns) {
201       ZX_OUT_MEM(p, attr->ns->prefix, attr->ns->prefix_len);
202       ZX_OUT_CH(p, ':');
203     }
204     ZX_OUT_MEM(p, at_tok->name, strlen(at_tok->name));
205   }
206   ZX_OUT_CH(p, '=');
207   ZX_OUT_CH(p, '"');
208   ZX_OUT_MEM(p, attr->g.s, attr->g.len);
209   ZX_OUT_CH(p, '"');
210   return p;
211 }
212 
213 /*() Render element into string. The XML attributes and elements are
214  * processed in wire order by starting with kids root and chasing g.n pointers.
215  * This is what you want for validating signatures on other people's XML documents.
216  * The lists are assumed to be in forward order, i.e. opposite
217  * of what zx_dec_zx_root() and zx_DEC_elem() return. You should call
218  * zx_reverse_elem_lists() if needed. */
219 
220 /* Called by:  main x2, zx_EASY_ENC_elem, zx_ENC_WO_any_elem x2 */
zx_ENC_WO_any_elem(struct zx_ctx * c,struct zx_elem_s * x,char * p)221 char* zx_ENC_WO_any_elem(struct zx_ctx* c, struct zx_elem_s* x, char* p)
222 {
223   struct zx_el_tok* el_tok;
224   struct zx_ns_s* pop_seen = 0;
225   struct zx_attr_s* attr;
226   struct zx_elem_s* kid;
227 #if D_LEN_ENA
228   char* b = p;
229 #endif
230   switch (x->g.tok) {
231   case zx_root_ELEM:
232     if (c->inc_ns)
233       zx_add_inc_ns(c, &pop_seen);
234     p = zx_enc_seen(p, pop_seen);
235     for (kid = x->kids; kid; kid = (struct zx_elem_s*)kid->g.n)
236       p = zx_ENC_WO_any_elem(c, kid, p);
237     break;
238   case ZX_TOK_DATA:
239     ZX_OUT_STR(p, x);
240     break;
241   case zx_ds_Signature_ELEM:
242     if (x == c->exclude_sig)
243       return p;
244     /* fall thru */
245   default:
246     ZX_OUT_CH(p, '<');
247     if (x->g.s) {
248       ZX_OUT_MEM(p, x->g.s, x->g.len);
249     } else { /* Construct elem string from tok */
250       if (!(el_tok = zx_get_el_tok(x)))
251 	return p;
252       ZX_OUT_MEM(p, x->ns->prefix, x->ns->prefix_len);
253       ZX_OUT_CH(p, ':');
254       ZX_OUT_MEM(p, el_tok->name, strlen(el_tok->name));
255     }
256     D_LEN("%06x   ** tag start: %d", x->g.tok, p-b);
257     zx_add_xmlns_if_not_seen(c, x->ns, &pop_seen);
258     if (c->inc_ns)
259       zx_add_inc_ns(c, &pop_seen);
260     D_LEN("%06x   after inc_ns: %d", x->g.tok, p-b);
261     zx_see_attr_ns(c, x->attr, &pop_seen);
262     p = zx_enc_seen(p, pop_seen);
263     D_LEN("%06x   after seen ns: %d", x->g.tok, p-b);
264 
265     for (attr = x->attr; attr; attr = (struct zx_attr_s*)attr->g.n)
266       p = zx_attr_wo_enc(p, attr);
267 
268     if (x->kids || !c->enc_tail_opt) {
269       ZX_OUT_CH(p, '>');
270       D_LEN("%06x   after attrs: %d", x->g.tok, p-b);
271 
272       for (kid = x->kids; kid; kid = (struct zx_elem_s*)kid->g.n)
273 	p = zx_ENC_WO_any_elem(c, kid, p);
274       D_LEN("%06x   after kids: %d", x->g.tok, p-b);
275 
276       ZX_OUT_CH(p, '<');
277       ZX_OUT_CH(p, '/');
278       if (x->g.s) {
279 	ZX_OUT_MEM(p, x->g.s, x->g.len);
280       } else { /* Construct elem string from tok */
281 	ZX_OUT_MEM(p, x->ns->prefix, x->ns->prefix_len);
282 	ZX_OUT_CH(p, ':');
283 	ZX_OUT_MEM(p, el_tok->name, strlen(el_tok->name));
284       }
285     } else {
286       ZX_OUT_CH(p, '/');  /* Also an XML legal way to terminate an empty tag, e.g. <ns:foo/> */
287     }
288     ZX_OUT_CH(p, '>');
289   }
290   zx_pop_seen(pop_seen);
291   D_LEN("%06x   final: %d", x->g.tok, p-b);
292   return p;
293 }
294 
295 /*(i) Render any element in wire order, as often needed in validating canonicalizations.
296  * See also: zx_easy_enc_elem_opt() */
297 
298 /* Called by:  zx_easy_enc_elem_opt, zx_easy_enc_elem_sig, zxsig_sign, zxsig_validate x2 */
zx_EASY_ENC_elem(struct zx_ctx * c,struct zx_elem_s * x)299 struct zx_str* zx_EASY_ENC_elem(struct zx_ctx* c, struct zx_elem_s* x)
300 {
301   int len;
302   char* buf;
303   char* p;
304   if (!c || !x) {
305     ERR("zx_easy_enc_elem called with NULL argument %p (programmer error)", x);
306     return 0;
307   }
308   len = zx_LEN_WO_any_elem(c, x);
309   buf = ZX_ALLOC(c, len+1);
310   p = zx_ENC_WO_any_elem(c, x, buf);
311   if (p != buf+len) {
312     ERR("Encoded length(%d) does not match computed length(%d). ED(%.*s)", ((int)(p-buf)), len, ((int)(p-buf)), buf);
313     len = p-buf;
314   }
315   buf[len] = 0;
316   return zx_ref_len_str(c, len, buf);
317 }
318 
319 /* EOF -- zxlibenc.c */
320