Example #1
0
/*
 * This function is for uploading an ENTIRE calendar, not just one
 * component.  This would be for webcal:// 'publish' operations, not
 * for GroupDAV.
 */
void dav_put_bigics(void)
{
	wcsession *WCC = WC;
	char buf[1024];

	/*
	 * Tell the server that when we save a calendar event, we
	 * do *not* want the server to generate invitations. 
	 */
	serv_puts("ICAL sgi|0");
	serv_getln(buf, sizeof buf);

	serv_puts("ICAL putics");
	serv_getln(buf, sizeof buf);
	if (buf[0] != '4') {
		hprintf("HTTP/1.1 502 Bad Gateway\r\n");
		dav_common_headers();
		hprintf("Content-type: text/plain\r\n");
		begin_burst();
		wc_printf("%s\r\n", &buf[4]);
		end_burst();
		return;
	}

	serv_putbuf(WCC->upload);
	serv_printf("\n000");

	/* Report success and not much else. */
	hprintf("HTTP/1.1 204 No Content\r\n");
	syslog(LOG_DEBUG, "HTTP/1.1 204 No Content\r\n");
	dav_common_headers();
	begin_burst();
	end_burst();
}
Example #2
0
/*
 * modify an existing node
 */
void display_edit_node(void)
{
	WCTemplputParams SubTP;
	HashList *NodeConfig;
	const StrBuf *Index;
	void *vNode;
	const StrBuf *Tmpl;

	Index = sbstr("index");
	if (Index == NULL) {
		AppendImportantMessage(_("Invalid Parameter"), -1);
		url_do_template();
		return;
	}

	NodeConfig = load_netconf(NULL, &NoCtx);
	if (!GetHash(NodeConfig, ChrPtr(Index), StrLength(Index), &vNode) || 
	    (vNode == NULL)) {
		AppendImportantMessage(_("Invalid Parameter"), -1);
		url_do_template();
		DeleteHash(&NodeConfig);
		return;
	}
	StackContext(NULL, &SubTP, vNode, CTX_NODECONF, 0, NULL);
	{
		begin_burst();
		Tmpl = sbstr("template");
		output_headers(1, 0, 0, 0, 1, 0);
		DoTemplate(SKEY(Tmpl), NULL, &SubTP);
		end_burst();
	}
	UnStackContext(&SubTP);
	DeleteHash(&NodeConfig);
	
}
Example #3
0
/* If it's a "force 404" situation then display the error and bail. */
void do_404(void)
{
	hprintf("HTTP/1.1 404 Not found\r\n");
	hprintf("Content-Type: text/plain\r\n");
	wc_printf("Not found\r\n");
	end_burst();
}
Example #4
0
/*
 * Generic function to do an HTTP redirect.  Easy and fun.
 */
void http_redirect(const char *whichpage) {
	hprintf("HTTP/1.1 302 Moved Temporarily\n");
	hprintf("Location: %s\r\n", whichpage);
	hprintf("URI: %s\r\n", whichpage);
	hprintf("Content-type: text/html; charset=utf-8\r\n");
	stuff_to_cookie(0);
	begin_burst();
	wc_printf("<html><body>");
	wc_printf("Go <a href=\"%s\">here</A>.", whichpage);
	wc_printf("</body></html>\n");
	end_burst();
}
Example #5
0
/*
 * wrap up an HTTP session, closes tags, etc.
 *
 * print_standard_html_footer should be set to:
 * 0		- to transmit only,
 * nonzero	- to append the closing tags
 */
void wDumpContent(int print_standard_html_footer)
{
	if (print_standard_html_footer) {
		wc_printf("</div> <!-- end of 'content' div -->\n");
		do_template("trailing");
	}

	/* If we've been saving it all up for one big output burst,
	 * go ahead and do that now.
	 */
	end_burst();
}
Example #6
0
/*
 * Output a piece of content to the web browser using conformant HTTP and MIME semantics.
 *
 * If this function is called, it is expected that begin_burst() has already been called
 * and some sort of content has been fed into the buffer.  This function will transmit a
 * bunch of headers to the client.  end_burst() will add some headers of its own, and then
 * transmit the buffered content to the client.
 */
