diff options
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | main.c | 41 | ||||
-rw-r--r-- | template.h | 104 | ||||
-rw-r--r-- | templates/simple.c | 131 |
4 files changed, 142 insertions, 135 deletions
diff --git a/TODO b/TODO index 1470d43..a361d9c 100644 --- a/TODO +++ b/TODO @@ -6,6 +6,5 @@ add tests for timeutil | id:b6cd0665e5c376f52cf9702698330308aa722031 tests for xml.c | id:b9e91d022be6bdc70e7ab082743826370b713c72 improve man3 situation | id:bf918930f6361c30f288c42894cbf9b0541c3340 use errno instead of returning HTTP status codes in make_index | id:bfa35c48dcedbecd3a951eba243c4840178289ed -templating: unify single_entry and index_entry | id:e6dcb195d890bb0b5186716808d806101d81e4f6 sandboxing, at least chroot | id:f209aec893fe310c8276f64f6ff4a3208a2a4f28 document cgiutil, timeutil and stringutil | id:fd05b97115848a8b22231a477b8252373bd87264 diff --git a/main.c b/main.c index 8571bdc..dfbfd5f 100644 --- a/main.c +++ b/main.c @@ -208,34 +208,55 @@ int main(void) { // confirm status and page_type match assert(status == 200 || page_type == PAGE_TYPE_ERROR); + assert(page_type != PAGE_TYPE_ERROR || status != 200); + + // initial contents of data, changed in loop for PAGE_TYPE_INDEX + struct template_data data; + data.page_type = page_type; + data.status = status; + data.script_name = script_name; + data.path_info = path_info; + + // confirm that we have SCRIPT_NAME and PATH_INFO unless an error occurred + assert(data.page_type != PAGE_TYPE_ERROR || + (data.path_info != NULL && data.script_name != NULL)); + + // make sure that PAGE_TYPE_ENTRY will have an entry set in template_header + if(count > 0) { + data.entry = entries; + } else { + data.entry = NULL; + } + assert(page_type != PAGE_TYPE_ENTRY || data.entry != NULL); + // render response if(page_type == PAGE_TYPE_ERROR) { send_standard_headers(status, "text/html"); - template_header(); - template_error(status); - template_footer(); + template_header(data); + template_main(data); + template_footer(data); } else if(is_feed == FEED_TYPE_NONE) { // either PAGE_TYPE_INDEX or PAGE_TYPE_ENTRY send_standard_headers(200, "text/html"); - template_header(); + template_header(data); // confirm that PAGE_TYPE_ENTRY → count == 1 assert(page_type != PAGE_TYPE_ENTRY || count == 1); for(int i = 0; i < count; i++) { if(entries[i].text != NULL || entry_get_text(&entries[i]) != -1) { - if(page_type == PAGE_TYPE_INDEX) { - template_index_entry(entries[i]); - } else { - template_single_entry(entries[i]); - } + data.entry = &entries[i]; + assert(data.entry != NULL); + + template_main(data); + entry_unget_text(&entries[i]); } } - template_footer(); + template_footer(data); } else if(is_feed == FEED_TYPE_RSS) { blog_rss(script_name, entries, count); } else if(is_feed == FEED_TYPE_ATOM) { diff --git a/template.h b/template.h index 572f09f..043f59e 100644 --- a/template.h +++ b/template.h @@ -21,6 +21,28 @@ */ /*! + * @brief (Meta) data about the page being served + * + * `struct template_data` is used to pass information about + * the current page to the template. It is received as the + * single argument by all template functions. + * + * The following assumptions about its contents can be made: + * + * * `page_type == PAGE_TYPE_ENTRY` → `entry != NULL` + * * `page_type == PAGE_TYPE_ERROR` ⟷ `status != 200` + * * `page_type != PAGE_TYPE_ERROR` → `script_name != NULL && path_info != NULL` + * * `page_type == PAGE_TYPE_ERROR` → `entry == NULL` + */ +struct template_data { + enum page_type page_type; //! type of page to render + int status; //! HTTP status of the response + struct entry *entry; //! Pointer to entry if applicable, else `NULL` + char *script_name; //! value of `SCRIPT_NAME` environment variable + char *path_info; //! value of `PATH_INFO` environment variable +}; + +/*! * @brief Prints beginning of HTML source * * template_header() is expected to print out the common beginning of @@ -29,11 +51,16 @@ * first template function). * * Typically it will print the HTML `<head>` and the header part - * of the `<body>` element which is common for all pages. + * of the `<body>` element which is common for all pages. It may + * adjust some parts of it (like headings, title, navigations, …) + * depending on the `data` that is passed. * - * If you are using `xml.h`, this is a good place to call new_xml_context(). + * If `data.page_type == PAGE_TYPE_INDEX`, `data.entry` will point + * to the first entry or be `NULL` if there are no entries. + * + * @see struct template_data */ -void template_header(void); +void template_header(struct template_data data); /*! * @brief Prints end of HTML source @@ -44,54 +71,31 @@ void template_header(void); * Usually this involves printing a footer part of the web page and * closing the `<body>` and `<html>` elements. * - * If you are using `xml.h`, this is a good place to call del_xml_context(). - */ -void template_footer(void); - -/*! - * @brief Prints HTML snippet for a single entry on the index page - * - * template_index_entry() is called for every entry that is part of - * the index page. It should print out the repeating content structure - * filled with the respective values for each entry. - * - * It gets passed a fully constructed `struct entry` with its `text` - * field populated, i. e. entry_get_text() has been called - * - * This function is essentially like template_single_entry(), but: - * - * * It must make sure to output appropriate and correct HTML - * if called repeatedly - * * Should reflect that it's an index page, i. e. link to - * the entries' individual pages and maybe display less - * detail than single pages. - * - * @see template_index_entry - * @see struct entry - * @see entry_get_text + * If `data.page_type == PAGE_TYPE_INDEX`, `data.entry` will point + * to the last entry or be `NULL` if there are no entries. */ -void template_index_entry(struct entry entry); +void template_footer(struct template_data data); /*! - * @brief Prints HTML snippet for an entry's own page - * - * template_single_entry() is like template_index_entry() - * and receives a fully populated `struct entry` as well, - * but it's a single page of an entry, so it may display - * more details and link back to the index. - * - * @see struct entry - * @see entry_get_text - */ -void template_single_entry(struct entry entry); - -/*! - * @brief Prints HTML snippet for an error page's main entry - * - * This should print main part of the page informing the user - * about an error. It may display different messages depending - * on the type of error. - * - * @param code HTTP status code of the error + * @brief Prints HTML snippet for the main part of the page + * + * template_main() should print the main part of the HTML source + * which is located between template_header() and template_footer(). + * + * Depending on `data.page_type` the following applies: + * + * * For `PAGE_TYPE_ENTRY` template_main() is called once and + * should print the main part of a single entry page. + * * For `PAGE_TYPE_ERROR` template_main() is called once and + * should print the main part of a page informing the user + * about an occurred HTTP error (reflecting `data.status`). + * * For `PAGE_TYPE_INDEX` template_main() is called 0 to n + * times where n is the number of total entries. Each time + * it's called it should print a HTML snippet which is + * suitable as an index entry. Furthermore it should be + * valid HTML regardless how many times it has been called + * before and will be called afterwards. + * + * @see struct template_data */ -void template_error(int code); +void template_main(struct template_data data); diff --git a/templates/simple.c b/templates/simple.c index eeee6db..9c9f20f 100644 --- a/templates/simple.c +++ b/templates/simple.c @@ -18,12 +18,12 @@ void output_entry_time(struct xml_context *ctx, struct entry entry) { if(flocaltime(strtime, HTML_TIME_FORMAT_READABLE, MAX_TIMESTR_SIZE, &entry.time) > 0) { xml_open_tag_attrs(ctx, "time", 1, "datetime", strtime); - xml_raw(ctx, strtime); + xml_escaped(ctx, strtime); xml_close_tag(ctx, "time"); } } -void template_header(void) { +void template_header(struct template_data data) { new_xml_context(&ctx); ctx.warn = stderr; ctx.closing_slash = 0; @@ -43,7 +43,13 @@ void template_header(void) { #endif xml_open_tag(&ctx, "title"); - xml_raw(&ctx, BLOG_TITLE); + xml_escaped(&ctx, BLOG_TITLE); + if(data.page_type == PAGE_TYPE_ENTRY) { + xml_escaped(&ctx, ": "); + xml_escaped(&ctx, data.entry->title); + } else if(data.page_type == PAGE_TYPE_ERROR) { + xml_escaped(&ctx, ": error"); + } xml_close_tag(&ctx, "title"); xml_close_tag(&ctx, "head"); @@ -51,20 +57,22 @@ void template_header(void) { xml_open_tag(&ctx, "body"); xml_open_tag(&ctx, "header"); xml_open_tag(&ctx, "h1"); - xml_raw(&ctx, BLOG_TITLE); + if(data.page_type != PAGE_TYPE_INDEX && data.script_name != NULL) { + xml_open_tag_attrs(&ctx, "a", 1, "href", data.script_name); + } + xml_escaped(&ctx, BLOG_TITLE); xml_close_including(&ctx, "header"); xml_open_tag(&ctx, "main"); } -void template_footer(void) { +void template_footer(struct template_data data) { xml_close_tag(&ctx, "main"); xml_open_tag(&ctx, "footer"); - char *script_name = getenv("SCRIPT_NAME"); - char *rss_link = catn_alloc(2, script_name, "/rss.xml"); - char *atom_link = catn_alloc(2, script_name, "/atom.xml"); + char *rss_link = catn_alloc(2, data.script_name, "/rss.xml"); + char *atom_link = catn_alloc(2, data.script_name, "/atom.xml"); if(rss_link != NULL) { xml_open_tag_attrs(&ctx, "a", 1, "href", rss_link); @@ -89,77 +97,52 @@ void template_footer(void) { del_xml_context(&ctx); } -void template_single_entry(struct entry entry) { - xml_open_tag(&ctx, "article"); - - if(entry.text_size > 0) { - xml_open_tag_attrs(&ctx, "div", 1, "class", "content"); - xml_raw(&ctx, entry.text); - xml_close_tag(&ctx, "div"); - } - - char *script_name = getenv("SCRIPT_NAME"); - - xml_open_tag_attrs(&ctx, "div", 1, "class", "meta"); - xml_open_tag(&ctx, "ul"); - - // time of publishing - xml_open_tag(&ctx, "li"); - output_entry_time(&ctx, entry); - xml_close_tag(&ctx, "li"); - - // link back to index - xml_open_tag(&ctx, "li"); - xml_open_tag_attrs(&ctx, "a", 1, "href", script_name); - xml_raw(&ctx, "Back"); - xml_close_tag(&ctx, "a"); - xml_close_tag(&ctx, "li"); - - xml_close_including(&ctx, "article"); -} +void template_main(struct template_data data) { + if(data.page_type == PAGE_TYPE_ERROR) { + xml_open_tag_attrs(&ctx, "div", 1, "class", "error-page"); + xml_open_tag(&ctx, "h2"); + xml_escaped(&ctx, "An error occured while handling your request"); + xml_close_tag(&ctx, "h2"); + + xml_open_tag_attrs(&ctx, "div", 1, "class", "content"); + xml_open_tag(&ctx, "p"); + + if(data.status == 500) { + xml_escaped(&ctx, "Something is wrong with this application and/or its server (error 500)."); + } else if(data.status == 404) { + xml_escaped(&ctx, "What you requested doesn't exist (error 404)."); + } else { + xml_escaped(&ctx, "The error encoutered is: "); + xml_escaped(&ctx, http_status_line(data.status)); + } + + xml_close_tag(&ctx, "p"); + xml_close_tag(&ctx, "div"); + xml_close_tag(&ctx, "div"); + } else { -void template_index_entry(struct entry entry) { - // TODO time? - xml_open_tag_attrs(&ctx, "article", 1, "id", entry.title); - if(entry.text_size > 0) { - xml_open_tag_attrs(&ctx, "div", 1, "class", "content index"); - xml_raw(&ctx, entry.text); - xml_close_tag(&ctx, "div"); - } + xml_open_tag(&ctx, "article"); - xml_open_tag_attrs(&ctx, "div", 1, "class", "meta index"); - xml_open_tag(&ctx, "p"); - xml_open_tag_attrs(&ctx, "a", 1, "href", entry.link); - xml_raw(&ctx, "Permalink"); + xml_open_tag(&ctx, "h2"); + if(data.page_type == PAGE_TYPE_INDEX) { + xml_open_tag_attrs(&ctx, "a", 1, "href", data.entry->link); + } + xml_escaped(&ctx, data.entry->title); + xml_close_including(&ctx, "h2"); - xml_close_including(&ctx, "article"); -} + if(data.entry->text_size > 0) { + xml_open_tag_attrs(&ctx, "div", 1, "class", "content"); + xml_raw(&ctx, data.entry->text); + xml_close_tag(&ctx, "div"); + } -void template_error(int status) { - xml_open_tag_attrs(&ctx, "div", 1, "class", "error-page"); - xml_open_tag(&ctx, "h2"); - xml_raw(&ctx, "An error occured while handling your request"); - xml_close_tag(&ctx, "h2"); + xml_open_tag_attrs(&ctx, "div", 1, "class", "meta"); - xml_open_tag(&ctx, "p"); + // modification time + xml_open_tag_attrs(&ctx, "p", 1, "class", "mtime"); + output_entry_time(&ctx, *data.entry); + xml_close_tag(&ctx, "p"); - if(status == 500) { - xml_raw(&ctx, "Something is wrong with this application and/or its server (error 500)."); - } else if(status == 404) { - xml_raw(&ctx, "What you requested doesn't exist (error 404)."); - } else { - xml_raw(&ctx, "The error encoutered is: "); - xml_raw(&ctx, http_status_line(status)); + xml_close_including(&ctx, "article"); } - - xml_close_tag(&ctx, "p"); - - char *script_name = getenv("SCRIPT_NAME"); - - xml_open_tag(&ctx, "nav"); - xml_open_tag(&ctx, "p"); - xml_open_tag_attrs(&ctx, "a", 1, "href", script_name); - xml_raw(&ctx, "Back"); - - xml_close_including(&ctx, "div"); } |