Ejemplo n.º 1
0
/*
 * Authorization required page (sends a 401, causing the browser to request login credentials)
 */
void authorization_required(void)
{
	wcsession *WCC = WC;
	const char *message = "";

	hprintf("HTTP/1.1 401 Authorization Required\r\n");
	hprintf(
		"Server: %s / %s\r\n"
		"Connection: close\r\n",
		PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software)
	);
	hprintf("WWW-Authenticate: Basic realm=\"%s\"\r\n", ChrPtr(WC->serv_info->serv_humannode));

	/* if this is a false cookie authentication, remove it to avoid endless loops. */
	if (StrLength(WCC->Hdr->HR.RawCookie) > 0)
		stuff_to_cookie(1);

	hprintf("Content-Type: text/html\r\n");
	begin_burst();
	wc_printf("<h1>");
	wc_printf(_("Authorization Required"));
	wc_printf("</h1>\r\n");

	if (WCC->ImportantMsg != NULL) {
		message = ChrPtr(WCC->ImportantMsg);
	}

	wc_printf(
		_("The resource you requested requires a valid username and password. "
		"You could not be logged in: %s\n"),
		message
	);
	wDumpContent(0);
}
Ejemplo n.º 2
0
/*
 * Embed the room banner
 *
 * got			The information returned from a GOTO server command
 * navbar_style 	Determines which navigation buttons to display
 */
void tmplput_roombanner(StrBuf *Target, WCTemplputParams *TP)
{
	wcsession *WCC = WC;
	
	/* Refresh current room states.  Doesn't work? gotoroom(NULL); */

	wc_printf("<div id=\"banner\">\n");

	/* The browser needs some information for its own use */
	wc_printf("<script type=\"text/javascript\">	\n"
		  "	room_is_trash = %d;		\n"
		  "</script>\n",
		  ((WC->CurRoom.RAFlags & UA_ISTRASH) != 0)
	);

	/*
	 * If the user happens to select the "make this my start page" link,
	 * we want it to remember the URL as a "/dotskip" one instead of
	 * a "skip" or "gotonext" or something like that.
	 */
	if (WCC->Hdr->this_page == NULL) {
		WCC->Hdr->this_page = NewStrBuf();
	}
	StrBufPrintf(WCC->Hdr->this_page, "dotskip?room=%s", ChrPtr(WC->CurRoom.name));

	do_template("roombanner");

	do_template("navbar");
	wc_printf("</div>\n");
}
Ejemplo n.º 3
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();
}
Ejemplo n.º 4
0
/*
 * Save a URL destination so we can go to it later
 */
void push_destination(void) {
	wcsession *WCC = WC;

	if (!WCC) {
		wc_printf("no session");
		return;
	}

	FreeStrBuf(&WCC->PushedDestination);
	WCC->PushedDestination = NewStrBufDup(SBSTR("url"));
	if (verbose)
		syslog(LOG_DEBUG, "Push: %s", ChrPtr(WCC->PushedDestination));
	wc_printf("OK");
}
Ejemplo n.º 5
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();
}
Ejemplo n.º 6
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();
}
Ejemplo n.º 7
0
/*
 * Output a string to the client as a CDATA block
 */
void cdataout(char *rawdata)
{
	char *ptr = rawdata;
	wc_printf("<![CDATA[");

	while ((ptr != NULL) && (ptr[0] != 0))
	{
		if (!strncmp(ptr, "]]>", 3)) {
			wc_printf("]]]]><![CDATA[>");
			++ptr; ++ptr; ++ptr;
		}
		else {
			wc_printf("%c", ptr[0]);
			++ptr;
		}
	}

	wc_printf("]]>");
}
Ejemplo n.º 8
0
/*
 * Helper function for the asynchronous check to see if we need
 * to open the instant messenger window.
 */
void seconds_since_last_gexp(void)
{
	char buf[256];

	if ( (time(NULL) - WC->last_pager_check) < 30) {
		wc_printf("NO\n");
	}
	else {
		memset(buf, 0, 5);
		serv_puts("NOOP");
		serv_getln(buf, sizeof buf);
		if (buf[3] == '*') {
			wc_printf("YES");
		}
		else {
			wc_printf("NO");
		}
	}
}
Ejemplo n.º 9
0
/*
 * wholist for chat
 */
