dacs  1.4.28
About: DACS (Distributed Access Control System) is a light-weight single sign-on and role-based access control system for web servers and server-based software.
  Fossies Dox: dacs-1.4.28b.tgz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
transform.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2003-2012
3  * Distributed Systems Software. All rights reserved.
4  * See the file LICENSE for redistribution information.
5  */
6 
7 /*
8  * Document transformation by redaction, selection, and substitution of
9  * content. Ordinary content and special directives can be combined in the
10  * input document(s).
11  *
12  * A directive can insert new content, process and replace content, or
13  * perform computations.
14  *
15  * Directives:
16  * 1. begin/end
17  * Include or exclude verbatim content
18  * 2. debug
19  * Emit variable values
20  * 3. expand
21  * Include or exclude content with variable substitutions
22  * 4. filter/end, filterv/end
23  *
24  * 5. id
25  * Emit an identification string
26  * 6. insert, insertv
27  *
28  * 7. set
29  * Set or reset zero or more variables
30  */
31 
32 #ifndef lint
33 static const char copyright[] =
34 "Copyright (c) 2003-2012\n\
35 Distributed Systems Software. All rights reserved.";
36 static const char revid[] =
37  "$Id: transform.c 2594 2012-10-19 17:28:49Z brachman $";
38 #endif
39 
40 #include "dacs.h"
41 #include "dacs_api.h"
42 
43 #include <sys/utsname.h>
44 
45 #ifndef DACS_TRANSFORM_ACLS
46 #define DACS_TRANSFORM_ACLS "[transform-acls]dacs-fs:"DACS_HOME"/dacs_transform/acls"
47 #endif
48 
49 #ifndef DACS_TRANSFORM_DOCS
50 #define DACS_TRANSFORM_DOCS DACS_HOME"/dacs_transform/docs"
51 #endif
52 
53 #define DEFAULT_SGML_DIRECTIVE_PREFIX "<!--DACS "
54 #define DEFAULT_SGML_DIRECTIVE_SUFFIX "-->"
55 
56 #ifndef DEFAULT_HTML_ANNOTATION
57 #define DEFAULT_HTML_ANNOTATION "<b>*** Content elided ***</b>"
58 #endif
59 
60 static MAYBE_UNUSED const char *log_module_name = "dacs_transform";
61 
62 #ifndef PROG
63 
64 typedef struct Directive_tab {
65  char *name;
68 
69 static Directive_tab directive_tab[] = {
70  { "begin", D_BEGIN },
71  { "comment", D_COMMENT },
72  { "debug", D_DEBUG },
73  { "end", D_END },
74  { "eval", D_EVAL },
75  { "expand", D_EXPAND },
76  { "filter", D_FILTER },
77  { "filterv", D_FILTERV },
78  { "id", D_ID },
79  { "insert", D_INSERT },
80  { "insertv", D_INSERTV },
81  { "set", D_SET },
82  { NULL, D_NONE }
83 };
84 
86 get_directive(Kwv *kwv, char **value, char **errmsg)
87 {
88  int i;
89  char *v;
91 
92  kwv_set_mode(kwv, "+i-d");
93  d = D_NONE;
94 
95  for (i = 0; directive_tab[i].name != NULL; i++) {
96  if ((v = kwv_lookup_value(kwv, directive_tab[i].name)) != NULL) {
97  if (d != D_NONE) {
98  if (errmsg != NULL)
99  *errmsg = "Only one directive is allowed";
100  return(D_ERROR);
101  }
102  d = directive_tab[i].directive;
103  *value = v;
104  }
105  }
106 
107  return(d);
108 }
109 
110 static Acs_expr_result
111 eval(char *expr, Kwv *kwv_attrs, Var_ns **namespaces, char **result_str)
112 {
113  Acs_expr_result st;
114  Acs_expr_ns_arg *ns_args;
115 
116  var_ns_replace(namespaces, "Attr", kwv_attrs);
117  ns_args = var_ns_to_acs_ns(*namespaces);
118 
119  st = acs_expr_string(expr, ns_args, result_str);
120 
121  if (st == ACS_EXPR_TRUE && result_str != NULL && *result_str != NULL)
122  log_msg((LOG_TRACE_LEVEL, "Eval result: \"%s\"", *result_str));
123 
124  return(st);
125 }
126 
127 /*
128  * Attribute values quoted by backticks are evaluated as an expression,
129  * but other values are copied verbatim.
130  */
131 static Kwv *
132 attrs(Kwv *kwv, Var_ns **namespaces)
133 {
134  char *newval, *p;
135  Kwv *kwv_attrs;
136  Kwv_iter *iter;
137  Kwv_pair *pair;
138 
139  kwv_attrs = kwv_init(4);
140  kwv_set_mode(kwv, "+i-d");
141 
142  iter = kwv_iter_begin(kwv, NULL);
143  for (pair = kwv_iter_first(iter); pair != NULL; pair = kwv_iter_next(iter)) {
144  if (pair->val != NULL) {
145  p = strdequote(pair->val);
146  if (pair->val[0] == '`') {
147  if (eval(p, kwv, namespaces, &newval) < 0)
148  return(NULL);
149  }
150  else
151  newval = p;
152 
153  kwv_add(kwv_attrs, pair->name, newval);
154  }
155  }
156  kwv_iter_end(iter);
157 
158  return(kwv_attrs);
159 }
160 
161 static int
162 is_directive(Transform_config *tc, char *line, char **startp, char **endp)
163 {
164  char *e, *s, *suffix, *suffix_crlf;
165  size_t slen;
166 
167  if ((s = strprefix(line, tc->directive_prefix)) != NULL) {
168  /* Remember that lines may end with a \n or a \r\n. */
169  slen = strlen(s);
170  suffix = ds_xprintf("%s\n", tc->directive_suffix);
171  suffix_crlf = ds_xprintf("%s\r\n", tc->directive_suffix);
172 
173  if ((e = strsuffix(s, slen, suffix)) == NULL
174  && (e = strsuffix(s, slen, suffix_crlf)) == NULL)
175  return(-1);
176  *startp = s;
177  *endp = e;
178  return(1);
179  }
180 
181  return(0);
182 }
183 
184 static int
185 is_regex_directive(char *line, regex_t *regex, char **startp, char **endp,
186  char **errmsg)
187 {
188  int rc, st;
189  regmatch_t m[4];
190 
191  if ((st = regexec(regex, line, 4, m, 0)) == 0) {
192  if (m[0].rm_so == -1 || m[1].rm_so == -1 || m[2].rm_so == -1
193  || m[3].rm_so != -1)
194  return(-1);
195 
196  *startp = line + m[1].rm_eo;
197  *endp = line + m[2].rm_so;
198  return(1);
199  }
200  else if (st != REG_NOMATCH) {
201  char errbuf[100];
202 
203  if (errmsg != NULL) {
204  errbuf[0] = '\0';
205  regerror(st, regex, errbuf, sizeof(errbuf));
206  *errmsg = ds_xprintf("bad regular expression: \"%s\": %s",
207  regex, errbuf);
208  }
209  rc = -1;
210  }
211  else
212  rc = 0;
213 
214  return(rc);
215 }
216 
217 /*
218  * Check if a region should be processed or discarded.
219  * Return 1 if the former, 0 if the latter, or -1 if an error occurs.
220  * If the region has a "cond" attribute, its value is an expression that
221  * must evaluate to True for the region to be processed.
222  * If the name of the region is "*", then no rule needs to be consulted,
223  * otherwise a rule must be checked.
224  * If both a "cond" attribute and a rule are specified, then both must
225  * be True for the region's content to be included.
226  */
227 static int
228 region_test(Transform_config *tc, char *region, Kwv *kwv_attrs, Kwv *kwv_conf,
229  Kwv *kwv_dacs, char *op, int invert, char *acls,
230  char *object_name, char *idents, char **errmsg)
231 {
232  int nargs, st;
233  char **constraints, *expr, *object;
234  Ds args;
235  Kwv_iter *iter;
236  Kwv_pair *pair;
237 
238  if ((expr = kwv_lookup_value(kwv_attrs, "cond")) != NULL) {
239  char *result;
240  Acs_expr_result rc;
241 
242  tc->env->namespaces = tc->stackp->var_context;
243  rc = acs_expr_string_env(expr, tc->env, &result);
244  if (acs_expr_error_occurred(rc)) {
245  if (errmsg != NULL)
246  *errmsg = ds_xprintf("Line %d: expansion error: \"%s\"",
247  tc->linenum, expr);
248  return(-1);
249  }
250  st = (rc == ACS_EXPR_TRUE);
251  }
252  else
253  st = ACS_EXPR_TRUE;
254 
255  if (st == ACS_EXPR_TRUE && !streq(region, "*")) {
256  ds_init(&args);
257  nargs = 0;
258  iter = kwv_iter_begin(kwv_attrs, NULL);
259  for (pair = kwv_iter_first(iter); pair != NULL;
260  pair = kwv_iter_next(iter)) {
261  if (strcaseeq(pair->name, op))
262  ds_asprintf(&args, "%sregion=%s",
263  (nargs == 0) ? "" : "&",
264  invert ? region + 1 : region);
265  else
266  ds_asprintf(&args, "%s%s=%s",
267  (nargs == 0) ? "" : "&", pair->name, pair->val);
268  nargs++;
269  }
270  kwv_iter_end(iter);
271 
272  object = ds_xprintf("%s?%s", object_name, ds_buf(&args));
273  st = check_access(acls, NULL, idents, object,
274  kwv_conf, kwv_dacs, NULL, &constraints);
275  }
276 
277  return(st);
278 }
279 
282 {
283  char *p;
284  Transform_config *tc;
285 
286  if (otc == NULL)
287  tc = ALLOC(Transform_config);
288  else
289  tc = otc;
290 
291  tc->ds_in = ds_init(NULL);
292  tc->ds_out = ds_init(NULL);
293 
294  tc->linenum = 0;
295  tc->stack_depth = 0;
296  tc->stackp = NULL;
298 
303 
304  tc->insert_dir = NULL;
305  tc->annotation = NULL;
306  tc->regex_prefix = NULL;
307  tc->regex_suffix = NULL;
308 
309  if (dacs_conf != NULL) {
310  p = var_ns_get_value(dacs_conf->conf_var_ns, "Conf", "transform_acls");
311  if (p != NULL)
312  tc->acls = p;
313 
314  p = var_ns_get_value(dacs_conf->conf_var_ns, "Conf", "transform_docs");
315  if (p != NULL)
316  tc->docs = p;
317 
318  p = var_ns_get_value(dacs_conf->conf_var_ns, "Conf", "transform_prefix");
319  if (p != NULL)
320  tc->directive_prefix = p;
321 
322  p = var_ns_get_value(dacs_conf->conf_var_ns, "Conf", "transform_suffix");
323  if (p != NULL)
324  tc->directive_suffix = p;
325 
326  p = var_ns_get_value(dacs_conf->conf_var_ns, "Conf", "transform_rprefix");
327  if (p != NULL)
328  tc->regex_prefix = p;
329 
330  p = var_ns_get_value(dacs_conf->conf_var_ns, "Conf", "transform_rsuffix");
331  if (p != NULL)
332  tc->regex_suffix = p;
333 
335  "transform_annotation");
336  }
337  tc->global_attrs = kwv_init(4);
338  tc->env = acs_new_env(NULL);
339 
340  return(tc);
341 }
342 
343 static Transform_config *
344 transform_reconfig(Transform_config *otc, Ds *in, Ds *out)
345 {
346  Transform_config *tc;
347 
348  tc = ALLOC(Transform_config);
349 
350  tc->ds_in = in;
351  tc->ds_out = out;
352 
353  tc->linenum = 0;
354  tc->stack_depth = otc->stack_depth;
355  tc->max_stack_depth = otc->max_stack_depth;
356  tc->stackp = otc->stackp;
357 
358  tc->acls = otc->acls;
359  tc->docs = otc->docs;
360  tc->insert_dir = otc->insert_dir;
363 
364  tc->annotation = otc->annotation;
365  tc->regex_prefix = otc->regex_prefix;
366  tc->regex_suffix = otc->regex_suffix;
367 
368  tc->global_attrs = kwv_copy(otc->global_attrs);
369 
370  tc->env = otc->env;
371 
372  return(tc);
373 }
374 
375 static Transform_stack *
376 push_stack(Transform_config *tc, Transform_directive directive, char *region,
377  Kwv *attrs, int skip, int expand, char *expr)
378 {
379  Transform_stack *stack_el;
380 
381  stack_el = ALLOC(Transform_stack);
382  stack_el->directive = directive;
383  stack_el->region = region;
384  stack_el->skipping = skip;
385  stack_el->expanding = expand;
386  stack_el->expr = expr;
387  stack_el->diverted = ds_init(NULL);
388 
389  /*
390  * Merge any global Attr variables into the new context.
391  * Get an initial variable context for the new context; if this is not
392  * happening at the top level, merge the upper levels' Attr variables.
393  */
394  kwv_merge(attrs, tc->global_attrs, KWV_NO_DUPS);
395  if (tc->stackp == NULL) {
396  stack_el->saved_attrs = NULL;
397  stack_el->var_context = var_ns_copy(tc->env->namespaces);
398  }
399  else {
400  Kwv *kwv;
401 
402  kwv = var_ns_lookup_kwv(tc->stackp->var_context, "Attr");
403  stack_el->saved_attrs = kwv;
404  stack_el->var_context = var_ns_copy(tc->stackp->var_context);
405  if (kwv != NULL)
406  kwv_merge(attrs, kwv, KWV_NO_DUPS);
407  }
408  var_ns_replace(&stack_el->var_context, "Attr", attrs);
409 
410  stack_el->prev = tc->stackp;
411  tc->stackp = stack_el;
412  tc->stack_depth++;
413 
414  return(stack_el);
415 }
416 
417 static int
418 pop_stack(Transform_config *tc)
419 {
420  Transform_stack *stack_el;
421 
422  if (tc->stackp == NULL || tc->stack_depth == 0)
423  return(-1);
424 
425  stack_el = tc->stackp;
426  tc->stackp = stack_el->prev;
427  tc->stack_depth--;
428  free(stack_el);
429 
430  return(0);
431 }
432 
433 /*
434  * A quick and dirty API for processing input from FP using
435  * variables KWV in the DACS namespace, but without any rules
436  * (therefore, no directive must require rule evaluation).
437  */
438 Ds *
439 transform_simple_fp(FILE *fp, Kwv *kwv, char **errmsg)
440 {
441  Transform_config *tc;
442 
443  tc = transform_init(NULL);
444 
445  var_ns_new(&tc->env->namespaces, "DACS", kwv);
446  dsio_set(tc->ds_in, fp, NULL, 0, 0);
447  if (transform(tc, "", NULL, errmsg) == -1)
448  return(NULL);
449 
450  return(tc->ds_out);
451 }
452 
453 Ds *
454 transform_simple_file(char *path, Kwv *kwv, char **errmsg)
455 {
456  Ds *ds_out;
457  FILE *fp;
458 
459  if ((fp = fopen(path, "r")) == NULL) {
460  if (errmsg != NULL)
461  *errmsg = ds_xprintf("Cannot read \"%s\": %s", path, strerror(errno));
462  return(NULL);
463  }
464 
465  ds_out = transform_simple_fp(fp, kwv, errmsg);
466  fclose(fp);
467 
468  return(ds_out);
469 }
470 
471 Ds *
472 transform_simple_str(char *input, Kwv *kwv, char **errmsg)
473 {
474  Transform_config *tc;
475 
476  tc = transform_init(NULL);
477 
478  var_ns_new(&tc->env->namespaces, "DACS", kwv);
479  dsio_set(tc->ds_in, NULL, input, 0, 0);
480  if (transform(tc, "", NULL, errmsg) == -1)
481  return(NULL);
482 
483  return(tc->ds_out);
484 }
485 
486 static int
487 test_transform_simple(FILE *fp_out, char *filename)
488 {
489  char *errmsg;
490  Ds *ds_out;
491  FILE *fp;
492  Kwv *kwv;
493 
494  kwv = kwv_init(8);
495  kwv_add(kwv, "dog1", "Auggie");
496  kwv_add(kwv, "dog2", "Harley");
497  kwv_add(kwv, "dog3", "Bandito");
498  errmsg = NULL;
499  ds_out = transform_simple_file(filename, kwv, &errmsg);
500  if (ds_out == NULL) {
501  if (errmsg != NULL)
502  fprintf(stderr, "%s\n", errmsg);
503  return(-1);
504  }
505 
506  if ((fp = fp_out) == NULL)
507  fp = stdout;
508  if (ds_len(ds_out) > 0)
509  fprintf(fp, "%s", ds_buf(ds_out));
510 
511  return(0);
512 }
513 
514 /*
515  * Transform input from OBJECT_NAME, customized for IDENTS (a username,
516  * or unauthenticated if NULL).
517  * Return 0 if ok, -1 if an error occurs.
518  */
519 int
520 transform(Transform_config *tc, char *object_name, char *idents, char **errmsg)
521 {
522  int dcount, elided, expand, granted, invert, recurse, st;
523  char *buf, *e, *regex_str, *region, *line, *s, *suffix, *username;
524  char *opname, *expr_attr, *filename_attr, *uri_attr, *p;
525  char *result_str;
526  regex_t *regex;
527  Acs_expr_result rc;
528  Transform_directive directive;
529  Http_method method;
530  Kwv *kwv_attrs, *kwv_conf, *kwv_dacs;
531  Transform_stack *stack_el, *current_stackp;
532  static Kwv_conf directive_conf = {
533  "=", "'\"`", NULL, KWV_CONF_KEEPQ, NULL, 4, NULL, NULL
534  };
535 
536  if (idents == NULL)
537  username = "unauth";
538  else
539  username = idents;
540  log_msg((LOG_TRACE_LEVEL, "User is %s", username));
541 
542  if ((kwv_conf = var_ns_lookup_kwv(tc->env->namespaces, "Conf")) == NULL)
543  kwv_conf = kwv_init(4);
544  if ((kwv_dacs = var_ns_lookup_kwv(tc->env->namespaces, "DACS")) == NULL)
545  kwv_dacs = kwv_init(4);
546 
547  current_stackp = tc->stackp;
548 
549  /*
550  * XXX could push a pseudo element here to wrap document with a default.
551  * If so, alter final tag balance test
552  */
553 
554  s = NULL;
555  region = NULL;
556  e = NULL;
557  method = HTTP_UNKNOWN_METHOD;
558  expr_attr = NULL;
559 
560  elided = 0;
561 
562  if (tc->regex_prefix != NULL || tc->regex_suffix != NULL) {
563  regex_str = ds_xprintf("^\\(%s\\).*\\(%s\\)\n$",
564  (tc->regex_prefix != NULL)
565  ? tc->regex_prefix : tc->directive_prefix,
566  (tc->regex_suffix != NULL)
567  ? tc->regex_suffix : tc->directive_suffix);
568  regex = ALLOC(regex_t);
569  if (regcomp(regex, regex_str, 0) != 0) {
570  if (errmsg != NULL)
571  *errmsg = "Invalid regular expression";
572  goto fail;
573  }
574  }
575  else {
576  regex = NULL;
577  regex_str = NULL;
578  }
579 
580  /*
581  * Read and process each input line.
582  */
583  tc->linenum = 0;
584  while ((line = dsio_agets(tc->ds_in)) != NULL) {
585  tc->linenum++;
586  if (regex_str == NULL) {
587  if ((st = is_directive(tc, line, &s, &e)) == -1) {
588  if (errmsg != NULL)
589  *errmsg = ds_xprintf("Line %d: invalid DACS tag", tc->linenum);
590  goto fail;
591  }
592  }
593  else if ((st = is_regex_directive(line, regex, &s, &e, errmsg)) == -1)
594  goto fail;
595 
596  if (st == 1) {
597  Kwv *kwv;
598 
599  /* This is a directive. */
600  suffix = strdup(e);
601  *e = '\0';
602 
603  kwv = kwv_init(4);
604  kwv_set_mode(kwv, "-d");
605  if (kwv_make_sep(kwv, s, &directive_conf) == NULL) {
606  if (errmsg != NULL)
607  *errmsg
608  = ds_xprintf("Line %d: invalid attribute specification: \"%s\"",
609  tc->linenum, s);
610  goto fail;
611  }
612 
613  if ((kwv_attrs = attrs(kwv, &tc->env->namespaces)) == NULL) {
614  if (errmsg != NULL)
615  *errmsg = ds_xprintf("Line %d: expression evaluation error",
616  tc->linenum);
617  goto fail;
618  }
619 
620  directive = get_directive(kwv_attrs, &region, errmsg);
621  if (*region == '!')
622  invert = 1;
623  else
624  invert = 0;
625 
626  if ((p = kwv_lookup_value(kwv_attrs, "recurse")) != NULL
627  && !strcaseeq(p, "yes"))
628  recurse = 0;
629  else
630  recurse = 1;
631 
632  if (directive == D_EXPAND)
633  expand = 1;
634  else
635  expand = 0;
636 
637  switch (directive) {
638  case D_BEGIN:
639  /*
640  * begin/end surround content that, only if selected, is variable
641  * expanded (if enabled) and recursively processed (if enabled).
642  */
643  if (tc->stack_depth == tc->max_stack_depth) {
644  if (errmsg != NULL)
645  *errmsg = ds_xprintf("Line %d: DACS tag stack overflow",
646  tc->linenum);
647  goto fail;
648  }
649 
650  stack_el = push_stack(tc, directive, region, kwv_attrs, 1,
651  expand, NULL);
652 
653  log_msg((LOG_TRACE_LEVEL, "begin: check if \"%s\" can access \"%s\"",
654  username, region));
655 
656  st = region_test(tc, region, kwv_attrs, kwv_conf, kwv_dacs, "begin",
657  invert, tc->acls, object_name, idents, errmsg);
658 
659  if (invert == 0 && st == 1) {
660  tc->stackp->skipping = 0;
661  log_msg((LOG_TRACE_LEVEL, "Access granted"));
662  }
663  else if (invert == 0 && st == 0)
664  log_msg((LOG_TRACE_LEVEL, "Access denied"));
665  else if (invert == 1 && st == 0) {
666  tc->stackp->skipping = 0;
667  log_msg((LOG_TRACE_LEVEL, "Access granted (negation)"));
668  }
669  else if (invert == 1 && st == 1)
670  log_msg((LOG_TRACE_LEVEL, "Access denied (negation)"));
671  else {
672  if (errmsg != NULL)
673  *errmsg = ds_xprintf("Line %d: access control test error",
674  tc->linenum);
675  goto fail;
676  }
677 
678  break;
679 
680  case D_END:
681  /*
682  * The current region ends.
683  * This can match D_BEGIN, D_EXPAND, or D_FILTER/D_FILTERV.
684  */
685  if (tc->stackp == NULL) {
686  if (errmsg != NULL)
687  *errmsg = ds_xprintf("Line %d: DACS tag stack underflow",
688  tc->linenum);
689  goto fail;
690  }
691 
692  if (!streq(tc->stackp->region, region)) {
693  if (errmsg != NULL)
694  *errmsg = ds_xprintf("Line %d: DACS tag end mismatch",
695  tc->linenum);
696  goto fail;
697  }
698 
699  log_msg((LOG_TRACE_LEVEL, "End of region \"%s\"", region));
700 
701  if (tc->stackp != NULL && tc->stackp->directive == D_EVAL) {
702  if ((buf = ds_buf(tc->stackp->diverted)) != NULL
703  && *buf != '\0') {
704  /*
705  * This is done only for its side effects and explicit output
706  * via tprintf().
707  */
708  tc->env->namespaces = tc->stackp->var_context;
709  rc = acs_expr_string_env(buf, tc->env, &result_str);
710  if (acs_expr_error_occurred(rc)) {
711  if (errmsg != NULL)
712  *errmsg
713  = ds_xprintf("Line %d: expression evaluation error: \"%s\"",
714  tc->linenum, expr_attr);
715  goto fail;
716  }
717  if (tc->stackp->saved_attrs == NULL)
718  var_ns_delete(&tc->env->namespaces, "Attr");
719  else
720  var_ns_replace(&tc->env->namespaces, "Attr",
721  tc->stackp->saved_attrs);
722  }
723 
724  if (tc->stackp->directive == D_EVAL) {
725  if (tc->stackp->prev != NULL)
726  tc->stackp->prev->var_context = tc->env->namespaces;
727  }
728  else {
729  /* XXX */
730  ds_asprintf(tc->ds_out, "%s%s",
731  result_str,
732  (tc->stackp->directive == D_FILTER) ? "\n" : "");
733  }
734  ds_free(tc->stackp->diverted);
735  }
736  else if (tc->stackp->expanding && !tc->stackp->skipping) {
737  Ds *ds;
738 
739  /* Is this the end of a filtered region? */
740  buf = ds_buf(tc->stackp->diverted);
741  if (buf != NULL && *buf != '\0') {
742  tc->env->namespaces = tc->stackp->var_context;
743 
744  if ((ds = acs_string_operand(buf, tc->env)) == NULL) {
745  if (errmsg != NULL)
746  *errmsg = ds_xprintf("Line %d: expansion error: \"%s\"",
747  tc->linenum, buf);
748  goto fail;
749  }
750  buf = ds_buf(ds);
751  if (tc->stackp->prev != NULL)
752  ds_asprintf(tc->stackp->prev->diverted, "%s", buf);
753  else
754  ds_asprintf(tc->ds_out, "%s", buf);
755  }
756  ds_free(tc->stackp->diverted);
757  }
758  else
759  log_msg((LOG_TRACE_LEVEL, "End of region \"%s\"", region));
760 
761  pop_stack(tc);
762 
763  break;
764 
765  case D_EXPAND:
766  case D_INSERT:
767  case D_INSERTV:
768  /*
769  * Insert content from one of: file, URI, or expression.
770  */
771  dcount = 0;
772  if ((filename_attr = kwv_lookup_value(kwv_attrs, "filename")) != NULL)
773  dcount++;
774 
775  if ((expr_attr = kwv_lookup_value(kwv_attrs, "expr")) != NULL)
776  dcount++;
777 
778  if ((uri_attr = kwv_lookup_value(kwv_attrs, "uri")) != NULL) {
779  char *p;
780 
781  dcount++;
782  if ((p = kwv_lookup_value(kwv_attrs, "method")) != NULL) {
783  if ((method = http_string_to_method(p)) == HTTP_UNKNOWN_METHOD) {
784  if (errmsg != NULL)
785  *errmsg = ds_xprintf("Line %d: unrecognized HTTP method",
786  tc->linenum);
787  goto fail;
788  }
789  }
790  else
791  method = HTTP_GET_METHOD;
792  }
793 
794  if (directive == D_EXPAND && dcount == 0) {
795  /* Require matching D_END... */
796  }
797  else if (dcount != 1) {
798  if (errmsg != NULL)
799  *errmsg = ds_xprintf("Line %d: invalid directive", tc->linenum);
800  goto fail;
801  }
802 
803  if (uri_attr != NULL) {
804  Uri *uri;
805 
806  if ((uri = uri_parse(uri_attr)) == NULL) {
807  if (errmsg != NULL)
808  *errmsg = ds_xprintf("Line %d: invalid URI: %s",
809  tc->linenum, uri_attr);
810  goto fail;
811  }
812  if (streq(uri->scheme, "file")) {
813  if (uri->host != NULL || uri->port_given != NULL) {
814  if (errmsg != NULL)
815  *errmsg = ds_xprintf("Line %d: invalid URI: %s",
816  tc->linenum, uri_attr);
817  goto fail;
818  }
819 
820  filename_attr = uri->path;
821  uri_attr = NULL;
822  }
823  else if (!streq(uri->scheme, "http") &&
824  !streq(uri->scheme, "https")) {
825  if (errmsg != NULL)
826  *errmsg = ds_xprintf("Line %d: invalid URI scheme: %s",
827  tc->linenum, uri_attr);
828  goto fail;
829  }
830  }
831 
832  if (filename_attr != NULL) {
833  if (*filename_attr != '/' && *tc->docs != '\0') {
834  if (errmsg != NULL)
835  *errmsg = ds_xprintf("Line %d: invalid directive", tc->linenum);
836  goto fail;
837  }
838  }
839 
840  if (tc->stack_depth == tc->max_stack_depth) {
841  if (errmsg != NULL)
842  *errmsg = ds_xprintf("Line %d: DACS tag stack overflow",
843  tc->linenum);
844  goto fail;
845  }
846 
847  stack_el
848  = push_stack(tc, directive, region, kwv_attrs, 1, expand, NULL);
849 
850  if (directive == D_EXPAND)
851  opname = "expand";
852  else if (directive == D_INSERT)
853  opname = "insert";
854  else
855  opname = "insertv";
856 
857  log_msg((LOG_TRACE_LEVEL, "%s: check if \"%s\" can access \"%s\"",
858  opname, username, region));
859 
860  st = region_test(tc, region, kwv_attrs, kwv_conf, kwv_dacs, opname,
861  invert, tc->acls, object_name, idents, errmsg);
862 
863  granted = 0;
864  if (invert == 0 && st == 1) {
865  granted = 1;
866  log_msg((LOG_TRACE_LEVEL, "Access granted"));
867  }
868  else if (invert == 0 && st == 0)
869  log_msg((LOG_TRACE_LEVEL, "Access denied"));
870  else if (invert == 1 && st == 0) {
871  granted = 1;
872  log_msg((LOG_TRACE_LEVEL, "Access granted (negation)"));
873  }
874  else if (invert == 1 && st == 1)
875  log_msg((LOG_TRACE_LEVEL, "Access denied (negation)"));
876  else {
877  if (errmsg != NULL)
878  *errmsg = ds_xprintf("Line %d: access control test error",
879  tc->linenum);
880  goto fail;
881  }
882 
883  if (directive == D_EXPAND && dcount == 0 && granted)
884  stack_el->skipping = 0;
885 
886  if (dcount && granted) {
887  /* Get the content to be inserted. */
888  stack_el->skipping = 0;
889 
890  if (filename_attr != NULL) {
891  char *fn;
892 
893  if (*filename_attr == '/' || tc->insert_dir == NULL)
894  fn = filename_attr;
895  else
896  fn = ds_xprintf("%s/%s", tc->insert_dir, filename_attr);
897 
898  if (load_file(fn, &buf, NULL) == -1) {
899  if (errmsg != NULL)
900  *errmsg = ds_xprintf("Line %d: cannot load file: %s",
901  tc->linenum, fn);
902  goto fail;
903  }
904  }
905  else if (uri_attr != NULL) {
906  int argnum, reply_len;
907 
908  argnum = 0;
909  if (http_invoke(uri_attr, method, HTTP_SSL_URL_SCHEME,
910  argnum, NULL, NULL, NULL, &buf, &reply_len,
911  NULL, NULL) == -1) {
912  if (errmsg != NULL)
913  *errmsg = buf;
914  goto fail;
915  }
916  }
917  else if (expr_attr != NULL) {
918  rc = eval(expr_attr, kwv_attrs, &tc->env->namespaces, &buf);
919  if (acs_expr_error_occurred(rc)) {
920  if (errmsg != NULL)
921  *errmsg
922  = ds_xprintf("Line %d: expression evaluation error: \"%s\"",
923  tc->linenum, expr_attr);
924  goto fail;
925  }
926  }
927 
928  /*
929  * Recursively process the new input, if enabled and there is some.
930  */
931  if (recurse && buf != NULL) {
932  Ds *ds_in, *ds_out;
933  Transform_config *ntc;
934 
935  ds_in = ds_init(NULL);
936  dsio_set(ds_in, NULL, buf, strlen(buf), 1);
937  ds_out = ds_init(NULL);
938  ntc = transform_reconfig(tc, ds_in, ds_out);
939  ntc->env->namespaces = tc->stackp->var_context;
940  if ((st = transform(ntc, object_name, idents, errmsg)) != 0)
941  goto fail;
942  buf = ds_buf(stack_el->diverted);
943  free(ntc);
944  }
945 
946  /*
947  * Expand the content (for variable references), if necessary.
948  */
949  if (tc->stackp->expanding && buf != NULL) {
950  Ds *ds;
951 
952  tc->env->namespaces = tc->stackp->var_context;
953 
954  if ((ds = acs_string_operand(buf, tc->env)) == NULL) {
955  if (errmsg != NULL)
956  *errmsg = ds_xprintf("Line %d: expansion error: \"%s\"",
957  tc->linenum, buf);
958  goto fail;
959  }
960  buf = ds_buf(ds);
961  if (tc->stackp->prev != NULL)
962  ds_asprintf(tc->stackp->prev->diverted, "%s", buf);
963  else
964  ds_asprintf(tc->ds_out, "%s", buf);
965  ds_free(tc->stackp->diverted);
966  }
967  else {
968  /*
969  * Insert the content.
970  */
971  if (tc->stackp->prev != NULL)
972  ds_asprintf(tc->stackp->prev->diverted, "%s", buf);
973  else
974  ds_asprintf(tc->ds_out, "%s%s",
975  buf, (directive == D_INSERT) ? "\n" : "");
976  }
977  }
978 
979  if (dcount)
980  pop_stack(tc);
981 
982  break;
983 
984  case D_ID:
985  kwv_merge(kwv_attrs, tc->global_attrs, KWV_NO_DUPS);
986 
987  st = region_test(tc, region, kwv_attrs, kwv_conf, kwv_dacs, "id",
988  invert, tc->acls, object_name, idents, errmsg);
989 
990  granted = 0;
991  if (invert == 0 && st == 1) {
992  granted = 1;
993  log_msg((LOG_TRACE_LEVEL, "Access granted"));
994  }
995  else if (invert == 0 && st == 0)
996  log_msg((LOG_TRACE_LEVEL, "Access denied"));
997  else if (invert == 1 && st == 0) {
998  granted = 1;
999  log_msg((LOG_TRACE_LEVEL, "Access granted (negation)"));
1000  }
1001  else if (invert == 1 && st == 1)
1002  log_msg((LOG_TRACE_LEVEL, "Access denied (negation)"));
1003  else {
1004  if (errmsg != NULL)
1005  *errmsg = ds_xprintf("Line %d: access control test error",
1006  tc->linenum);
1007  goto fail;
1008  }
1009 
1010  if (granted) {
1011  struct utsname utsname;
1012  Ds *out;
1013 
1014  if (tc->stackp != NULL && tc->stackp->diverted != NULL)
1015  out = tc->stackp->diverted;
1016  else
1017  out = tc->ds_out;
1018  uname(&utsname);
1019  ds_asprintf(out, "%sGenerated %s by %s %s on %s %s",
1020  tc->directive_prefix,
1022  log_module_name,
1024  utsname.nodename,
1025  suffix);
1026  }
1027  break;
1028 
1029  case D_DEBUG:
1030  kwv_merge(kwv_attrs, tc->global_attrs, KWV_NO_DUPS);
1031 
1032  st = region_test(tc, region, kwv_attrs, kwv_conf, kwv_dacs, "debug",
1033  invert, tc->acls, object_name, idents, errmsg);
1034 
1035  granted = 0;
1036  if (invert == 0 && st == 1) {
1037  granted = 1;
1038  log_msg((LOG_TRACE_LEVEL, "Access granted"));
1039  }
1040  else if (invert == 0 && st == 0)
1041  log_msg((LOG_TRACE_LEVEL, "Access denied"));
1042  else if (invert == 1 && st == 0) {
1043  granted = 1;
1044  log_msg((LOG_TRACE_LEVEL, "Access granted (negation)"));
1045  }
1046  else if (invert == 1 && st == 1)
1047  log_msg((LOG_TRACE_LEVEL, "Access denied (negation)"));
1048  else {
1049  if (errmsg != NULL)
1050  *errmsg = ds_xprintf("Line %d: access control test error",
1051  tc->linenum);
1052  goto fail;
1053  }
1054 
1055  if (granted) {
1056  int show_attrs, show_dacs, show_conf;
1057  Ds *out;
1058 
1059  show_attrs = show_dacs = show_conf = 1;
1060  if ((p = kwv_lookup_value(kwv_attrs, "show")) != NULL) {
1061  show_attrs = show_dacs = show_conf = 0;
1062  if (strcaseeq(p, "Attrs"))
1063  show_attrs = 1;
1064  else if (strcaseeq(p, "DACS"))
1065  show_dacs = 1;
1066  else if (strcaseeq(p, "Conf"))
1067  show_conf = 1;
1068  else if (strcaseeq(p, "all"))
1069  show_attrs = show_dacs = show_conf = 1;
1070  }
1071  if ((p = kwv_lookup_value(kwv_attrs, "Attrs")) != NULL) {
1072  if (strcaseeq(p, "no"))
1073  show_attrs = 0;
1074  else
1075  show_attrs = 1;
1076  }
1077  if ((p = kwv_lookup_value(kwv_attrs, "Conf")) != NULL) {
1078  if (strcaseeq(p, "no"))
1079  show_conf = 0;
1080  else
1081  show_conf = 1;
1082  }
1083  if ((p = kwv_lookup_value(kwv_attrs, "DACS")) != NULL) {
1084  if (strcaseeq(p, "no"))
1085  show_dacs = 0;
1086  else
1087  show_dacs = 1;
1088  }
1089 
1090  if (tc->stackp != NULL && tc->stackp->diverted != NULL)
1091  out = tc->stackp->diverted;
1092  else
1093  out = tc->ds_out;
1094 
1095  ds_asprintf(out, "%sDebug:\n", tc->directive_prefix);
1096  if (show_attrs && kwv_count(kwv_attrs, NULL) > 0)
1097  ds_asprintf(out, "Attrs:\n%s",
1098  kwv_buf(kwv_attrs, (int) '=', (int) '"'));
1099  if (show_dacs && kwv_count(kwv_dacs, NULL) > 0)
1100  ds_asprintf(out, "DACS:\n%s",
1101  kwv_buf(kwv_dacs, (int) '=', (int) '"'));
1102  if (show_conf && kwv_count(kwv_conf, NULL) > 0)
1103  ds_asprintf(out, "Conf:\n%s",
1104  kwv_buf(kwv_conf, (int) '=', (int) '"'));
1105  ds_asprintf(out, "%s", suffix);
1106  }
1107 
1108  break;
1109 
1110  case D_COMMENT:
1111  if (tc->stackp == NULL || !tc->stackp->skipping) {
1112  Ds *out;
1113 
1114  if (tc->stackp != NULL && tc->stackp->diverted != NULL)
1115  out = tc->stackp->diverted;
1116  else
1117  out = tc->ds_out;
1118 
1119  ds_asprintf(out, "%s%s", line, suffix);
1120  elided = 0;
1121  }
1122 
1123  break;
1124 
1125  case D_EVAL:
1126  if (tc->stack_depth == tc->max_stack_depth) {
1127  if (errmsg != NULL)
1128  *errmsg = ds_xprintf("Line %d: DACS tag stack overflow",
1129  tc->linenum);
1130  goto fail;
1131  }
1132 
1133  stack_el
1134  = push_stack(tc, directive, region, kwv_attrs, 1, expand, NULL);
1135 
1136  log_msg((LOG_TRACE_LEVEL, "filter: check if \"%s\" can access \"%s\"",
1137  username, region));
1138 
1139  st = region_test(tc, region, kwv_attrs, kwv_conf, kwv_dacs, "eval",
1140  invert, tc->acls, object_name, idents, errmsg);
1141 
1142  if (invert == 0 && st == 1) {
1143  tc->stackp->skipping = 0;
1144  log_msg((LOG_TRACE_LEVEL, "Access granted"));
1145  }
1146  else if (invert == 0 && st == 0)
1147  log_msg((LOG_TRACE_LEVEL, "Access denied"));
1148  else if (invert == 1 && st == 0) {
1149  tc->stackp->skipping = 0;
1150  log_msg((LOG_TRACE_LEVEL, "Access granted (negation)"));
1151  }
1152  else if (invert == 1 && st == 1)
1153  log_msg((LOG_TRACE_LEVEL, "Access denied (negation)"));
1154  else {
1155  if (errmsg != NULL)
1156  *errmsg = ds_xprintf("Line %d: access control test error",
1157  tc->linenum);
1158  goto fail;
1159  }
1160 
1161  break;
1162 
1163  case D_SET:
1164  kwv_merge(tc->global_attrs, kwv_attrs, KWV_REPLACE_DUPS);
1165 
1166  st = region_test(tc, region, kwv_attrs, kwv_conf, kwv_dacs, "set",
1167  invert, tc->acls, object_name, idents, errmsg);
1168 
1169  granted = 0;
1170  if (invert == 0 && st == 1) {
1171  granted = 1;
1172  log_msg((LOG_TRACE_LEVEL, "Access granted"));
1173  }
1174  else if (invert == 0 && st == 0)
1175  log_msg((LOG_TRACE_LEVEL, "Access denied"));
1176  else if (invert == 1 && st == 0) {
1177  granted = 1;
1178  log_msg((LOG_TRACE_LEVEL, "Access granted (negation)"));
1179  }
1180  else if (invert == 1 && st == 1)
1181  log_msg((LOG_TRACE_LEVEL, "Access denied (negation)"));
1182  else {
1183  if (errmsg != NULL)
1184  *errmsg = ds_xprintf("Line %d: access control test error",
1185  tc->linenum);
1186  goto fail;
1187  }
1188 
1189  if (granted)
1190  kwv_merge(tc->global_attrs, kwv_attrs, KWV_NO_DUPS);
1191 
1192  break;
1193 
1194  case D_FILTER:
1195  case D_FILTERV:
1196  /*
1197  * If document content is included, it will either be fed to an
1198  * expression or be evaluated as an expression.
1199  */
1200  dcount = 0;
1201  if ((expr_attr = kwv_lookup_value(kwv_attrs, "expr")) != NULL)
1202  dcount++;
1203 
1204  if ((uri_attr = kwv_lookup_value(kwv_attrs, "uri")) != NULL) {
1205  char *p;
1206 
1207  dcount++;
1208  if ((p = kwv_lookup_value(kwv_attrs, "method")) != NULL) {
1209  if ((method = http_string_to_method(p)) == HTTP_UNKNOWN_METHOD) {
1210  if (errmsg != NULL)
1211  *errmsg = ds_xprintf("Line %d: unrecognized HTTP method",
1212  tc->linenum);
1213  goto fail;
1214  }
1215  }
1216  else
1217  method = HTTP_POST_METHOD;
1218  }
1219 
1220  if (dcount != 1) {
1221  if (errmsg != NULL)
1222  *errmsg = ds_xprintf("Line %d: invalid directive", tc->linenum);
1223  goto fail;
1224  }
1225 
1226  if (uri_attr != NULL) {
1227  if (errmsg != NULL)
1228  *errmsg = ds_xprintf("Line %d: unimplemented directive",
1229  tc->linenum);
1230  goto fail;
1231  }
1232 
1233  if (tc->stack_depth == tc->max_stack_depth) {
1234  if (errmsg != NULL)
1235  *errmsg = ds_xprintf("Line %d: DACS tag stack overflow",
1236  tc->linenum);
1237  goto fail;
1238  }
1239 
1240  stack_el
1241  = push_stack(tc, directive, region, kwv_attrs, 1, expand, NULL);
1242 
1243 
1244  log_msg((LOG_TRACE_LEVEL, "filter: check if \"%s\" can access \"%s\"",
1245  username, region));
1246 
1247  if (directive == D_FILTER)
1248  opname = "filter";
1249  else
1250  opname = "filterv";
1251  st = region_test(tc, region, kwv_attrs, kwv_conf, kwv_dacs, opname,
1252  invert, tc->acls, object_name, idents, errmsg);
1253 
1254  if (invert == 0 && st == 1) {
1255  tc->stackp->skipping = 0;
1256  log_msg((LOG_TRACE_LEVEL, "Access granted"));
1257  }
1258  else if (invert == 0 && st == 0)
1259  log_msg((LOG_TRACE_LEVEL, "Access denied"));
1260  else if (invert == 1 && st == 0) {
1261  tc->stackp->skipping = 0;
1262  log_msg((LOG_TRACE_LEVEL, "Access granted (negation)"));
1263  }
1264  else if (invert == 1 && st == 1)
1265  log_msg((LOG_TRACE_LEVEL, "Access denied (negation)"));
1266  else {
1267  if (errmsg != NULL)
1268  *errmsg = ds_xprintf("Line %d: access control test error",
1269  tc->linenum);
1270  goto fail;
1271  }
1272 
1273  if (tc->stackp->skipping == 0) {
1274  tc->stackp->diverted = ds_init(NULL);
1275  /*
1276  tc->stackp->attrs = kwv_attrs;
1277  */
1278  }
1279 
1280  break;
1281 
1282  case D_NONE:
1283  /*
1284  * The directive does not contain any attributes or does not contain
1285  * a recognized attribute name.
1286  */
1287  if (errmsg != NULL)
1288  *errmsg = ds_xprintf("Line %d: unrecognized directive", tc->linenum);
1289  goto fail;
1290  /*NOTREACHED*/
1291 
1292  default:
1293  case D_ERROR:
1294  /* An invalid directive. */
1295  if (errmsg != NULL)
1296  *errmsg = ds_xprintf("Line %d: %s", tc->linenum, errmsg);
1297  goto fail;
1298  /*NOTREACHED*/
1299  }
1300  }
1301  else {
1302  /*
1303  * This is not a directive, just content.
1304  * Emit it if we're emitting, otherwise emit an annotation or nothing
1305  * at all in its place.
1306  */
1307  if (tc->stackp == NULL || !tc->stackp->skipping) {
1308  if (tc->stackp != NULL && tc->stackp->diverted != NULL)
1309  ds_asprintf(tc->stackp->diverted, "%s", line);
1310  else
1311  ds_asprintf(tc->ds_out, "%s", line);
1312  elided = 0;
1313  }
1314  else if (tc->annotation != NULL) {
1315  if (elided == 0) {
1316  /* XXX This needs to depend on the document's Content-Type */
1317  ds_asprintf(tc->ds_out, "<br/>%s<br/>\n", tc->annotation);
1318  }
1319  elided = 1;
1320  }
1321  }
1322  ds_reset(tc->ds_in);
1323  }
1324 
1325  if (tc->stackp != current_stackp) {
1326  if (errmsg != NULL)
1327  *errmsg = "Directive tags are unbalanced";
1328  goto fail;
1329  }
1330 
1331  return(0);
1332 
1333  fail:
1334 
1335  return(-1);
1336 }
1337 
1338 /*
1339  * Transform either DOCNAME (a local file) or the document at DOCURI.
1340  * DOCNAME may be "/dev/stdin" with the obvious behaviour.
1341  * NAME is the object name to use instead of DOCNAME.
1342  * If transform_docs is a non-empty string, it means that DOCNAME is
1343  * a subdirectory of transform_docs; i.e., DOCNAME is being served from
1344  * a particular area in the file system.
1345  *
1346  * DOCURI is retrieved via the GET method.
1347  *
1348  * Take care with DOCNAME, especially wrt a "/../" component, which might be
1349  * used in an attempt to escape from the transform_docs directory.
1350  *
1351  * The MIME type of the emitted document is CONTENT_TYPE, if provided;
1352  * otherwise an attempt is made to deduce the type from the filename suffix of
1353  * DOCNAME or the path suffix of DOCURI using Apache's mime.types file.
1354  * If that fails, we fall back on a default.
1355  *
1356  * Return 0 if all goes well, -1 otherwise (and set ERRMSG).
1357  */
1358 static int
1359 do_transform(Transform_config *tc, char *docname, char *docuri, char *name,
1360  char *idents, char *content_type, char **errmsg)
1361 {
1362  int st;
1363  char *doctype, *ext, *object_name;
1364  char *path;
1365  Dsvec *mime_types;
1366  FILE *fp;
1367  Mime_file_type *mft;
1368 
1369  fp = NULL;
1370  path = NULL;
1371 
1372  if (docname != NULL) {
1373  /* XXX disallow ".." within DOCNAME too */
1374  if (strstr(docname, "/../")
1375  || strsuffix(docname, strlen(docname), "/") != NULL) {
1376  if (errmsg != NULL)
1377  *errmsg = "Invalid DOC name";
1378  goto fail;
1379  }
1380  if (*tc->docs == '\0')
1381  path = docname;
1382  else
1383  path = ds_xprintf("%s/%s", tc->docs, docname + 1);
1384  if ((fp = fopen(path, "r")) == NULL) {
1385  if (errmsg != NULL)
1386  *errmsg = ds_xprintf("Cannot load \"%s\"", path);
1387  goto fail;
1388  }
1389  dsio_set(tc->ds_in, fp, NULL, 0, 0);
1390  log_msg((LOG_TRACE_LEVEL, "File is %s", path));
1391  }
1392  else {
1393  int argnum, reply_len;
1394  char *reply;
1395  Uri *uri;
1396 
1397  if ((uri = uri_parse(docuri)) == NULL) {
1398  if (errmsg != NULL)
1399  *errmsg = ds_xprintf("Invalid URI: %s", docuri);
1400  goto fail;
1401  }
1402  path = uri->path;
1403  argnum = 0;
1404  reply_len = -1;
1406  argnum, NULL, NULL, NULL, &reply, &reply_len,
1407  NULL, NULL) == -1) {
1408  if (errmsg != NULL)
1409  *errmsg = reply;
1410  goto fail;
1411  }
1412  dsio_set(tc->ds_in, NULL, reply, reply_len, 1);
1413  }
1414 
1415  if (name == NULL) {
1416  if (docname != NULL)
1417  object_name = docname;
1418  else
1419  object_name = path;
1420  }
1421  else
1422  object_name = name;
1423 
1424  if (object_name == NULL) {
1425  if (errmsg != NULL)
1426  *errmsg = "No name is available for the input document";
1427  goto fail;
1428  }
1429  if (*object_name != '/')
1430  object_name = ds_xprintf("/%s", object_name);
1431 
1432  /* Figure out the right content type to emit, if any. */
1433  if (content_type != NULL) {
1434  if (*content_type == '\0')
1435  doctype = NULL;
1436  else
1437  doctype = content_type;
1438  }
1439  else {
1440  /*
1441  * The document type-by-extension will be lost because the document
1442  * is being fetched indirectly, so we need to set it explicitly.
1443  */
1444  doctype = "text/html";
1445  if ((ext = strextname(path)) != NULL) {
1446  if ((mime_types
1447  = mime_get_file_types(ds_xprintf("%s/conf/mime.types",
1448  APACHE_HOME))) != NULL) {
1449  if ((mft = mime_find_file_type(mime_types, ext + 1)) != NULL)
1450  doctype = mft->type_name;
1451  }
1452  else {
1453  if (path != NULL) {
1454  if (strcaseeq(ext, ".xml"))
1455  doctype = "text/xml";
1456  else if (strcaseeq(ext, ".txt"))
1457  doctype = "text/plain";
1458  }
1459  }
1460  }
1461  }
1462 
1463  if (doctype != NULL)
1464  printf("Content-Type: %s\n\n", doctype);
1465 
1466  st = transform(tc, object_name, idents, errmsg);
1467  if (fp != NULL)
1468  fclose(fp);
1469 
1470  if (ds_len(tc->ds_out) > 0)
1471  printf("%s", ds_buf(tc->ds_out));
1472 
1473  return(st);
1474 
1475  fail:
1476  if (fp != NULL)
1477  fclose(fp);
1478 
1479  return(-1);
1480 }
1481 
1482 static void
1483 dacs_usage(void)
1484 {
1485 
1486  fprintf(stderr, "Usage: dacstransform [dacsoptions] [flags] [- | file]\n");
1487  fprintf(stderr, "flags are composed from:\n");
1488  fprintf(stderr, "-admin: following idents are DACS admins\n");
1489  fprintf(stderr, "-ct str: the MIME content type string\n");
1490  fprintf(stderr, "-docs dir: directory containing the documents\n");
1491  fprintf(stderr, "-f: don't map the file's location\n");
1492  fprintf(stderr, "-F sep: use sep as the field separator for roles\n");
1493  fprintf(stderr, "-fd domain: the federation domain\n");
1494  fprintf(stderr, "-fh hostname: the hostname\n");
1495  fprintf(stderr, "-fj jurname: the jurisdiction name\n");
1496  fprintf(stderr, "-fn fedname: the federation name\n");
1497  fprintf(stderr, "-h|-help: show this help blurb\n");
1498  fprintf(stderr, "-i ident: use ident\n");
1499  fprintf(stderr, "-icgi: use REMOTE_USER for username\n");
1500  fprintf(stderr, "-icgig: like -icgi but also add roles\n");
1501  fprintf(stderr, "-ieuid: add the euid to set of identities\n");
1502  fprintf(stderr, "-ieuidg: like -ieuid but also add its groups\n");
1503  fprintf(stderr, "-il ident: use ident, a local name\n");
1504  fprintf(stderr, "-ilg ident: use local ident with its Unix groups\n");
1505  fprintf(stderr, "-insert dir: directory containing inserted documents\n");
1506  fprintf(stderr, "-iuid: add the uid to set of identities\n");
1507  fprintf(stderr, "-iuidg: like -iuid but also add its groups\n");
1508  fprintf(stderr, "-lg: add Unix groups to roles of locals\n");
1509  fprintf(stderr, "-name str: the name of the input document\n");
1510  fprintf(stderr, "-prefix str: the directive prefix string\n");
1511  fprintf(stderr, "-r|-rules rules_uri: where to look for rules\n");
1512  fprintf(stderr, "-roles roles_vfs: where to look for roles\n");
1513  fprintf(stderr, "-rprefix regex: the directive prefix, as a regex\n");
1514  fprintf(stderr, "-rsuffix regex: the directive suffix, as a regex\n");
1515  fprintf(stderr, "-suffix str: the directive suffix string\n");
1516  fprintf(stderr, "-var name=value: set name to value in DACS namespace\n");
1517  fprintf(stderr, "-x: run as an app, not a web service\n");
1518  fprintf(stderr, "--: end of flag arguments\n");
1519  fprintf(stderr, "\n");
1520  fprintf(stderr, "The standard input is read if no file is specified or\n");
1521  fprintf(stderr, "if '-' is specified. This implies the -f flag.\n");
1522  fprintf(stderr, "Unless the -f flag appears, a file argument is mapped\n");
1523  fprintf(stderr, "to the document directory.\n");
1524  fprintf(stderr, "The default document directory is \"%s\"\n",
1526  fprintf(stderr, "The default ACL URI is \"%s\"\n",
1528 
1529  exit(1);
1530 }
1531 
1532 int
1533 dacstransform_main(int argc, char **argv, int do_init, void *main_out)
1534 {
1535  int exec_run, i, st;
1536  char *content_type, *doc, *errmsg, *idents, *name, *p, *remote_addr, *uri;
1537  char *acls, *annotation, *directive_prefix, *directive_suffix, *docs;
1538  char *insert_dir, *regex_prefix, *regex_suffix;
1539  DACS_app_type app_type;
1540  Kwv *kwv, *kwv_conf, *kwv_dacs;
1541  Transform_config *tc, transform_config;
1542  Var_ns *env_ns;
1543 
1544  errmsg = "Internal error";
1545 
1546  tc = transform_init(&transform_config);
1547  acls = NULL;
1548  docs = NULL;
1549  insert_dir = NULL;
1550  content_type = NULL;
1551  directive_prefix = NULL;
1552  directive_suffix = NULL;
1553  annotation = NULL;
1554  regex_prefix = NULL;
1555  regex_suffix = NULL;
1556 
1557  doc = uri = NULL;
1558  name = NULL;
1559  idents = NULL;
1560 
1561  /* Tentatively... */
1562  if ((remote_addr = getenv("REMOTE_ADDR")) == NULL)
1563  app_type = DACS_STANDALONE;
1564  else
1565  app_type = DACS_WEB_SERVICE;
1566 
1567 #ifdef NOTDEF
1568  for (i = 0; i < argc; i++)
1569  fprintf(stderr, "%d: \"%s\"\n", i, argv[i]);
1570 #endif
1571 
1572  /*
1573  * If run as a script via:
1574  * #! .../dacs
1575  * then everything that follows the command name on the line is passed as
1576  * a single argument to this program, followed by the name of the file
1577  * being executed, and any command line arguments as individual arguments.
1578  * See execve(2).
1579  */
1580  exec_run = 0;
1581  if (streq(argv[0], "dacstransform") || streq(argv[0], "transform")
1582  || strsuffix(argv[0], strlen(argv[0]), "/dacstransform") != NULL
1583  || strsuffix(argv[0], strlen(argv[0]), "/transform") != NULL) {
1584  if ((argc == 2 && argv[1][0] != '-')
1585  || (argc == 3 && argv[1][0] == '-')) {
1586  Dsvec *av;
1587 
1588  exec_run = 1;
1589  av = ds_mkargv_add(NULL, argv[0]);
1590  if (argc == 3) {
1591  ds_mkargv(av, argv[1], NULL);
1592  ds_mkargv_add(av, argv[2]);
1593  }
1594  else
1595  ds_mkargv_add(av, argv[1]);
1596  argc = dsvec_len(av) - 1;
1597  argv = (char **) dsvec_base(av);
1598  doc = "/dev/stdin";
1599  app_type = DACS_STANDALONE;
1600  }
1601  }
1602 
1603 #ifdef NOTDEF
1604  for (i = 0; i < argc; i++)
1605  fprintf(stderr, "%d: \"%s\"\n", i, argv[i]);
1606 #endif
1607 
1608  for (i = 1; argv[i] != NULL; i++) {
1609  if (streq(argv[i], "-x")) {
1610  app_type = DACS_STANDALONE;
1611  break;
1612  }
1613  }
1614 
1615  if (app_type == DACS_STANDALONE)
1616  log_module_name = "dacstransform";
1617  else
1618  log_module_name = "dacs_transform";
1619 
1620  if (dacs_init(app_type, &argc, &argv, &kwv, &errmsg) == -1) {
1621  fail:
1622  if (app_type == DACS_STANDALONE) {
1623  if (errmsg)
1624  fprintf(stderr, "Error: %s\n", errmsg);
1625  else
1626  fprintf(stderr, "An error occurred\n");
1627 
1628  exit(1);
1629  }
1630 
1631  if (errmsg)
1632  log_msg((LOG_ERROR_LEVEL, "Error: %s", errmsg));
1633 
1634  emit_html_header(stdout, NULL);
1635  printf("<p>An error occurred during the service request.\n");
1636  if (errmsg != NULL)
1637  printf("<p>Reason: %s.\n", errmsg);
1638  emit_html_trailer(stdout);
1639 
1640  exit(1);
1641  }
1642 
1643  kwv_conf = kwv_init(10);
1644  kwv_dacs = kwv_dacsoptions;
1645  kwv_set_mode(kwv_dacs, "dr");
1646  kwv_set_mode(kwv, "+i");
1647 
1648  if (app_type == DACS_STANDALONE) {
1649  for (i = 1; i < argc; i++) {
1650  if (argv[i][0] != '-')
1651  break;
1652 
1653  st = set_ident_config(&i, argv, kwv_dacs, kwv_conf, &idents, &errmsg);
1654  if (st == -1)
1655  goto fail;
1656  if (st > 0) {
1657  i--;
1658  continue;
1659  }
1660  else if (streq(argv[i], "-docs")) {
1661  if (++i == argc) {
1662  errmsg = ds_xprintf("Document VFS expected after %s", argv[i - 1]);
1663  goto fail;
1664  }
1665  if (docs != NULL) {
1666  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1667  goto fail;
1668  }
1669  docs = argv[i];
1670  }
1671  else if (streq(argv[i], "-insert")) {
1672  if (++i == argc) {
1673  errmsg = ds_xprintf("Document VFS expected after %s", argv[i - 1]);
1674  goto fail;
1675  }
1676  if (insert_dir != NULL) {
1677  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1678  goto fail;
1679  }
1680  insert_dir = argv[i];
1681  }
1682  else if (streq(argv[i], "-f"))
1683  docs = "";
1684  else if (streq(argv[i], "-x")) {
1685  /* Ignore here */
1686  }
1687  else if (streq(argv[i], "-h") || streq(argv[i], "-help")) {
1688  dacs_usage();
1689  /*NOTREACHED*/
1690  }
1691  else if (streq(argv[i], "-name")) {
1692  if (++i == argc) {
1693  errmsg = ds_xprintf("Name string expected after %s", argv[i - 1]);
1694  goto fail;
1695  }
1696  if (name != NULL) {
1697  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1698  goto fail;
1699  }
1700  name = argv[i];
1701  }
1702  else if (streq(argv[i], "-prefix")) {
1703  if (++i == argc) {
1704  errmsg = ds_xprintf("Prefix string expected after %s", argv[i - 1]);
1705  goto fail;
1706  }
1707  if (directive_prefix != NULL) {
1708  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1709  goto fail;
1710  }
1711  directive_prefix = argv[i];
1712  }
1713  else if (streq(argv[i], "-rprefix")) {
1714  if (++i == argc) {
1715  errmsg = ds_xprintf("Regex prefix string expected after %s",
1716  argv[i - 1]);
1717  goto fail;
1718  }
1719  if (regex_prefix != NULL) {
1720  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1721  goto fail;
1722  }
1723  regex_prefix = argv[i];
1724  }
1725  else if (streq(argv[i], "-rsuffix")) {
1726  if (++i == argc) {
1727  errmsg = ds_xprintf("Regex suffix string expected after %s",
1728  argv[i - 1]);
1729  goto fail;
1730  }
1731  if (regex_suffix != NULL) {
1732  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1733  goto fail;
1734  }
1735  regex_suffix = argv[i];
1736  }
1737  else if (streq(argv[i], "-suffix")) {
1738  if (++i == argc) {
1739  errmsg = ds_xprintf("Suffix string expected after %s", argv[i - 1]);
1740  goto fail;
1741  }
1742  if (directive_suffix != NULL) {
1743  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1744  goto fail;
1745  }
1746  directive_suffix = argv[i];
1747  }
1748  else if (streq(argv[i], "-ct")) {
1749  if (++i == argc) {
1750  errmsg = ds_xprintf("Content type string expected after %s",
1751  argv[i - 1]);
1752  goto fail;
1753  }
1754  if (content_type != NULL) {
1755  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1756  goto fail;
1757  }
1758  content_type = argv[i];
1759  }
1760  else if (streq(argv[i], "-r") || streq(argv[i], "-rules")) {
1761  Vfs_directive *vd;
1762 
1763  if (++i == argc) {
1764  errmsg = ds_xprintf("Ruleset VFS expected after %s", argv[i - 1]);
1765  goto fail;
1766  }
1767  if (acls != NULL) {
1768  errmsg = ds_xprintf("Duplicate %s specification", argv[i - 1]);
1769  goto fail;
1770  }
1771  if ((vd = vfs_uri_parse(argv[i], NULL)) == NULL) {
1772  if (argv[i][0] != '/') {
1773  errmsg = "Invalid ruleset VFS";
1774  goto fail;
1775  }
1776  acls = ds_xprintf("[transform-acls]dacs-fs:%s", argv[i]);
1777  }
1778  else
1779  acls = argv[i];
1780  }
1781  else if (streq(argv[i], "-var")) {
1782  char *def, *varname, *varvalue;
1783 
1784  if (++i == argc) {
1785  errmsg = "Variable name=value required after -var";
1786  goto fail;
1787  }
1788  def = argv[i];
1789 
1790  if (kwv_parse_str(def, &varname, &varvalue) == -1
1791  || !var_ns_is_valid_varname(varname, NULL)) {
1792  errmsg = "Usage: -var name=value";
1793  goto fail;
1794  }
1795 
1796  if (kwv_add_nocopy(kwv_dacs, varname, varvalue) == NULL) {
1797  errmsg = ds_xprintf("Can't initialize: %s", def);
1798  goto fail;
1799  }
1800  }
1801  else if (streq(argv[i], "--")) {
1802  i++;
1803  break;
1804  }
1805  else
1806  break;
1807  }
1808 
1809  if (argv[i] == NULL || streq(argv[i], "-")) {
1810  doc = "/dev/stdin";
1811  docs = "";
1812  }
1813  else if (argv[i + 1] != NULL) {
1814  errmsg = "Only one document may be specified";
1815  goto fail;
1816  }
1817  else {
1818  if (strcaseprefix(argv[i], "http://") ||
1819  strcaseprefix(argv[i], "https://"))
1820  uri = argv[i];
1821  else
1822  doc = argv[i];
1823  }
1824 
1825  if (docs == NULL)
1826  docs = "";
1827  if (content_type == NULL)
1828  content_type = "";
1829  }
1830  else {
1831  doc = kwv_lookup_value(kwv, "DOC");
1832  uri = kwv_lookup_value(kwv, "DOCURI");
1833  /* An ANNOTATE argument overrides any other annotation config. */
1834  if ((p = kwv_lookup_value(kwv, "ANNOTATE")) != NULL && strcaseeq(p, "yes"))
1835  annotation = DEFAULT_HTML_ANNOTATION;
1836 
1837  content_type = kwv_lookup_value(kwv, "CONTENT_TYPE");
1838  }
1839 
1840  if (doc == NULL && uri == NULL) {
1841  errmsg = "DOC or DOCURI argument is required";
1842  goto fail;
1843  }
1844 
1845  if (doc != NULL) {
1846  if (uri != NULL) {
1847  errmsg = "Only one of DOC or DOCURI can be specified";
1848  goto fail;
1849  }
1850  if (app_type == DACS_WEB_SERVICE && *doc != '/') {
1851  errmsg = "DOC must be an absolute path";
1852  goto fail;
1853  }
1854  }
1855  else {
1856  if (strcaseprefix(uri, "http://") == NULL
1857  && strcaseprefix(uri, "https://") == NULL) {
1858  errmsg = "Unrecognized URI scheme";
1859  goto fail;
1860  }
1861  }
1862 
1863  if (acls != NULL)
1864  tc->acls = acls;
1865  if (docs != NULL)
1866  tc->docs = docs;
1867  if (directive_prefix != NULL)
1868  tc->directive_prefix = directive_prefix;
1869  if (directive_suffix != NULL)
1870  tc->directive_suffix = directive_suffix;
1871  if (regex_prefix != NULL)
1872  tc->regex_prefix = regex_prefix;
1873  if (regex_suffix != NULL)
1874  tc->regex_suffix = regex_suffix;
1875 
1876  tc->insert_dir = insert_dir;
1877 
1878  log_msg((LOG_TRACE_LEVEL, "transform_docs=\"%s\"", non_null(tc->docs)));
1879  log_msg((LOG_TRACE_LEVEL, "transform_acls=\"%s\"", non_null(tc->acls)));
1880  log_msg((LOG_TRACE_LEVEL, "directive_prefix=\"%s\"",
1881  non_null(tc->directive_prefix)));
1882  log_msg((LOG_TRACE_LEVEL, "directive_suffix=\"%s\"",
1883  non_null(tc->directive_suffix)));
1884  log_msg((LOG_TRACE_LEVEL, "regex_prefix=\"%s\"",
1885  non_null(tc->regex_prefix)));
1886  log_msg((LOG_TRACE_LEVEL, "regex_suffix=\"%s\"",
1887  non_null(tc->regex_suffix)));
1888 
1889  if (idents == NULL) {
1890  char *user;
1891 
1892  if ((user = getenv("REMOTE_USER")) != NULL
1893  && *user != '\0' && !streq(user, "unauth")) {
1894  char *roles;
1895 
1896  roles = getenv("DACS_ROLES");
1897  idents = make_ident(user, roles, NULL, 0, remote_addr);
1898  }
1899  }
1900 
1901  var_ns_new(&tc->env->namespaces, "DACS", kwv_dacs);
1902  if (dacs_conf != NULL && dacs_conf->conf_var_ns != NULL)
1905 
1906  if ((env_ns = var_ns_from_env("Env")) != NULL)
1907  var_ns_new(&tc->env->namespaces, "Env", env_ns->kwv);
1908 
1909  if (do_transform(tc, doc, uri, name, idents, content_type, &errmsg) == -1)
1910  goto fail;
1911 
1912  exit(0);
1913 }
1914 
1915 #else
1916 
1917 int
1918 main(int argc, char **argv)
1919 {
1920  int rc;
1921 
1922  if ((rc = dacstransform_main(argc, argv, 1, NULL)) == 0)
1923  exit(0);
1924 
1925  exit(1);
1926 }
1927 
1928 #endif