static int list_actions(struct transaction_t *txn, int rights)
{
    int ret = 0, precond;
    time_t lastmod = compile_time;
    static char etag[21];
    unsigned level = 0;
    struct buf *body = &txn->resp_body.payload;
    const char *proto = NULL, *host = NULL;

    /* Check rights */
    if ((rights & DACL_READ) != DACL_READ) {
	/* DAV:need-privileges */
	txn->error.precond = DAV_NEED_PRIVS;
	txn->error.resource = txn->req_tgt.path;
	txn->error.rights = DACL_READ;
	return HTTP_NO_PRIVS;
    }

    sprintf(etag, "%ld", compile_time);

    /* Check any preconditions */
    precond = caldav_check_precond(txn, NULL, etag, lastmod);

    switch (precond) {
    case HTTP_OK:
    case HTTP_NOT_MODIFIED:
	/* Fill in ETag, Last-Modified, and Expires */
	txn->resp_body.etag = etag;
	txn->resp_body.lastmod = lastmod;
	txn->resp_body.maxage = 86400;  /* 24 hrs */
	txn->flags.cc |= CC_MAXAGE;

	if (precond != HTTP_NOT_MODIFIED) break;

    default:
	/* We failed a precondition - don't perform the request */
	ret = precond;
	goto done;
    }

    /* Setup for chunked response */
    txn->flags.te |= TE_CHUNKED;
    txn->resp_body.type = "text/html; charset=utf-8";

    /* Short-circuit for HEAD request */
    if (txn->meth == METH_HEAD) {
	response_header(HTTP_OK, txn);
	goto done;
    }

    /* Send HTML header */
    buf_reset(body);
    buf_printf_markup(body, level, HTML_DOCTYPE);
    buf_printf_markup(body, level++, "<html>");
    buf_printf_markup(body, level++, "<head>");
    buf_printf_markup(body, level, "<title>%s</title>", "Available Actions");
    buf_printf_markup(body, --level, "</head>");
    buf_printf_markup(body, level++, "<body>");
    buf_printf_markup(body, level, "<h2>%s</h2>", "Available Actions");
    buf_printf_markup(body, level++, "<ul>");

    /* Generate list of actions */
    http_proto_host(txn->req_hdrs, &proto, &host);
    buf_printf_markup(body, level,
		      "<li><a href=\"%s://%s%s?action=%s\">%s</a></li>",
		      proto, host, txn->req_tgt.path,
		      "list", "Available Calendars");
    buf_printf_markup(body, level,
		      "<li><a href=\"%s://%s%s?action=%s\">%s</a></li>",
		      proto, host, txn->req_tgt.path,
		      "freebusy", "Free/Busy Query");

    /* Finish HTML */
    buf_printf_markup(body, --level, "</ul>");
    buf_printf_markup(body, --level, "</body>");
    buf_printf_markup(body, --level, "</html>");
    write_body(HTTP_OK, txn, buf_cstring(body), buf_len(body));

    /* End of output */
    write_body(0, txn, NULL, 0);

  done:
    return ret;
}
static int list_actions(struct transaction_t *txn, int rights)
{
    int ret = 0, precond;
    time_t lastmod = compile_time;
    static char etag[21];
    unsigned level = 0;
    struct buf *body = &txn->resp_body.payload;
    const char *proto = NULL, *host = NULL;

     
    if ((rights & DACL_READ) != DACL_READ) {
	 
	txn->error.precond = DAV_NEED_PRIVS;
	txn->error.resource = txn->req_tgt.path;
	txn->error.rights = DACL_READ;
	return HTTP_NO_PRIVS;
    }

    sprintf(etag, "%ld", compile_time);

     
    precond = caldav_check_precond(txn, NULL, etag, lastmod);

    switch (precond) {
    case HTTP_OK:
    case HTTP_NOT_MODIFIED:
	 
	txn->resp_body.etag = etag;
	txn->resp_body.lastmod = lastmod;
	txn->resp_body.maxage = 86400;   
	txn->flags.cc |= CC_MAXAGE;

	if (precond != HTTP_NOT_MODIFIED) break;

    default:
	 
	ret = precond;
	goto done;
    }

     
    txn->flags.te |= TE_CHUNKED;
    txn->resp_body.type = "text/html; charset=utf-8";

     
    if (txn->meth == METH_HEAD) {
	response_header(HTTP_OK, txn);
	goto done;
    }

     
    buf_reset(body);
    buf_printf_markup(body, level, HTML_DOCTYPE);
    buf_printf_markup(body, level++, "<html>");
    buf_printf_markup(body, level++, "<head>");
    buf_printf_markup(body, level, "<title>%s</title>", "Available Actions");
    buf_printf_markup(body, --level, "</head>");
    buf_printf_markup(body, level++, "<body>");
    buf_printf_markup(body, level, "<h2>%s</h2>", "Available Actions");
    buf_printf_markup(body, level++, "<ul>");

     
    http_proto_host(txn->req_hdrs, &proto, &host);
    buf_printf_markup(body, level,
		      "<li><a href=\"%s://%s%s?action=%s\">%s</a></li>",
		      proto, host, txn->req_tgt.path,
		      "list", "Available Calendars");
    buf_printf_markup(body, level,
		      "<li><a href=\"%s://%s%s?action=%s\">%s</a></li>",
		      proto, host, txn->req_tgt.path,
		      "freebusy", "Free/Busy Query");

     
    buf_printf_markup(body, --level, "</ul>");
    buf_printf_markup(body, --level, "</body>");
    buf_printf_markup(body, --level, "</html>");
    write_body(HTTP_OK, txn, buf_cstring(body), buf_len(body));

     
    write_body(0, txn, NULL, 0);

  done:
    return ret;
}