void http_transmit_thing(const char *content_type, int is_static)
{
	if (verbose)
		syslog(LOG_DEBUG, "http_transmit_thing(%s)%s", content_type, ((is_static > 0) ? " (static)" : ""));
	output_headers(0, 0, 0, 0, 0, is_static);

	hprintf("Content-type: %s\r\n"
		"Server: %s\r\n"
		"Connection: close\r\n",
		content_type,
		PACKAGE_STRING);

	end_burst();
}
Example #7
0
/*
 * The pathname is always going to be /groupdav/room_name/msg_num
 */
void dav_report(void) 
{
	char datestring[256];
	time_t now = time(NULL);

	http_datestring(datestring, sizeof datestring, now);
	const char *req = ChrPtr(WC->upload);

	syslog(LOG_DEBUG, "REPORT: \033[31m%s\033[0m", req);

	hprintf("HTTP/1.1 500 Internal Server Error\r\n");
	dav_common_headers();
	hprintf("Date: %s\r\n", datestring);
	hprintf("Content-Type: text/plain\r\n");
	wc_printf("An internal error has occurred at %s:%d.\r\n", __FILE__ , __LINE__ );
	end_burst();
	return;
}
Example #8
0
void display_graphics_upload(char *filename)
{
	StrBuf *Line;

	Line = NewStrBuf();
	serv_printf("UIMG 0||%s", filename);
	StrBuf_ServGetln(Line);
	if (GetServerStatusMsg(Line, NULL, 1, 2) != 2) {
		display_main_menu();
		return;
	}
	else
	{
		output_headers(1, 0, 0, 0, 1, 0);
		do_template("files_graphicsupload");
		end_burst();
	}
	FreeStrBuf(&Line);
}
Example #9
0
/*
 * Entry point for WebCit transaction
 */