void chat_rwho(void) {
	char buf[1024];

	serv_puts("RCHT rwho");
	serv_getln(buf, sizeof buf);
	if (buf[0] == '1') {
		while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
			if (!strcasecmp(buf, ChrPtr(WC->wc_fullname))) {
				wc_printf("<span class=\"chat_myname_class\">");
			}
			else {
				wc_printf("<span class=\"chat_notmyname_class\">");
			}
			wc_printf("<img src=\"static/webcit_icons/essen/16x16/chat.png\">");
			escputs(buf);
			wc_printf("</span><br>\n");
		}
	}
}
Ejemplo n.º 10
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();
}
Ejemplo n.º 11
0
/*
 * Display a specific page from a wiki room
 *
 * "rev" may be set to an empty string to display the current version.
 * "do_revert" may be set to nonzero to perform a reversion to the specified version.
 */
void display_wiki_page_backend(char *pagename, char *rev, int do_revert)
{
	wcsession *WCC = WC;
	const StrBuf *Mime;
	long msgnum = (-1L);
	char buf[256];

	if ((WCC->CurRoom.view != VIEW_WIKI) &&
	    (WCC->CurRoom.view != VIEW_WIKIMD)) {
		wc_printf(_("'%s' is not a Wiki room."), ChrPtr(WCC->CurRoom.name) );
		return;
	}

	if (IsEmptyStr(pagename)) {
		strcpy(pagename, "home");
	}

	str_wiki_index(pagename);	/* convert index name to lowercase and numeric only */

	if ((rev != NULL) && (strlen(rev) > 0)) {
		/* read an older revision */
		serv_printf("WIKI rev|%s|%s|%s", pagename, rev, (do_revert ? "revert" : "fetch") );
		serv_getln(buf, sizeof buf);
		if (buf[0] == '2') {
			msgnum = extract_long(&buf[4], 0);
		}
	}
	else {
		/* read the current revision */
		msgnum = locate_message_by_uid(pagename);
	}

	if (msgnum >= 0L) {
		read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
		return;
	}

	wc_printf("<br><br>"
		"<div align=\"center\">"
		"<table border=\"0\" bgcolor=\"#ffffff\" cellpadding=\"10\">"
		"<tr><td align=\"center\">"
	);
	wc_printf("<br><b>");
	wc_printf(_("There is no page called '%s' here."), pagename);
	wc_printf("</b><br><br>");
	wc_printf(_("Select the 'Edit this page' link in the room banner "
		"if you would like to create this page."));
	wc_printf("<br><br>");
	wc_printf("</td></tr></table></div>\n");
}
Ejemplo n.º 12
0
int notes_GetParamsGetServerCall(SharedMessageStatus *Stat, 
				 void **ViewSpecific, 
				 long oper, 
				 char *cmd, 
				 long len,
				 char *filter,
				 long flen)
{
	strcpy(cmd, "MSGS ALL");
	Stat->maxmsgs = 32767;
	wc_printf("<div id=\"new_notes_here\"></div>\n");
	return 200;

}
Ejemplo n.º 13
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;
}
Ejemplo n.º 14
0
/*
 * Convenience functions to display a page containing only a string
 *
 * titlebarcolor	color of the titlebar of the frame
 * titlebarmsg		text to display in the title bar
 * messagetext		body of the box
 */
void convenience_page(const char *titlebarcolor, const char *titlebarmsg, const char *messagetext)
{
	hprintf("HTTP/1.1 200 OK\n");
	output_headers(1, 1, 1, 0, 0, 0);
	wc_printf("<div id=\"room_banner_override\">\n");
	wc_printf("<table width=100%% border=0 bgcolor=\"#%s\"><tr><td>", titlebarcolor);
	wc_printf("<span class=\"titlebar\">%s</span>\n", titlebarmsg);
	wc_printf("</td></tr></table>\n");
	wc_printf("</div>\n<div id=\"content\">\n");
	escputs(messagetext);
	wc_printf("<hr />\n");
	wDumpContent(1);
}
Ejemplo n.º 15
0
/*
 * Display a list of all pages in a Wiki room (template callback)
 */
