2 * This file is part of DisOrder.
3 * Copyright (C) 2004-2008 Richard Kettlewell
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
42 #include "configuration.h"
48 #include "inputline.h"
75 static hash
*cgi_macros
;
77 /** @brief Parse of a template */
79 /** @brief Next element */
80 struct cgi_element
*next
;
82 /** @brief Element type */
84 #define ELEMENT_TEXT 0
85 #define ELEMENT_EXPANSION 1
87 /** @brief Line number at start of element */
90 /** @brief Plain text */
93 /** @brief Expansion name */
96 /** @brief Argument count */
99 /** @brief Argument values (NOT recursively expanded) */
103 #define RELIST(x) struct re *x, **x##_tail = &x
105 static int have_read_options
;
106 static struct kvp
*labels
;
107 static struct column
*columns
;
109 static void include_options(const char *name
);
110 static void cgi_expand_parsed(const char *name
,
111 struct cgi_element
*head
,
112 const struct cgi_expansion
*expansions
,
117 static void cgi_parse_get(void) {
120 if(!(q
= getenv("QUERY_STRING"))) fatal(0, "QUERY_STRING not set");
121 cgi_args
= kvp_urldecode(q
, strlen(q
));
124 static void cgi_input(char **ptrp
, size_t *np
) {
130 if(!(cl
= getenv("CONTENT_LENGTH"))) fatal(0, "CONTENT_LENGTH not set");
132 q
= xmalloc_noptr(n
+ 1);
134 r
= read(0, q
+ m
, n
- m
);
138 fatal(0, "unexpected end of file reading request body");
141 default: fatal(errno
, "error reading request body");
144 if(memchr(q
, 0, n
)) fatal(0, "null character in request body");
150 static int cgi_field_callback(const char *name
, const char *value
,
152 char *disposition
, *pname
, *pvalue
;
155 if(!strcmp(name
, "content-disposition")) {
156 if(mime_rfc2388_content_disposition(value
,
160 fatal(0, "error parsing Content-Disposition field");
161 if(!strcmp(disposition
, "form-data")
163 && !strcmp(pname
, "name")) {
165 fatal(0, "duplicate Content-Disposition field");
172 static int cgi_part_callback(const char *s
,
173 void attribute((unused
)) *u
) {
177 if(!(s
= mime_parse(s
, cgi_field_callback
, &name
)))
178 fatal(0, "error parsing part header");
179 if(!name
) fatal(0, "no name found");
180 k
= xmalloc(sizeof *k
);
188 static void cgi_parse_multipart(const char *boundary
) {
192 if(mime_multipart(q
, cgi_part_callback
, boundary
, 0))
193 fatal(0, "invalid multipart object");
196 static void cgi_parse_post(void) {
197 const char *ct
, *boundary
;
202 if(!(ct
= getenv("CONTENT_TYPE")))
203 ct
= "application/x-www-form-urlencoded";
204 if(mime_content_type(ct
, &type
, &k
))
205 fatal(0, "invalid content type '%s'", ct
);
206 if(!strcmp(type
, "application/x-www-form-urlencoded")) {
208 cgi_args
= kvp_urldecode(q
, n
);
211 if(!strcmp(type
, "multipart/form-data")) {
212 if(!(boundary
= kvp_get(k
, "boundary")))
213 fatal(0, "no boundary parameter found");
214 cgi_parse_multipart(boundary
);
217 fatal(0, "unrecognized content type '%s'", type
);
220 void cgi_parse(void) {
224 if(!(p
= getenv("REQUEST_METHOD"))) fatal(0, "REQUEST_METHOD not set");
225 if(!strcmp(p
, "GET"))
227 else if(!strcmp(p
, "POST"))
230 fatal(0, "unknown request method %s", p
);
231 for(k
= cgi_args
; k
; k
= k
->next
)
232 if(!utf8_valid(k
->name
, strlen(k
->name
))
233 || !utf8_valid(k
->value
, strlen(k
->value
)))
234 fatal(0, "invalid UTF-8 sequence in cgi argument");
237 const char *cgi_get(const char *name
) {
238 return kvp_get(cgi_args
, name
);
241 void cgi_output(cgi_sink
*output
, const char *fmt
, ...) {
247 n
= byte_vasprintf(&r
, fmt
, ap
);
249 fatal(errno
, "error calling byte_vasprintf");
251 r
= cgi_sgmlquote(r
, 0);
252 output
->sink
->write(output
->sink
, r
, strlen(r
));
256 void cgi_header(struct sink
*output
, const char *name
, const char *value
) {
257 sink_printf(output
, "%s: %s\r\n", name
, value
);
260 void cgi_body(struct sink
*output
) {
261 sink_printf(output
, "\r\n");
264 char *cgi_sgmlquote(const char *s
, int raw
) {
265 uint32_t *ucs
, *p
, c
;
270 if(!(ucs
= utf8_to_utf32(s
, strlen(s
), 0))) exit(EXIT_FAILURE
);
272 ucs
= xmalloc_noptr((strlen(s
) + 1) * sizeof(uint32_t));
273 for(n
= 0; s
[n
]; ++n
)
274 ucs
[n
] = (unsigned char)s
[n
];
279 /* estimate the length we'll need */
280 for(p
= ucs
; (c
= *p
); ++p
) {
283 if(c
> 127 || c
< 32) {
294 /* format the string */
295 b
= bp
= xmalloc_noptr(n
);
296 for(p
= ucs
; (c
= *p
); ++p
) {
299 if(*p
> 127 || *p
< 32) {
304 bp
+= sprintf(bp
, "&#%lu;", (unsigned long)c
);
314 void cgi_attr(struct sink
*output
, const char *name
, const char *value
) {
315 if(!value
[strspn(value
, "abcdefghijklmnopqrstuvwxyz"
316 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
318 sink_printf(output
, "%s=%s", name
, value
);
320 sink_printf(output
, "%s=\"%s\"", name
, cgi_sgmlquote(value
, 0));
323 void cgi_opentag(struct sink
*output
, const char *name
, ...) {
327 sink_printf(output
, "<%s", name
);
329 while((n
= va_arg(ap
, const char *))) {
330 sink_printf(output
, " ");
331 v
= va_arg(ap
, const char *);
333 cgi_attr(output
, n
, v
);
335 sink_printf(output
, n
);
337 sink_printf(output
, ">");
340 void cgi_closetag(struct sink
*output
, const char *name
) {
341 sink_printf(output
, "</%s>", name
);
344 static int template_open(const char *name
,
346 const char **filenamep
) {
351 dirs
[0] = pkgconfdir
;
352 dirs
[1] = pkgdatadir
;
354 if((fd
= open(name
, O_RDONLY
)) < 0) fatal(0, "cannot open %s", name
);
357 for(n
= 0; n
< config
->templates
.n
+ (int)(sizeof dirs
/ sizeof *dirs
); ++n
) {
358 byte_xasprintf(&fullpath
, "%s/%s%s",
359 n
< config
->templates
.n ? config
->templates
.s
[n
]
360 : dirs
[n
- config
->templates
.n
],
362 if((fd
= open(fullpath
, O_RDONLY
)) >= 0) break;
364 if(fd
< 0) error(0, "cannot find %s%s in template path", name
, ext
);
365 *filenamep
= fullpath
;
370 static int valid_template_name(const char *name
) {
371 if(strchr(name
, '/') || name
[0] == '.')
376 void cgi_expand(const char *template,
377 const struct cgi_expansion
*expansions
,
387 if(!valid_template_name(template))
388 fatal(0, "invalid template name '%s'", template);
389 if((fd
= template_open(template, ".html", &template)) < 0)
390 exitfn(EXIT_FAILURE
);
391 if(fstat(fd
, &sb
) < 0) fatal(errno
, "cannot stat %s", template);
393 b
= xmalloc_noptr(sb
.st_size
+ 1);
394 while(m
< sb
.st_size
) {
395 n
= read(fd
, b
+ m
, sb
.st_size
- m
);
397 else if(n
== 0) fatal(0, "unexpected EOF reading %s", template);
398 else if(errno
!= EINTR
) fatal(errno
, "error reading %s", template);
402 cgi_expand_string(template, b
, expansions
, nexpansions
, output
, u
);
405 /** @brief Return a linked list of the parse of @p template */
406 static struct cgi_element
*cgi_parse_string(const char *name
,
407 const char *template) {
408 int braces
, line
= 1, sline
;
412 struct cgi_element
*head
= 0, **tailp
= &head
, *e
;
415 if(*template != '@') {
418 /* Gather up text without any expansions in. */
419 while(*template && *template != '@') {
420 if(*template == '\n')
422 dynstr_append(&d
, *template++);
424 dynstr_terminate(&d
);
425 e
= xmalloc(sizeof *e
);
428 e
->type
= ELEMENT_TEXT
;
439 while(*template != '@') {
440 /* Skip whitespace */
441 while(isspace((unsigned char)*template))
444 if(*template == '{') {
447 while(*template && (*template != '}' || braces
> 0)) {
449 case '{': ++braces
; break;
450 case '}': --braces
; break;
451 case '\n': ++line
; break;
453 dynstr_append(&d
, *template++);
455 if(!*template) fatal(0, "%s:%d: unterminated expansion '%.*s'",
456 name
, sline
, (int)(template - p
), p
);
458 if(isspace((unsigned char)*template)) {
459 /* We have @{...}<WHITESPACE><SOMETHING> */
460 for(p
= template; isspace((unsigned char)*p
); ++p
)
462 /* Now we are looking at <SOMETHING>. If it's "{" then that
463 * must be the next argument. Otherwise we infer that this
464 * is really the end of the expansion. */
466 goto finished_expansion
;
469 /* unbracketed arg */
471 && *template != '@' && *template != '{' && *template != ':') {
472 if(*template == '\n') ++line
;
473 dynstr_append(&d
, *template++);
477 if(!*template) fatal(0, "%s:%d: unterminated expansion '%.*s'",
478 name
, sline
, (int)(template - p
), p
);
479 /* trailing whitespace is not significant in unquoted args */
480 while(d
.nvec
&& (isspace((unsigned char)d
.vec
[d
.nvec
- 1])))
483 dynstr_terminate(&d
);
484 vector_append(&v
, d
.vec
);
488 vector_terminate(&v
);
489 /* @@ terminates this file */
492 e
= xmalloc(sizeof *e
);
495 e
->type
= ELEMENT_EXPANSION
;
497 e
->nargs
= v
.nvec
- 1;
505 void cgi_expand_string(const char *name
,
506 const char *template,
507 const struct cgi_expansion
*expansions
,
511 cgi_expand_parsed(name
, cgi_parse_string(name
, template),
512 expansions
, nexpansions
, output
, u
);
515 /** @brief Expand a list of arguments in place */
516 static void cgi_expand_all_args(const char *name
,
518 const struct cgi_expansion
*expansions
,
526 cgi_sink parameter_output
;
528 for(n
= 0; n
< nargs
; ++n
) {
530 byte_xasprintf(&argname
, "<%s:%d arg #%d>", name
, line
,n
);
531 parameter_output
.quote
= 0;
532 parameter_output
.sink
= sink_dynstr(&d
);
533 cgi_expand_string(argname
, args
[n
],
534 expansions
, nexpansions
,
535 ¶meter_output
, u
);
536 dynstr_terminate(&d
);
542 /** @brief Substitute macro arguments in place */
543 static void cgi_substitute_args(const char *name
,
544 struct cgi_element
*head
,
545 const struct cgi_macro
*macro
,
547 struct cgi_element
*e
;
550 for(e
= head
; e
; e
= e
->next
) {
551 if(e
->type
!= ELEMENT_EXPANSION
)
553 /* See if this is an argument name */
554 for(n
= 0; n
< macro
->nargs
; ++n
)
555 if(!strcmp(e
->name
, macro
->args
[n
]))
557 if(n
< macro
->nargs
) {
560 fatal(0, "%s:%d: macro argument (%s) cannot take parameters",
561 name
, e
->line
, e
->name
);
562 /* Replace it with the argument text */
563 e
->type
= ELEMENT_TEXT
;
567 /* It's not a macro argument. We must recurse into its arguments to
568 * substitute the macro arguments. */
570 /* In order to do this we must parse it and our callers must expect the
571 * parsed form. We're not ready for this yet... */
575 static void cgi_expand_parsed(const char *name
,
576 struct cgi_element
*head
,
577 const struct cgi_expansion
*expansions
,
582 const struct cgi_macro
*macro
;
583 struct cgi_element
*e
, *macro_head
;
585 for(e
= head
; e
; e
= e
->next
) {
588 output
->sink
->write(output
->sink
, e
->text
, strlen(e
->text
));
590 case ELEMENT_EXPANSION
:
591 if((n
= table_find(expansions
,
592 offsetof(struct cgi_expansion
, name
),
593 sizeof (struct cgi_expansion
),
596 /* We found a built-in */
597 if(e
->nargs
< expansions
[n
].minargs
)
598 fatal(0, "%s:%d: insufficient arguments to @%s@ (min %d, got %d)",
599 name
, e
->line
, e
->name
, expansions
[n
].minargs
, e
->nargs
);
600 if(e
->nargs
> expansions
[n
].maxargs
)
601 fatal(0, "%s:%d: too many arguments to @%s@ (max %d, got %d)",
602 name
, e
->line
, e
->name
, expansions
[n
].maxargs
, e
->nargs
);
603 /* for ordinary expansions, recursively expand the arguments */
604 if(!(expansions
[n
].flags
& EXP_MAGIC
))
605 cgi_expand_all_args(name
, e
->line
, expansions
, nexpansions
, u
,
607 expansions
[n
].handler(e
->nargs
, e
->args
, output
, u
);
608 } else if(cgi_macros
&& (macro
= hash_find(cgi_macros
, e
->name
))) {
609 /* We found a macro */
610 if(e
->nargs
!= macro
->nargs
)
611 fatal(0, "%s:%d: wrong number of arguments to @%s@ (need %d, got %d)",
612 name
, e
->line
, e
->name
, macro
->nargs
, e
->nargs
);
613 /* Expand arguments */
614 cgi_expand_all_args(name
, e
->line
, expansions
, nexpansions
, u
,
616 /* Parse the macro value. Doing this every time isn't very efficient,
617 * but NB that we mess with the result of the parse, so for the time
618 * being we do need to do it. */
619 macro_head
= cgi_parse_string(e
->name
, macro
->value
);
620 /* Substitute in argument values */
621 cgi_substitute_args(name
, macro_head
, macro
, e
->args
);
622 /* Expand the result */
623 cgi_expand_parsed(e
->name
,
630 /* Totally undefined */
631 fatal(0, "%s:%d: unknown expansion '%s'", name
, e
->line
, e
->name
);
638 char *cgi_makeurl(const char *url
, ...) {
640 struct kvp
*kvp
, *k
, **kk
= &kvp
;
645 dynstr_append_string(&d
, url
);
647 while((n
= va_arg(ap
, const char *))) {
648 v
= va_arg(ap
, const char *);
649 *kk
= k
= xmalloc(sizeof *k
);
656 dynstr_append(&d
, '?');
657 dynstr_append_string(&d
, kvp_urlencode(kvp
, 0));
659 dynstr_terminate(&d
);
663 void cgi_set_option(const char *name
, const char *value
) {
664 struct kvp
*k
= xmalloc(sizeof *k
);
672 static void option_label(int attribute((unused
)) nvec
,
674 cgi_set_option(vec
[0], vec
[1]);
677 static void option_include(int attribute((unused
)) nvec
,
679 include_options(vec
[0]);
682 static void option_columns(int nvec
,
684 struct column
*c
= xmalloc(sizeof *c
);
688 c
->ncolumns
= nvec
- 1;
689 c
->columns
= &vec
[1];
693 static struct option
{
695 int minargs
, maxargs
;
696 void (*handler
)(int nvec
, char **vec
);
698 { "columns", 1, INT_MAX
, option_columns
},
699 { "include", 1, 1, option_include
},
700 { "label", 2, 2, option_label
},
703 struct read_options_state
{
708 static void read_options_error(const char *msg
,
710 struct read_options_state
*cs
= u
;
712 error(0, "%s:%d: %s", cs
->name
, cs
->line
, msg
);
715 static void include_options(const char *name
) {
720 struct read_options_state cs
;
722 if((fd
= template_open(name
, "", &cs
.name
)) < 0) return;
723 if(!(fp
= fdopen(fd
, "r"))) fatal(errno
, "error calling fdopen");
725 while(!inputline(cs
.name
, fp
, &buffer
, '\n')) {
727 if(!(vec
= split(buffer
, &n
, SPLIT_COMMENTS
|SPLIT_QUOTES
,
728 read_options_error
, &cs
)))
731 if((i
= TABLE_FIND(options
, struct option
, name
, vec
[0])) == -1) {
732 error(0, "%s:%d: unknown option '%s'", cs
.name
, cs
.line
, vec
[0]);
737 if(n
< options
[i
].minargs
) {
738 error(0, "%s:%d: too few arguments to '%s'", cs
.name
, cs
.line
, vec
[-1]);
741 if(n
> options
[i
].maxargs
) {
742 error(0, "%s:%d: too many arguments to '%s'", cs
.name
, cs
.line
, vec
[-1]);
745 options
[i
].handler(n
, vec
);
750 static void read_options(void) {
751 if(!have_read_options
) {
752 have_read_options
= 1;
753 include_options("options");
757 const char *cgi_label(const char *key
) {
761 if(!(label
= kvp_get(labels
, key
))) {
763 if(!strncmp(key
, "images.", 7)) {
764 static const char *url_static
;
765 /* images.X defaults to <url.static>X.png */
768 url_static
= cgi_label("url.static");
769 byte_xasprintf((char **)&label
, "%s%s.png", url_static
, key
+ 7);
770 } else if((label
= strchr(key
, '.')))
771 /* X.Y defaults to Y */
774 /* otherwise default to label name */
780 int cgi_label_exists(const char *key
) {
782 return kvp_get(labels
, key
) ?
1 : 0;
785 char **cgi_columns(const char *name
, int *ncolumns
) {
789 for(c
= columns
; c
&& strcmp(name
, c
->name
); c
= c
->next
)
793 *ncolumns
= c
->ncolumns
;
802 void cgi_define(const char *name
,
809 cgi_macros
= hash_new(sizeof(struct cgi_macro
));
813 hash_add(cgi_macros
, name
, &m
, HASH_INSERT_OR_REPLACE
);