void session_loop(void)
{
	int xhttp;
	StrBuf *Buf;
	
	/*
	 * We stuff these with the values coming from the client cookies,
	 * so we can use them to reconnect a timed out session if we have to.
	 */
	wcsession *WCC;
      
	WCC= WC;
	WCC->upload_length = 0;
	WCC->upload = NULL;
	WCC->Hdr->nWildfireHeaders = 0;

	if (WCC->Hdr->HR.ContentLength > 0) {
		if (ReadPostData() < 0) {
			return;
		}
	}

	Buf = NewStrBuf();
	WCC->trailing_javascript = NewStrBuf();

	/* Convert base64-encoded URL's back to plain text */
	if (!strncmp(ChrPtr(WCC->Hdr->this_page), "/B64", 4)) {
		StrBufCutLeft(WCC->Hdr->this_page, 4);
		StrBufDecodeBase64(WCC->Hdr->this_page);
		http_redirect(ChrPtr(WCC->Hdr->this_page));
		goto SKIP_ALL_THIS_CRAP;
	}

	/* If there are variables in the URL, we must grab them now */
	if (WCC->Hdr->PlainArgs != NULL)
		ParseURLParams(WCC->Hdr->PlainArgs);

	/* If the client sent a nonce that is incorrect, kill the request. */
	if (havebstr("nonce")) {
		if (verbose)
			syslog(LOG_DEBUG, "Comparing supplied nonce %s to session nonce %d", 
			       bstr("nonce"), WCC->nonce
				);
		if (ibstr("nonce") != WCC->nonce) {
			syslog(LOG_INFO, "Ignoring request with mismatched nonce.");
			hprintf("HTTP/1.1 404 Security check failed\r\n");
			hprintf("Content-Type: text/plain\r\n");
			begin_burst();
			wc_printf("Security check failed.\r\n");
			end_burst();
			goto SKIP_ALL_THIS_CRAP;
		}
	}

	/*
	 * If we're not connected to a Citadel server, try to hook up the connection now.
	 */
	if (!WCC->connected) {
		if (GetConnected()) {
			hprintf("HTTP/1.1 503 Service Unavailable\r\n");
			hprintf("Content-Type: text/html\r\n");
			begin_burst();
			wc_printf("<html><head><title>503 Service Unavailable</title></head><body>\n");
			wc_printf(_("This program was unable to connect or stay "
				"connected to the Citadel server.  Please report "
				"this problem to your system administrator.")
			);
			wc_printf("<br>");
			wc_printf("<a href=\"http://www.citadel.org/doku.php/"
				"faq:generalquestions:webcit_unable_to_connect\">%s</a>",
				_("Read More...")
			);
			wc_printf("</body></html>\n");
			end_burst();
			goto SKIP_ALL_THIS_CRAP;
		}
	}

	/*
	 * If we're not logged in, but we have authentication data (either from
	 * a cookie or from http-auth), try logging in to Citadel using that.
	 */
	if (	(!WCC->logged_in)
		&& (StrLength(WCC->Hdr->c_username) > 0)
		&& (StrLength(WCC->Hdr->c_password) > 0)
	) {
		long Status;

		FlushStrBuf(Buf);
		serv_printf("USER %s", ChrPtr(WCC->Hdr->c_username));
		StrBuf_ServGetln(Buf);
		if (GetServerStatus(Buf, &Status) == 3) {
			serv_printf("PASS %s", ChrPtr(WCC->Hdr->c_password));
			StrBuf_ServGetln(Buf);
			if (GetServerStatus(Buf, NULL) == 2) {
				become_logged_in(WCC->Hdr->c_username,
						 WCC->Hdr->c_password, Buf);
			} else {
				/* Should only display when password is wrong */
				WCC->ImportantMsg = NewStrBufPlain(ChrPtr(Buf) + 4, StrLength(Buf) - 4);
				authorization_required();
				FreeStrBuf(&Buf);
				goto SKIP_ALL_THIS_CRAP;
			}
		}
		else if (Status == 541) {
			WCC->logged_in = 1;
		}
	}

	xhttp = (WCC->Hdr->HR.eReqType != eGET) &&
		(WCC->Hdr->HR.eReqType != ePOST) &&
		(WCC->Hdr->HR.eReqType != eHEAD);

	/*
	 * If a 'go' (or 'gotofirst') parameter has been specified, attempt to goto that room
	 * prior to doing anything else.
	 */
	if (havebstr("go")) {
		int ret;
		if (verbose)
			syslog(LOG_DEBUG, "Explicit room selection: %s", bstr("go"));
		ret = gotoroom(sbstr("go"));	/* do quietly to avoid session output! */
		if ((ret/100) != 2) {
			if (verbose)
				syslog(LOG_DEBUG, "Unable to change to [%s]; Reason: %d", bstr("go"), ret);
		}
	}
	else if (havebstr("gotofirst")) {
		int ret;
		if (verbose)
			syslog(LOG_DEBUG, "Explicit room selection: %s", bstr("gotofirst"));
		ret = gotoroom(sbstr("gotofirst"));	/* do quietly to avoid session output! */
		if ((ret/100) != 2) {
			syslog(LOG_INFO, "Unable to change to [%s]; Reason: %d", bstr("gotofirst"), ret);
		}
	}

	/*
	 * If we aren't in any room yet, but we have cookie data telling us where we're
	 * supposed to be, and 'go' was not specified, then go there.
	 */
	else if ( (StrLength(WCC->CurRoom.name) == 0) && ( (StrLength(WCC->Hdr->c_roomname) > 0) )) {
		int ret;

		if (verbose)
			syslog(LOG_DEBUG, "We are in '%s' but cookie indicates '%s', going there...",
			       ChrPtr(WCC->CurRoom.name),
			       ChrPtr(WCC->Hdr->c_roomname)
		);
		ret = gotoroom(WCC->Hdr->c_roomname);	/* do quietly to avoid session output! */
		if ((ret/100) != 2) {
			if (verbose)
				syslog(LOG_DEBUG, "COOKIEGOTO: Unable to change to [%s]; Reason: %d",
				       ChrPtr(WCC->Hdr->c_roomname), ret);
		}
	}

	if (WCC->Hdr->HR.Handler != NULL) {
		if (	(!WCC->logged_in)
			&& ((WCC->Hdr->HR.Handler->Flags & ANONYMOUS) == 0)
			&& (WCC->serv_info != NULL)
			&& (WCC->serv_info->serv_supports_guest == 0)
		) {
			display_login();
		}
		else {
			if ((WCC->Hdr->HR.Handler->Flags & AJAX) != 0) {
				begin_ajax_response();
			}
			WCC->Hdr->HR.Handler->F();
			if ((WCC->Hdr->HR.Handler->Flags & AJAX) != 0) {
				end_ajax_response();
			}
		}
	}
	/* When all else fails, display the default landing page or a main menu. */
	else {
		/* 
		 * ordinary browser users get a nice login screen, DAV etc. requsets
		 * are given a 401 so they can handle it appropriate.
		 */
		if (!WCC->logged_in)  {
			if (xhttp) {
				authorization_required();
			}
			else {
				display_default_landing_page();
			}
		}
		/*
		 * Toplevel dav requests? or just a flat browser request? 
		 */
		else {
			if (xhttp) {
				dav_main();
			}
			else {
				display_main_menu();
			}
		}
	}

SKIP_ALL_THIS_CRAP:
	FreeStrBuf(&Buf);
	fflush(stdout);
}
Example #10
0
/*
 * The pathname is always going to take one of two formats:
 * [/groupdav/]room_name/euid	(GroupDAV)
 * [/groupdav/]room_name		(webcal)
 */