void tmplput_display_wiki_pagelist(StrBuf *Target, WCTemplputParams *TP)
{
	StrBuf *Buf;
	int row = 0;

	if (!IsEmptyStr(bstr("query"))) {
		serv_printf("MSGS SEARCH|%s||4", bstr("query"));	/* search-reduced list */
	}
	else {
		serv_printf("MSGS ALL|||4");				/* full list */
	}

	Buf = NewStrBuf();
	StrBuf_ServGetln(Buf);
	if (GetServerStatus(Buf, NULL) == 1) {
		StrBuf *pagetitle = NewStrBuf();

		wc_printf("<table class=\"wiki_pagelist_background\">");
		wc_printf("<th>%s</th>", _("Page title"));

		while((StrBuf_ServGetln(Buf) >= 0) && strcmp(ChrPtr(Buf), "000")) {
			StrBufExtract_token(pagetitle, Buf, 1, '|');

			if (!bmstrcasestr((char *)ChrPtr(pagetitle), "_HISTORY_")) {	/* no history pages */
				wc_printf("<tr bgcolor=\"%s\">", ((row%2) ? "#FFFFFF" : "#DDDDDD"));
				wc_printf("<td><a href=\"wiki?page=");
				urlescputs(ChrPtr(pagetitle));
				wc_printf("\">");
				escputs(ChrPtr(pagetitle));
				wc_printf("</a></td>");
				wc_printf("</tr>\n");
				++row;
			}
		}
		wc_printf("</table>\n");
		FreeStrBuf(&pagetitle);
	}

	FreeStrBuf(&Buf);
}
Ejemplo n.º 16
0
/*
 * Receiving side of the chat window.  
 * This does JavaScript writes to
 * other divs whenever it refreshes and finds new data.
 */
void chat_recv(void) {
	char buf[SIZ];
	char cl_user[SIZ];

	serv_printf("RCHT poll|%d", WC->last_chat_seq);
	serv_getln(buf, sizeof buf);
	if (buf[0] == '1') {
		WC->last_chat_seq = extract_int(&buf[4], 0);
		extract_token(cl_user, &buf[4], 2, '|', sizeof cl_user);

		/* who is speaking ... */
		if (strcasecmp(cl_user, WC->last_chat_user)) {
			wc_printf("<br>\n");
			if (!strcasecmp(cl_user, ChrPtr(WC->wc_fullname))) {
				wc_printf("<span class=\"chat_myname_class\">");
			}
			else {
				wc_printf("<span class=\"chat_notmyname_class\">");
			}
			escputs(cl_user);
			strcpy(WC->last_chat_user, cl_user);

			wc_printf(": </span>");
		}
		else {
			wc_printf("&nbsp;&nbsp;&nbsp;");
		}

		/* what did they say ... */
		wc_printf("<span class=\"chat_text_class\">");
		while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
			escputs(buf);
		}
		wc_printf("<br></span>\n");
	}
}
Ejemplo n.º 17
0
/*
 * RSS feed generator -- do one message
 */