void dav_put(void) 
{
	wcsession *WCC = WC;
	StrBuf *dav_roomname;
	StrBuf *dav_uid;
	long new_msgnum = (-2L);
	long old_msgnum = (-1L);
	char buf[SIZ];
	int n = 0;

	if (StrBufNum_tokens(WCC->Hdr->HR.ReqLine, '/') < 2) {
		hprintf("HTTP/1.1 404 not found\r\n");
		dav_common_headers();
		hprintf("Content-Type: text/plain\r\n");
		begin_burst();
		wc_printf("The object you requested was not found.\r\n");
		end_burst();
		return;
	}

	dav_roomname = NewStrBuf();;
	dav_uid = NewStrBuf();;
	StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
	StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
	if ((!strcasecmp(ChrPtr(dav_uid), "ics")) || 
	    (!strcasecmp(ChrPtr(dav_uid), "calendar.ics"))) {
		FlushStrBuf(dav_uid);
	}

	/* Go to the correct room. */
	if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) {
		gotoroom(dav_roomname);
	}
	if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) {
		hprintf("HTTP/1.1 404 not found\r\n");
		dav_common_headers();
		hprintf("Content-Type: text/plain\r\n");
		begin_burst();
		wc_printf("There is no folder called \"%s\" on this server.\r\n",
			ChrPtr(dav_roomname));
		end_burst();
		FreeStrBuf(&dav_roomname);
		FreeStrBuf(&dav_uid);		
		return;
	}

	/*
	 * If an HTTP If-Match: header is present, the client is attempting
	 * to replace an existing item.  We have to check to see if the
	 * message number associated with the supplied uid matches what the
	 * client is expecting.  If not, the server probably contains a newer
	 * version, so we fail...
	 */
	if (StrLength(WCC->Hdr->HR.dav_ifmatch) > 0) {
		syslog(LOG_DEBUG, "dav_ifmatch: %s\n", ChrPtr(WCC->Hdr->HR.dav_ifmatch));
		old_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
		syslog(LOG_DEBUG, "old_msgnum:  %ld\n", old_msgnum);
		if (StrTol(WCC->Hdr->HR.dav_ifmatch) != old_msgnum) {
			hprintf("HTTP/1.1 412 Precondition Failed\r\n");
			syslog(LOG_INFO, "HTTP/1.1 412 Precondition Failed (ifmatch=%ld, old_msgnum=%ld)\r\n",
				StrTol(WCC->Hdr->HR.dav_ifmatch), old_msgnum);
			dav_common_headers();
			
			end_burst();
			FreeStrBuf(&dav_roomname);
			FreeStrBuf(&dav_uid);
			return;
		}
	}

	/** PUT on the collection itself uploads an ICS of the entire collection.
	 */
	if (StrLength(dav_uid) == 0) {
		dav_put_bigics();
		FreeStrBuf(&dav_roomname);
		FreeStrBuf(&dav_uid);
		return;
	}

	/*
	 * We are cleared for upload!  We use the new calling syntax for ENT0
	 * which allows a confirmation to be sent back to us.  That's how we
	 * extract the message ID.
	 */
	serv_puts("ENT0 1|||4|||1|");
	serv_getln(buf, sizeof buf);
	if (buf[0] != '8') {
		hprintf("HTTP/1.1 502 Bad Gateway\r\n");
		dav_common_headers();
		hprintf("Content-type: text/plain\r\n");
		begin_burst();
		wc_printf("%s\r\n", &buf[4]);
		end_burst();
		return;
	}

	/* Send the content to the Citadel server */
	//serv_printf("Content-type: %s\n\n", WCC->upload_content_type);
	serv_putbuf(WCC->upload);
	serv_puts("\n000");

	/* Fetch the reply from the Citadel server */
	n = 0;
	FlushStrBuf(dav_uid);
	while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
		switch(n++) {
		case 0: 
			new_msgnum = atol(buf);
			break;
		case 1:	
			syslog(LOG_DEBUG, "new_msgnum=%ld (%s)\n", new_msgnum, buf);
			break;
		case 2: 
			StrBufAppendBufPlain(dav_uid, buf, -1, 0);
			break;
		default:
			break;
		}
	}

	/* Tell the client what happened. */

	/* Citadel failed in some way? */
	if (new_msgnum < 0L) {
		hprintf("HTTP/1.1 502 Bad Gateway\r\n");
		dav_common_headers();
		hprintf("Content-type: text/plain\r\n");
		begin_burst();
		wc_printf("new_msgnum is %ld\r\n"
			"\r\n", new_msgnum);
		end_burst();
		FreeStrBuf(&dav_roomname);
		FreeStrBuf(&dav_uid);
		return;
	}

	/* We created this item for the first time. */
	if (old_msgnum < 0L) {
	        char escaped_uid[1024];
		hprintf("HTTP/1.1 201 Created\r\n");
		syslog(LOG_DEBUG, "HTTP/1.1 201 Created\r\n");
		dav_common_headers();
		hprintf("etag: \"%ld\"\r\n", new_msgnum);
		hprintf("Location: ");
		dav_identify_hosthdr();
		hprintf("/groupdav/");/* TODO */
		hurlescputs(ChrPtr(dav_roomname));
	        euid_escapize(escaped_uid, ChrPtr(dav_uid));
	        hprintf("/%s\r\n", escaped_uid);
		end_burst();
		FreeStrBuf(&dav_roomname);
		FreeStrBuf(&dav_uid);
		return;
	}

	/* We modified an existing item. */
	hprintf("HTTP/1.1 204 No Content\r\n");
	syslog(LOG_DEBUG, "HTTP/1.1 204 No Content\r\n");
	dav_common_headers();
	hprintf("Etag: \"%ld\"\r\n", new_msgnum);
	/* The item we replaced has probably already been deleted by
	 * the Citadel server, but we'll do this anyway, just in case.
	 */
	serv_printf("DELE %ld", old_msgnum);
	serv_getln(buf, sizeof buf);
	begin_burst();
	end_burst();
	FreeStrBuf(&dav_roomname);
	FreeStrBuf(&dav_uid);
	return;
}
Example #11
0
/*
 * The pathname is always going to be /groupdav/room_name/euid
 */