void feed_rss_one_message(long msgnum) {
	int in_body = 0;
	int in_messagetext = 0;
	int found_title = 0;
	int found_guid = 0;
	char pubdate[128];
	StrBuf *messagetext = NULL;
	int is_top_level_post = 1;
	const char *BufPtr = NULL;
	StrBuf *Line = NewStrBufPlain(NULL, 1024);
	char buf[1024];
	int permalink_hash = 0;

	/* Phase 1: read the message into memory */
	serv_printf("MSG4 %ld", msgnum);
	serv_getln(buf, sizeof buf);
	if (buf[0] != '1') return;
	StrBuf *ServerResponse = NewStrBuf();
	while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
		StrBufAppendPrintf(ServerResponse, "%s\n", buf);
	}

	/* Phase 2: help SkyNet become self-aware */
	BufPtr = NULL;
	while (StrBufSipLine(Line, ServerResponse, &BufPtr), ((BufPtr!=StrBufNOTNULL)&&(BufPtr!=NULL)) ) {
		if (in_body) {
			/* do nothing */
		}
		else if (StrLength(Line) == 0) {
			++in_body;
		}
		else if ((StrLength(Line) > 5) && (!strncasecmp(ChrPtr(Line), "wefw=", 5))) {
			is_top_level_post = 0;	/* presence of references means it's a reply/comment */
		}
		else if ((StrLength(Line) > 5) && (!strncasecmp(ChrPtr(Line), "msgn=", 5))) {
			StrBufCutLeft(Line, 5);
			permalink_hash = ThreadIdHash(Line);
		}
	}

	/*
	 * Phase 3: output the message in RSS <item> form
	 * (suppress replies [comments] if this is a blog room)
	 */
	if ( (WC->CurRoom.view != VIEW_BLOG) || (is_top_level_post == 1) ) {
		wc_printf("<item>");
		wc_printf("<link>%s/readfwd?go=", ChrPtr(site_prefix));
		urlescputs(ChrPtr(WC->CurRoom.name));
		if ((WC->CurRoom.view == VIEW_BLOG) && (permalink_hash != 0)) {
			wc_printf("?p=%d", permalink_hash);
		}
		else {
			wc_printf("?start_reading_at=%ld", msgnum);
		}
		wc_printf("</link>");
	
		BufPtr = NULL;
		in_body = 0;
		in_messagetext = 0;
		while (StrBufSipLine(Line, ServerResponse, &BufPtr), ((BufPtr!=StrBufNOTNULL)&&(BufPtr!=NULL)) ) {
			safestrncpy(buf, ChrPtr(Line), sizeof buf);

			/* XML parsers can be picky; strip out nonprintable header characters */
			if ((strlen(buf)>=6) && (buf[4]=='=')) {
				char *p = &buf[5];
				while (*p) {
					if (!isprint(*p)) {
						*p = 0;
					}
					++p;
				}
			}

			/* Now output fields */
			if (in_body) {
				if (in_messagetext) {
					StrBufAppendBufPlain(messagetext, buf, -1, 0);
					StrBufAppendBufPlain(messagetext, HKEY("\r\n"), 0);
				}
				else if (IsEmptyStr(buf)) {
					in_messagetext = 1;
				}
			}
			else if (!strncasecmp(buf, "subj=", 5)) {
				wc_printf("<title>");
				escputs(&buf[5]);
				wc_printf("</title>");
				++found_title;
			}
			else if (!strncasecmp(buf, "exti=", 5)) {
				wc_printf("<guid isPermaLink=\"false\">");
				escputs(&buf[5]);
				wc_printf("</guid>");
				++found_guid;
			}
			else if (!strncasecmp(buf, "time=", 5)) {
				http_datestring(pubdate, sizeof pubdate, atol(&buf[5]));
				wc_printf("<pubDate>%s</pubDate>", pubdate);
			}
			else if (!strncasecmp(buf, "text", 4)) {
				if (!found_title) {
					wc_printf("<title>Message #%ld</title>", msgnum);
				}
				if (!found_guid) {
					wc_printf("<guid isPermaLink=\"false\">%ld@%s</guid>",
						msgnum,
						ChrPtr(WC->serv_info->serv_humannode)
					);
				}
				wc_printf("<description>");
				in_body = 1;
				messagetext = NewStrBuf();
			}
		}
	
		if (in_body) {
			cdataout((char*)ChrPtr(messagetext));
			FreeStrBuf(&messagetext);
			wc_printf("</description>");
		}

		wc_printf("</item>");
	}

	FreeStrBuf(&Line);
	FreeStrBuf(&ServerResponse);
	return;
}
Ejemplo n.º 18
0
/*
 * Entry point for RSS feed generator
 */