void dav_delete(void) 
{
	wcsession *WCC = WC;
	char dav_uid[SIZ];
	long dav_msgnum = (-1);
	char buf[SIZ];
	int n = 0;
	StrBuf *dav_roomname = NewStrBuf();
	
	/* Now extract the message euid */
	n = StrBufNum_tokens(WCC->Hdr->HR.ReqLine, '/');
	extract_token(dav_uid, ChrPtr(WCC->Hdr->HR.ReqLine), n-1, '/', sizeof dav_uid);
	StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');

	///* What's left is the room name.  Remove trailing slashes. */
	//len = StrLength(WCC->Hdr->HR.ReqLine);
	//if ((len > 0) && (ChrPtr(WCC->Hdr->HR.ReqLinee)[len-1] == '/')) {
	//	StrBufCutRight(WCC->Hdr->HR.ReqLine, 1);
	//}
	//StrBufCutLeft(WCC->Hdr->HR.ReqLine, 1);

	/* Go to the correct room. */
	if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) {
		gotoroom(dav_roomname);
	}
	if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) {
		hprintf("HTTP/1.1 404 not found\r\n");
		dav_common_headers();
		hprintf("Content-Length: 0\r\n\r\n");
		begin_burst();
		end_burst();
		FreeStrBuf(&dav_roomname);
		return;
	}

	dav_msgnum = locate_message_by_uid(dav_uid);

	/*
	 * If no item exists with the requested uid ... simple error.
	 */
	if (dav_msgnum < 0L) {
		hprintf("HTTP/1.1 404 Not Found\r\n");
		dav_common_headers();
		hprintf("Content-Length: 0\r\n\r\n");
		begin_burst();
		end_burst();
		FreeStrBuf(&dav_roomname);
		return;
	}

	/*
	 * It's there ... check the ETag and make sure it matches
	 * the message number.
	 */
	if (StrLength(WCC->Hdr->HR.dav_ifmatch) > 0) {
		if (StrTol(WCC->Hdr->HR.dav_ifmatch) != dav_msgnum) {
			hprintf("HTTP/1.1 412 Precondition Failed\r\n");
			dav_common_headers();
			hprintf("Content-Length: 0\r\n\r\n");
			begin_burst();
			end_burst();
			FreeStrBuf(&dav_roomname);
			return;
		}
	}

	/*
	 * Ok, attempt to delete the item.
	 */
	serv_printf("DELE %ld", dav_msgnum);
	serv_getln(buf, sizeof buf);
	if (buf[0] == '2') {
		hprintf("HTTP/1.1 204 No Content\r\n");	/* success */
		dav_common_headers();
		hprintf("Content-Length: 0\r\n\r\n");
		begin_burst();
		end_burst();
	}
	else {
		hprintf("HTTP/1.1 403 Forbidden\r\n");	/* access denied */
		dav_common_headers();
		hprintf("Content-Length: 0\r\n\r\n");
		begin_burst();
		end_burst();
	}
	FreeStrBuf(&dav_roomname);
	return;
}
Example #12
0
/*
 * The pathname is always going to be /groupdav/room_name/msg_num
 */