void feed_rss(void) {
	char buf[1024];

	output_headers(0, 0, 0, 0, 1, 0);
	hprintf("Content-type: text/xml; charset=utf-8\r\n");
	hprintf(
		"Server: %s / %s\r\n"
		"Connection: close\r\n"
	,
		PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software)
	);
	begin_burst();

	wc_printf("<?xml version=\"1.0\"?>"
		"<rss version=\"2.0\">"
		"<channel>"
	);

	wc_printf("<title>");
	escputs(ChrPtr(WC->CurRoom.name));
	wc_printf("</title>");

	wc_printf("<link>");
	escputs(ChrPtr(site_prefix));
	wc_printf("/</link>");

	serv_puts("RINF");
	serv_getln(buf, sizeof buf);
	if (buf[0] == '1') {
		wc_printf("<description>\r\n");
		while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
			escputs(buf);
			wc_printf("\r\n");
		}
		wc_printf("</description>");
	}

	wc_printf("<image><title>");
	escputs(ChrPtr(WC->CurRoom.name));
	wc_printf("</title><url>");
	escputs(ChrPtr(site_prefix));
	wc_printf("/roompic?room=");
	urlescputs(ChrPtr(WC->CurRoom.name));
	wc_printf("</url><link>");
	escputs(ChrPtr(site_prefix));
	wc_printf("/</link></image>\r\n");

	feed_rss_do_room_info_as_description();
	feed_rss_do_messages();

	wc_printf("</channel>"
		"</rss>"
		"\r\n\r\n"
	);

	wDumpContent(0);
}
Ejemplo n.º 19
0
/*
 * Output the room info file of the current room as a <description> for the channel
 */
void feed_rss_do_room_info_as_description(void)
{
	wc_printf("<description>");
	escputs(ChrPtr(WC->CurRoom.name));	/* FIXME use the output of RINF instead */
	wc_printf("</description>\r\n");
}
Ejemplo n.º 20
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);
}
Ejemplo n.º 21
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();
}
Ejemplo n.º 22
0
/*
 * Display the OpenIDs associated with an account
 */
void display_openids(void)
{
	wcsession *WCC = WC;
	char buf[1024];
	int bg = 0;

	output_headers(1, 1, 1, 0, 0, 0);

	do_template("box_begin_1");
	StrBufAppendBufPlain(WCC->WBuf, _("Manage Account/OpenID Associations"), -1, 0);
	do_template("box_begin_2");

	if (WCC->serv_info->serv_supports_openid) {

		wc_printf("<table class=\"altern\">");
	
		serv_puts("OIDL");
		serv_getln(buf, sizeof buf);
		if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
			bg = 1 - bg;
			wc_printf("<tr class=\"%s\">", (bg ? "even" : "odd"));
			wc_printf("<td><img src=\"static/webcit_icons/openid-small.gif\"></td><td>");
			escputs(buf);
			wc_printf("</td><td>");
			wc_printf("<a href=\"openid_detach?id_to_detach=");
			urlescputs(buf);
			wc_printf("\" onClick=\"return confirm('%s');\">",
				_("Do you really want to delete this OpenID?"));
			wc_printf("%s</a>", _("(delete)"));
			wc_printf("</td></tr>\n");
		}
	
		wc_printf("</table><br>\n");
	
	        wc_printf("<form method=\"POST\" action=\"openid_attach\">\n");
		wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WCC->nonce);
		wc_printf(_("Add an OpenID: "));
	        wc_printf("<input type=\"text\" name=\"openid_url\" class=\"openid_urlarea\" size=\"40\">\n");
	        wc_printf("<input type=\"submit\" name=\"attach_button\" value=\"%s\">"
			"</form></center>\n", _("Attach"));
	}

	else {
		wc_printf(_("%s does not permit authentication via OpenID."), ChrPtr(WCC->serv_info->serv_humannode));
	}

	do_template("box_end");
	wDumpContent(2);
}
Ejemplo n.º 23
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;
}
Ejemplo n.º 24
0
/*
 * Output HTTP headers and leading HTML for a page
 */
void output_headers(	int do_httpheaders,	/* 1 = output HTTP headers			  */
			int do_htmlhead,	/* 1 = output HTML <head> section and <body> opener */
			int do_room_banner,	/* 1 = include the room banner and <div id="content"></div> */
			int unset_cookies,	/* 1 = session is terminating, so unset the cookies */
			int suppress_check,	/* 1 = suppress check for instant messages	  */
			int cache		/* 1 = allow browser to cache this page	     */
) {
	wcsession *WCC = WC;
	char httpnow[128];

	if (WCC->Hdr->HaveRange > 1)
		hprintf("HTTP/1.1 206 Partial Content\r\n");
	else
		hprintf("HTTP/1.1 200 OK\r\n");

	http_datestring(httpnow, sizeof httpnow, time(NULL));

	if (do_httpheaders) {
		if (WCC->serv_info != NULL)
			hprintf("Content-type: text/html; charset=utf-8\r\n"
				"Server: %s / %s\n"
				"Connection: close\r\n",
				PACKAGE_STRING, 
				ChrPtr(WCC->serv_info->serv_software));
		else
			hprintf("Content-type: text/html; charset=utf-8\r\n"
				"Server: %s / [n/a]\n"
				"Connection: close\r\n",
				PACKAGE_STRING);
	}

	if (cache > 0) {
		char httpTomorow[128];

		http_datestring(httpTomorow, sizeof httpTomorow, 
				time(NULL) + 60 * 60 * 24 * 2);

		hprintf("Pragma: public\r\n"
			"Cache-Control: max-age=3600, must-revalidate\r\n"
			"Last-modified: %s\r\n"
			"Expires: %s\r\n",
			httpnow,
			httpTomorow
		);
	}
	else {
		hprintf("Pragma: no-cache\r\n"
			"Cache-Control: no-store\r\n"
			"Expires: -1\r\n"
		);
	}

	if (cache < 2) stuff_to_cookie(unset_cookies);

	if (do_htmlhead) {
		begin_burst();
		do_template("head");
		if ( (WCC->logged_in) && (!unset_cookies) ) {
			DoTemplate(HKEY("paging"), NULL, &NoCtx);
		}
		if (do_room_banner) {
			tmplput_roombanner(NULL, NULL);
		}
	}

	if (do_room_banner) {
		wc_printf("<div id=\"content\">\n");
	}
}
Ejemplo n.º 25
0
/*
 * Display the revision history for a wiki page (template callback)
 */
void tmplput_display_wiki_history(StrBuf *Target, WCTemplputParams *TP)
{
	char pagename[128];
	StrBuf *Buf;
	int row = 0;

	safestrncpy(pagename, bstr("page"), sizeof pagename);
	str_wiki_index(pagename);

	serv_printf("WIKI history|%s", pagename);
	Buf = NewStrBuf();
	StrBuf_ServGetln(Buf);
	if (GetServerStatus(Buf, NULL) == 1) {

		time_t rev_date;
		char rev_date_displayed[64];
		StrBuf *rev_uuid = NewStrBuf();
		StrBuf *author = NewStrBuf();
		StrBuf *node = NewStrBuf();

		wc_printf("<table class=\"wiki_history_background\">");

		wc_printf("<th>%s</th>", _("Date"));
		wc_printf("<th>%s</th>", _("Author"));

		while((StrBuf_ServGetln(Buf) >= 0) &&  strcmp(ChrPtr(Buf), "000")) {

			rev_date = extract_long(ChrPtr(Buf), 1);
			webcit_fmt_date(rev_date_displayed, sizeof rev_date_displayed, rev_date, DATEFMT_FULL);
			StrBufExtract_token(author, Buf, 2, '|');

			wc_printf("<tr bgcolor=\"%s\">", ((row%2) ? "#FFFFFF" : "#DDDDDD"));
			wc_printf("<td>%s</td><td>", rev_date_displayed);
			if (!strcasecmp(ChrPtr(node), (char *)WC->serv_info->serv_nodename)) {
				escputs(ChrPtr(author));
				wc_printf(" @ ");
				escputs(ChrPtr(node));
			}
			else {
				wc_printf("<a href=\"showuser?who=");
				urlescputs(ChrPtr(author));
				wc_printf("\">");
				escputs(ChrPtr(author));
				wc_printf("</a>");
			}
			wc_printf("</td>");

			if (row == 0) {
				wc_printf("<td><a href=\"wiki?page=%s", bstr("page"));
				wc_printf("?go="); urlescputs(ChrPtr(WC->CurRoom.name));
				wc_printf("\">%s</a></td>", _("(show)"));
				wc_printf("<td>(%s)</td>", _("Current version"));
			}

			else {
				wc_printf("<td><a href=\"wiki?page=%s?rev=%s",
					bstr("page"),
					ChrPtr(rev_uuid)
				);
				wc_printf("?go="); urlescputs(ChrPtr(WC->CurRoom.name));
				wc_printf("\">%s</a></td>", _("(show)"));
				wc_printf("<td><a href=\"javascript:GetLoggedInFirst(encodeURIComponent('wiki?page=%s?rev=%s?revert=1'))\">%s</a></td>",
					bstr("page"),
					ChrPtr(rev_uuid),
					_("(revert)")
				);
			}
			wc_printf("</tr>\n");

			/* Extract all fields except the author and date after displaying the row.  This
			 * is deliberate, because the timestamp reflects when the diff was written, not
			 * when the version which it reflects was written.  Similarly, the name associated
			 * with each diff is the author who created the newer version of the page that
			 * made the diff happen.
			 */
			StrBufExtract_token(rev_uuid, Buf, 0, '|');
			StrBufExtract_token(node, Buf, 3, '|');
			++row;
		}

		wc_printf("</table>\n");
		FreeStrBuf(&author);
		FreeStrBuf(&node);
		FreeStrBuf(&rev_uuid);
	}
	else {
		wc_printf("%s", ChrPtr(Buf));
	}

	FreeStrBuf(&Buf);
}
Ejemplo n.º 26
0
/*
 * Background ajax call to receive updates from the browser when a note is moved, resized, or updated.
 */