void dav_options(void)
{
	wcsession *WCC = WC;
	StrBuf *dav_roomname;
	StrBuf *dav_uid;
	long dav_msgnum = (-1);
	char datestring[256];
	time_t now;

	now = time(NULL);
	http_datestring(datestring, sizeof datestring, now);

	dav_roomname = NewStrBuf();
	dav_uid = NewStrBuf();
	StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
	StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');

	syslog(LOG_DEBUG, "\033[35m%s (logged_in=%d)\033[0m", ChrPtr(WCC->Hdr->HR.ReqLine), WC->logged_in);
	/*
	 * If the room name is blank, the client is doing an OPTIONS on the root.
	 */
	if (StrLength(dav_roomname) == 0) {
		syslog(LOG_DEBUG, "\033[36mOPTIONS requested for root\033[0m");
		hprintf("HTTP/1.1 200 OK\r\n");
		dav_common_headers();
		hprintf("Date: %s\r\n", datestring);
		hprintf("DAV: 1\r\n");
		hprintf("Allow: OPTIONS, PROPFIND\r\n");
		hprintf("\r\n");
		begin_burst();
		end_burst();
		FreeStrBuf(&dav_roomname);
		FreeStrBuf(&dav_uid);
		return;
	}

	/* Go to the correct room. */
	if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) {
		gotoroom(dav_roomname);
	}

	if (strcasecmp(ChrPtr(WC->CurRoom.name), ChrPtr(dav_roomname))) {
		syslog(LOG_DEBUG, "\033[36mOPTIONS requested for invalid item\033[0m");
		hprintf("HTTP/1.1 404 not found\r\n");
		dav_common_headers();
		hprintf("Date: %s\r\n", datestring);
		hprintf(
			"Content-Type: text/plain\r\n");
		begin_burst();
		wc_printf(
			"There is no folder called \"%s\" on this server.\r\n",
			ChrPtr(dav_roomname)
		);
		end_burst();
		FreeStrBuf(&dav_roomname);
		FreeStrBuf(&dav_uid);
		return;
	}

	/* If dav_uid is non-empty, client is requesting an OPTIONS on
	 * a specific item in the room.
	 */
	if (StrLength(dav_uid) != 0) {
		syslog(LOG_DEBUG, "\033[36mOPTIONS requested for specific item\033[0m");
		dav_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
		if (dav_msgnum < 0) {
			hprintf("HTTP/1.1 404 not found\r\n");
			dav_common_headers();
			hprintf("Content-Type: text/plain\r\n");
			begin_burst();
			wc_printf(
				"Object \"%s\" was not found in the \"%s\" folder.\r\n",
				ChrPtr(dav_uid),
				ChrPtr(dav_roomname)
			);
			FreeStrBuf(&dav_roomname);
			FreeStrBuf(&dav_uid);
			end_burst();return;
		}

		hprintf("HTTP/1.1 200 OK\r\n");
		dav_common_headers();
		hprintf("Date: %s\r\n", datestring);
		hprintf("DAV: 1\r\n");
		hprintf("Allow: OPTIONS, PROPFIND, GET, PUT, DELETE\r\n");
		
		begin_burst();
		end_burst();
		FreeStrBuf(&dav_roomname);
		FreeStrBuf(&dav_uid);
		return;
	}

	FreeStrBuf(&dav_roomname);
	FreeStrBuf(&dav_uid);

	/*
	 * We got to this point, which means that the client is requesting
	 * an OPTIONS on the room itself.
	 */
	syslog(LOG_DEBUG, "\033[36mOPTIONS requested for room '%s' (%slogged in)\033[0m",
		ChrPtr(WC->CurRoom.name),
		((WC->logged_in) ? "" : "not ")
	);
	hprintf("HTTP/1.1 200 OK\r\n");
	dav_common_headers();
	hprintf("Date: %s\r\n", datestring);

	/*
	 * Offer CalDAV (RFC 4791) if this is a calendar room
	 */
	if ( (WC->CurRoom.view == VIEW_CALENDAR) || (WC->CurRoom.view == VIEW_CALBRIEF) ) {
		hprintf("DAV: 1, calendar-access\r\n");
		syslog(LOG_DEBUG, "\033[36mDAV: 1, calendar-access\033[0m");
	}
	else {
		hprintf("DAV: 1\r\n");
		syslog(LOG_DEBUG, "\033[36mDAV: 1\033[0m");
	}

	hprintf("Allow: OPTIONS, PROPFIND, GET, PUT, REPORT\r\n");
	begin_burst();
	end_burst();
}