void ajax_update_note(void) {

	char buf[1024];
	int msgnum;
	struct vnote *v = NULL;

        if (!havebstr("note_uid")) {
		begin_ajax_response();
		wc_printf("Received ajax_update_note() request without a note UID.");
		end_ajax_response();
		return;
	}

	serv_printf("EUID %s", bstr("note_uid"));
	serv_getln(buf, sizeof buf);
	if (buf[0] != '2') {
		begin_ajax_response();
		wc_printf("Cannot find message containing vNote with the requested uid!");
		end_ajax_response();
		return;
	}
	msgnum = atol(&buf[4]);
	
	/* Was this request a delete operation?  If so, nuke it... */
	if (havebstr("deletenote")) {
		if (!strcasecmp(bstr("deletenote"), "yes")) {
			serv_printf("DELE %d", msgnum);
			serv_getln(buf, sizeof buf);
			begin_ajax_response();
			wc_printf("%s", buf);
			end_ajax_response();
			return;
		}
	}

	/* If we get to this point it's an update, not a delete */
	v = vnote_new_from_msg(msgnum, 0);
	if (!v) {
		begin_ajax_response();
		wc_printf("Cannot locate a vNote within message %d\n", msgnum);
		end_ajax_response();
		return;
	}

	/* Make any requested changes */
        if (havebstr("top")) {
		v->pos_top = atoi(bstr("top"));
	}
        if (havebstr("left")) {
		v->pos_left = atoi(bstr("left"));
	}
        if (havebstr("height")) {
		v->pos_height = atoi(bstr("height"));
	}
        if (havebstr("width")) {
		v->pos_width = atoi(bstr("width"));
	}
        if (havebstr("red")) {
		v->color_red = atoi(bstr("red"));
	}
        if (havebstr("green")) {
		v->color_green = atoi(bstr("green"));
	}
        if (havebstr("blue")) {
		v->color_blue = atoi(bstr("blue"));
	}
        if (havebstr("value")) {	/* I would have preferred 'body' but InPlaceEditor hardcodes 'value' */
		if (v->body) free(v->body);
		v->body = strdup(bstr("value"));
	}

	/* Serialize it and save it to the message base.  Server will delete the old one. */
	write_vnote_to_server(v);

	begin_ajax_response();
	if (v->body) {
		escputs(v->body);
	}
	end_ajax_response();

	vnote_free(v);
}