Ejemplo n.º 1
0
/*
 * Go to the URL saved by push_destination()
 */
void pop_destination(void) {
	wcsession *WCC = WC;

	/*
	 * If we are in the middle of a new user signup, the server may request that
	 * we first pass through a registration screen.
	 */
	if ((WCC) && (WCC->need_regi)) {
		if ((WCC->PushedDestination != NULL) && (StrLength(WCC->PushedDestination) > 0)) {
			/* Registering will take us to the My Citadel Config room, so save our place */
			StrBufAppendBufPlain(WCC->PushedDestination, HKEY("?go="), 0);
			StrBufUrlescAppend(WCC->PushedDestination, WCC->CurRoom.name, NULL);
		}
		WCC->need_regi = 0;
		display_reg(1);
		return;
	}

	/*
	 * Do something reasonable if we somehow ended up requesting a pop without
	 * having first done a push.
	 */
	if ( (!WCC) || (WCC->PushedDestination == NULL) || (StrLength(WCC->PushedDestination) == 0) ) {
		do_welcome();
		return;
	}

	/*
	 * All righty then!  We have a destination saved, so go there now.
	 */
	if (verbose)
		syslog(LOG_DEBUG, "Pop: %s", ChrPtr(WCC->PushedDestination));
	http_redirect(ChrPtr(WCC->PushedDestination));
}
Ejemplo n.º 2
0
/*
 * Offer the RSS feed button for this room
 */
void tmplput_rssbutton(StrBuf *Target, WCTemplputParams *TP) 
{
	StrBuf *FeedLink = NULL;

	FeedLink = NewStrBufPlain(HKEY("/feed_rss?go="));
	StrBufUrlescAppend(FeedLink, WC->CurRoom.name, NULL);

	StrBufAppendPrintf(Target, "<a type=\"application/rss+xml\" href=\"");
	StrBufAppendBuf(Target, FeedLink, 0);
	StrBufAppendPrintf(Target, "\"><img src=\"static/webcit_icons/essen/16x16/rss.png\" alt=\"RSS\">");
	StrBufAppendPrintf(Target, "</a>");
	FreeStrBuf(&FeedLink);
}
Ejemplo n.º 3
0
static void TestUrlescEncodeStdin(void)
{
	int fdin = 0;// STDIN
	const char *Err;
	StrBuf *Target;
	StrBuf *Source;

	Source = NewStrBuf();

	while (fdin == 0) {

		StrBufTCP_read_line(Source, &fdin, 0, &Err);
		Target = NewStrBuf();
		
		StrBufUrlescAppend(Target, Source, NULL);
		
		TestRevalidateStrBuf(Target);
		printf("%s\n", ChrPtr(Target));
		FreeStrBuf(&Target);
	}
	FreeStrBuf(&Source);
}
Ejemplo n.º 4
0
/*
 * Setup an OpenID authentication
 */
void cmd_oids(char *argbuf) {
	struct CitContext *CCC = CC;	/* CachedCitContext - performance boost */
	const char *Pos = NULL;
	StrBuf *ArgBuf = NULL;
	StrBuf *ReplyBuf = NULL;
	StrBuf *return_to = NULL;
	StrBuf *RedirectUrl = NULL;
	ctdl_openid *oiddata;
	int discovery_succeeded = 0;

	if (CtdlGetConfigInt("c_disable_newu"))
	{
		cprintf("%d this system does not support openid.\n",
			ERROR + CMD_NOT_SUPPORTED);
		return;
	}
	Free_ctdl_openid ((ctdl_openid**)&CCC->openid_data);

	CCC->openid_data = oiddata = malloc(sizeof(ctdl_openid));
	if (oiddata == NULL) {
		syslog(LOG_ALERT, "malloc() failed: %s", strerror(errno));
		cprintf("%d malloc failed\n", ERROR + INTERNAL_ERROR);
		return;
	}
	memset(oiddata, 0, sizeof(ctdl_openid));

	ArgBuf = NewStrBufPlain(argbuf, -1);

	oiddata->verified = 0;
	oiddata->claimed_id = NewStrBufPlain(NULL, StrLength(ArgBuf));
	return_to = NewStrBufPlain(NULL, StrLength(ArgBuf));

	StrBufExtract_NextToken(oiddata->claimed_id, ArgBuf, &Pos, '|');
	StrBufExtract_NextToken(return_to, ArgBuf, &Pos, '|');

	syslog(LOG_DEBUG, "User-Supplied Identifier is: %s", ChrPtr(oiddata->claimed_id));

	/********** OpenID 2.0 section 7.3 - Discovery **********/

	/* Section 7.3.1 says we have to attempt XRI based discovery.
	 * No one is using this, no one is asking for it, no one wants it.
	 * So we're not even going to bother attempting this mode.
	 */

	/* Attempt section 7.3.2 (Yadis discovery) and section 7.3.3 (HTML discovery);
	 */
	discovery_succeeded = perform_openid2_discovery(oiddata->claimed_id);

	if (discovery_succeeded == 0) {
		cprintf("%d There is no OpenID identity provider at this location.\n", ERROR);
	}

	else {
		/*
		 * If we get to this point we are in possession of a valid OpenID Provider URL.
		 */
		syslog(LOG_DEBUG, "OP URI '%s' discovered using method %d",
			ChrPtr(oiddata->op_url),
			discovery_succeeded
		);

		/* We have to "normalize" our Claimed ID otherwise it will cause some OP's to barf */
		if (cbmstrcasestr(ChrPtr(oiddata->claimed_id), "://") == NULL) {
			StrBuf *cid = oiddata->claimed_id;
			oiddata->claimed_id = NewStrBufPlain(HKEY("http://"));
			StrBufAppendBuf(oiddata->claimed_id, cid, 0);
			FreeStrBuf(&cid);
		}

		/*
		 * OpenID 2.0 section 9: request authentication
		 * Assemble a URL to which the user-agent will be redirected.
		 */
	
		RedirectUrl = NewStrBufDup(oiddata->op_url);

		StrBufAppendBufPlain(RedirectUrl, HKEY("?openid.ns="), 0);
		StrBufUrlescAppend(RedirectUrl, NULL, "http://specs.openid.net/auth/2.0");

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.mode=checkid_setup"), 0);

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.claimed_id="), 0);
		StrBufUrlescAppend(RedirectUrl, oiddata->claimed_id, NULL);

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.identity="), 0);
		StrBufUrlescAppend(RedirectUrl, oiddata->claimed_id, NULL);

		/* return_to tells the provider how to complete the round trip back to our site */
		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.return_to="), 0);
		StrBufUrlescAppend(RedirectUrl, return_to, NULL);

		/* Attribute Exchange
		 * See:
		 *	http://openid.net/specs/openid-attribute-exchange-1_0.html
		 *	http://code.google.com/apis/accounts/docs/OpenID.html#endpoint
		 * 	http://test-id.net/OP/AXFetch.aspx
		 */

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.ns.ax="), 0);
		StrBufUrlescAppend(RedirectUrl, NULL, "http://openid.net/srv/ax/1.0");

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.ax.mode=fetch_request"), 0);

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.ax.required=firstname,lastname,friendly,nickname"), 0);

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.ax.type.firstname="), 0);
		StrBufUrlescAppend(RedirectUrl, NULL, "http://axschema.org/namePerson/first");

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.ax.type.lastname="), 0);
		StrBufUrlescAppend(RedirectUrl, NULL, "http://axschema.org/namePerson/last");

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.ax.type.friendly="), 0);
		StrBufUrlescAppend(RedirectUrl, NULL, "http://axschema.org/namePerson/friendly");

		StrBufAppendBufPlain(RedirectUrl, HKEY("&openid.ax.type.nickname="), 0);
		StrBufUrlescAppend(RedirectUrl, NULL, "http://axschema.org/namePerson/nickname");

		syslog(LOG_DEBUG, "OpenID: redirecting client to %s", ChrPtr(RedirectUrl));
		cprintf("%d %s\n", CIT_OK, ChrPtr(RedirectUrl));
	}
	
	FreeStrBuf(&ArgBuf);
	FreeStrBuf(&ReplyBuf);
	FreeStrBuf(&return_to);
	FreeStrBuf(&RedirectUrl);
}
Ejemplo n.º 5
0
/*
 * Scan a room's netconfig to determine whether it requires POP3 aggregation
 */
void pop3client_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCFG)
{
	const RoomNetCfgLine *pLine;
	void *vptr;

	pthread_mutex_lock(&POP3QueueMutex);
	if (GetHash(POP3QueueRooms, LKEY(qrbuf->QRnumber), &vptr))
	{
		pthread_mutex_unlock(&POP3QueueMutex);
		EVP3CQ_syslog(LOG_DEBUG,
			      "pop3client: [%ld] %s already in progress.",
			      qrbuf->QRnumber,
			      qrbuf->QRname);
		return;
	}
	pthread_mutex_unlock(&POP3QueueMutex);

	if (server_shutting_down) return;

	pLine = OneRNCFG->NetConfigs[pop3client];

	while (pLine != NULL)
	{
		pop3aggr *cptr;

		cptr = (pop3aggr *) malloc(sizeof(pop3aggr));
		memset(cptr, 0, sizeof(pop3aggr));
		///TODO do we need this? cptr->roomlist_parts=1;
		cptr->RoomName = NewStrBufPlain(qrbuf->QRname, -1);
		cptr->pop3user = NewStrBufDup(pLine->Value[1]);
		cptr->pop3pass = NewStrBufDup(pLine->Value[2]);
		cptr->Url = NewStrBuf();
		cptr->Host = NewStrBufDup(pLine->Value[0]);

		cptr->keep = atol(ChrPtr(pLine->Value[3]));
		cptr->interval = atol(ChrPtr(pLine->Value[4]));

		StrBufAppendBufPlain(cptr->Url, HKEY("pop3://"), 0);
		StrBufUrlescUPAppend(cptr->Url, cptr->pop3user, NULL);
		StrBufAppendBufPlain(cptr->Url, HKEY(":"), 0);
		StrBufUrlescUPAppend(cptr->Url, cptr->pop3pass, NULL);
		StrBufAppendBufPlain(cptr->Url, HKEY("@"), 0);
		StrBufAppendBuf(cptr->Url, cptr->Host, 0);
		StrBufAppendBufPlain(cptr->Url, HKEY("/"), 0);
		StrBufUrlescAppend(cptr->Url, cptr->RoomName, NULL);

		ParseURL(&cptr->IO.ConnectMe, cptr->Url, 110);


#if 0
/* todo: we need to reunite the url to be shure. */

		pthread_mutex_lock(&POP3ueueMutex);
		GetHash(POP3FetchUrls, SKEY(ptr->Url), &vptr);
		use_this_cptr = (pop3aggr *)vptr;

		if (use_this_rncptr != NULL)
		{
			/* mustn't attach to an active session */
			if (use_this_cptr->RefCount > 0)
			{
				DeletePOP3Cfg(cptr);
///						Count->count--;
			}
			else
			{
				long *QRnumber;
				StrBufAppendBufPlain(
					use_this_cptr->rooms,
					qrbuf->QRname,
					-1, 0);
				if (use_this_cptr->roomlist_parts == 1)
				{
					use_this_cptr->OtherQRnumbers
						= NewHash(1, lFlathash);
				}
				QRnumber = (long*)malloc(sizeof(long));
				*QRnumber = qrbuf->QRnumber;
				Put(use_this_cptr->OtherQRnumbers,
				    LKEY(qrbuf->QRnumber),
				    QRnumber,
				    NULL);

				use_this_cptr->roomlist_parts++;
			}
			pthread_mutex_unlock(&POP3QueueMutex);
			continue;
		}
		pthread_mutex_unlock(&RSSQueueMutex);
#endif
		cptr->n = Pop3ClientID++;
		pthread_mutex_lock(&POP3QueueMutex);
		Put(POP3FetchUrls,
		    SKEY(cptr->Url),
		    cptr,
		    DeletePOP3Aggregator);

		pthread_mutex_unlock(&POP3QueueMutex);
		pLine = pLine->next;

	}
}
Ejemplo n.º 6
0
/**
 * urlescape buffer and print it as header 
 */
void hurlescputs(const char *strbuf) 
{
	StrBufUrlescAppend(WC->HBuf, NULL, strbuf);
}
Ejemplo n.º 7
0
/*
 * Sanitize and enhance an HTML message for display.
 * Also convert weird character sets to UTF-8 if necessary.
 * Also fixup img src="cid:..." type inline images to fetch the image
 *
 */
void output_html(const char *supplied_charset, int treat_as_wiki, int msgnum, StrBuf *Source, StrBuf *Target) {
	char buf[SIZ];
	char *msg;
	char *ptr;
	char *msgstart;
	char *msgend;
	StrBuf *converted_msg;
	int buffer_length = 1;
	int line_length = 0;
	int content_length = 0;
	char new_window[SIZ];
	int brak = 0;
	int alevel = 0;
	int scriptlevel = 0;
	int script_start_pos = (-1);
	int i;
	int linklen;
	char charset[128];
	StrBuf *BodyArea = NULL;
#ifdef HAVE_ICONV
	iconv_t ic = (iconv_t)(-1) ;
	char *ibuf;                   /* Buffer of characters to be converted */
	char *obuf;                   /* Buffer for converted characters      */
	size_t ibuflen;               /* Length of input buffer               */
	size_t obuflen;               /* Length of output buffer              */
	char *osav;                   /* Saved pointer to output buffer       */
#endif
	if (Target == NULL)
		Target = WC->WBuf;

	safestrncpy(charset, supplied_charset, sizeof charset);
	msg = strdup("");
	sprintf(new_window, "<a target=\"%s\" href=", TARGET);

	if (Source == NULL) while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
		line_length = strlen(buf);
		buffer_length = content_length + line_length + 2;
		ptr = realloc(msg, buffer_length);
		if (ptr == NULL) {
			StrBufAppendPrintf(Target, "<b>");
			StrBufAppendPrintf(Target, _("realloc() error! couldn't get %d bytes: %s"),
					buffer_length + 1,
					strerror(errno));
			StrBufAppendPrintf(Target, "</b><br><br>\n");
			while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
				/** flush */
			}
			free(msg);
			return;
		}
		msg = ptr;
		strcpy(&msg[content_length], buf);
		content_length += line_length;
		strcpy(&msg[content_length], "\n");
		content_length += 1;
	}
	else {
		content_length = StrLength(Source);
		free(msg);
		msg = (char*) ChrPtr(Source);/* TODO: remove cast */
		buffer_length = content_length;
	}

	/** Do a first pass to isolate the message body */
	ptr = msg + 1;
	msgstart = msg;
	msgend = &msg[content_length];

	while (ptr < msgend) {

		/** Advance to next tag */
		ptr = strchr(ptr, '<');
		if ((ptr == NULL) || (ptr >= msgend)) break;
		++ptr;
		if ((ptr == NULL) || (ptr >= msgend)) break;

		/*
		 *  Look for META tags.  Some messages (particularly in
		 *  Asian locales) illegally declare a message's character
		 *  set in the HTML instead of in the MIME headers.  This
		 *  is wrong but we have to work around it anyway.
		 */
		if (!strncasecmp(ptr, "META", 4)) {

			char *meta_start;
			char *meta_end;
			int meta_length;
			char *meta;
			char *meta_http_equiv;
			char *meta_content;
			char *spaceptr;

			meta_start = &ptr[4];
			meta_end = strchr(ptr, '>');
			if ((meta_end != NULL) && (meta_end <= msgend)) {
				meta_length = meta_end - meta_start + 1;
				meta = malloc(meta_length + 1);
				safestrncpy(meta, meta_start, meta_length);
				meta[meta_length] = 0;
				striplt(meta);
				if (!strncasecmp(meta, "HTTP-EQUIV=", 11)) {
					meta_http_equiv = strdup(&meta[11]);
					spaceptr = strchr(meta_http_equiv, ' ');
					if (spaceptr != NULL) {
						*spaceptr = 0;
						meta_content = strdup(++spaceptr);
						if (!strncasecmp(meta_content, "content=", 8)) {
							strcpy(meta_content, &meta_content[8]);
							stripquotes(meta_http_equiv);
							stripquotes(meta_content);
							extract_charset_from_meta(charset,
									meta_http_equiv, meta_content);
						}
						free(meta_content);
					}
					free(meta_http_equiv);
				}
				free(meta);
			}
		}

		/*
		 * Any of these tags cause everything up to and including
		 * the tag to be removed.
		 */	
		if ( (!strncasecmp(ptr, "HTML", 4))
				||(!strncasecmp(ptr, "HEAD", 4))
				||(!strncasecmp(ptr, "/HEAD", 5))
				||(!strncasecmp(ptr, "BODY", 4)) ) {
			char *pBody = NULL;

			if (!strncasecmp(ptr, "BODY", 4)) {
				pBody = ptr;
			}
			ptr = strchr(ptr, '>');
			if ((ptr == NULL) || (ptr >= msgend)) break;
			if ((pBody != NULL) && (ptr - pBody > 4)) {
				char* src;
				char *cid_start, *cid_end;

				*ptr = '\0';
				pBody += 4; 
				while ((isspace(*pBody)) && (pBody < ptr))
					pBody ++;
				BodyArea = NewStrBufPlain(NULL,  ptr - pBody);

				if (pBody < ptr) {
					src = strstr(pBody, "cid:");
					if (src) {
						cid_start = src + 4;
						cid_end = cid_start;
						while ((*cid_end != '"') && 
								!isspace(*cid_end) &&
								(cid_end < ptr))
							cid_end ++;

						/* copy tag and attributes up to src="cid: */
						StrBufAppendBufPlain(BodyArea, pBody, src - pBody, 0);

						/* add in /webcit/mimepart/<msgno>/CID/ 
						   trailing / stops dumb URL filters getting excited */
						StrBufAppendPrintf(BodyArea,
								"/webcit/mimepart/%d/",msgnum);
						StrBufAppendBufPlain(BodyArea, cid_start, cid_end - cid_start, 0);

						if (ptr - cid_end > 0)
							StrBufAppendBufPlain(BodyArea, 
									cid_end + 1, 
									ptr - cid_end, 0);
					}
					else 
						StrBufAppendBufPlain(BodyArea, pBody, ptr - pBody, 0);
				}
				*ptr = '>';
			}
			++ptr;
			if ((ptr == NULL) || (ptr >= msgend)) break;
			msgstart = ptr;
		}

		/*
		 * Any of these tags cause everything including and following
		 * the tag to be removed.
		 */
		if ( (!strncasecmp(ptr, "/HTML", 5))
				||(!strncasecmp(ptr, "/BODY", 5)) ) {
			--ptr;
			msgend = ptr;
			strcpy(ptr, "");

		}

		++ptr;
	}
	if (msgstart > msg) {
		strcpy(msg, msgstart);
	}

	/* Now go through the message, parsing tags as necessary. */
	converted_msg = NewStrBufPlain(NULL, content_length + 8192);


	/** Convert foreign character sets to UTF-8 if necessary. */
#ifdef HAVE_ICONV
	if ( (strcasecmp(charset, "us-ascii"))
			&& (strcasecmp(charset, "UTF-8"))
			&& (strcasecmp(charset, ""))
	   ) {
		syslog(LOG_DEBUG, "Converting %s to UTF-8\n", charset);
		ctdl_iconv_open("UTF-8", charset, &ic);
		if (ic == (iconv_t)(-1) ) {
			syslog(LOG_WARNING, "%s:%d iconv_open() failed: %s\n",
					__FILE__, __LINE__, strerror(errno));
		}
	}
	if  (Source == NULL) {
		if (ic != (iconv_t)(-1) ) {
			ibuf = msg;
			ibuflen = content_length;
			obuflen = content_length + (content_length / 2) ;
			obuf = (char *) malloc(obuflen);
			osav = obuf;
			iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
			content_length = content_length + (content_length / 2) - obuflen;
			osav[content_length] = 0;
			free(msg);
			msg = osav;
			iconv_close(ic);
		}
	}
	else {
		if (ic != (iconv_t)(-1) ) {
			StrBuf *Buf = NewStrBufPlain(NULL, StrLength(Source) + 8096);;
			StrBufConvert(Source, Buf, &ic);
			FreeStrBuf(&Buf);
			iconv_close(ic);
			msg = (char*)ChrPtr(Source); /* TODO: get rid of this. */
		}
	}

#endif

	/*
	 *	At this point, the message has been stripped down to
	 *	only the content inside the <BODY></BODY> tags, and has
	 *	been converted to UTF-8 if it was originally in a foreign
	 *	character set.  The text is also guaranteed to be null
	 *	terminated now.
	 */

	if (converted_msg == NULL) {
		StrBufAppendPrintf(Target, "Error %d: %s<br>%s:%d", errno, strerror(errno), __FILE__, __LINE__);
		goto BAIL;
	}

	if (BodyArea != NULL) {
		StrBufAppendBufPlain(converted_msg, HKEY("<table "), 0);  
		StrBufAppendBuf(converted_msg, BodyArea, 0);
		StrBufAppendBufPlain(converted_msg, HKEY(" width=\"100%\"><tr><td>"), 0);
	}
	ptr = msg;
	msgend = strchr(msg, 0);
	while (ptr < msgend) {

		/** Try to sanitize the html of any rogue scripts */
		if (!strncasecmp(ptr, "<script", 7)) {
			if (scriptlevel == 0) {
				script_start_pos = StrLength(converted_msg);
			}
			++scriptlevel;
		}
		if (!strncasecmp(ptr, "</script", 8)) {
			--scriptlevel;
		}

		/**
		 * Change mailto: links to WebCit mail, by replacing the
		 * link with one that points back to our mail room.  Due to
		 * the way we parse URL's, it'll even handle mailto: links
		 * that have "?subject=" in them.
		 */
		if (!strncasecmp(ptr, "<a href=\"mailto:", 16)) {
			content_length += 64;
			StrBufAppendPrintf(converted_msg,
					"<a href=\"display_enter?force_room=_MAIL_?recp=");
			ptr = &ptr[16];
			++alevel;
			++brak;
		}
		/** Make external links open in a separate window */
		else if (!strncasecmp(ptr, "<a href=\"", 9)) {
			++alevel;
			++brak;
			if ( ((strchr(ptr, ':') < strchr(ptr, '/')))
					&&  ((strchr(ptr, '/') < strchr(ptr, '>'))) 
			   ) {
				/* open external links to new window */
				StrBufAppendPrintf(converted_msg, new_window);
				ptr = &ptr[8];
			}
			else if (
				(treat_as_wiki)
				&& (strncasecmp(ptr, "<a href=\"wiki?", 14))
				&& (strncasecmp(ptr, "<a href=\"dotgoto?", 17))
				&& (strncasecmp(ptr, "<a href=\"knrooms?", 17))
			) {
				content_length += 64;
				StrBufAppendPrintf(converted_msg, "<a href=\"wiki?go=");
				StrBufUrlescAppend(converted_msg, WC->CurRoom.name, NULL);
				StrBufAppendPrintf(converted_msg, "?page=");
				ptr = &ptr[9];
			}
			else {
				StrBufAppendPrintf(converted_msg, "<a href=\"");
				ptr = &ptr[9];
			}
		}
		/** Fixup <img src="cid:... ...> to fetch the mime part */
		else if (!strncasecmp(ptr, "<img ", 5)) {
			char *cid_start, *cid_end;
			char* tag_end=strchr(ptr,'>');
			char* src;
			/* FIXME - handle this situation (maybe someone opened an <img cid... 
			 * and then ended the message)
			 */
			if (!tag_end) {
				syslog(LOG_DEBUG, "tag_end is null and ptr is:\n");
				syslog(LOG_DEBUG, "%s\n", ptr);
				syslog(LOG_DEBUG, "Theoretical bytes remaining: %d\n", (int)(msgend - ptr));
			}

			src=strstr(ptr, "src=\"cid:");
			++brak;

			if (src
			    && isspace(*(src-1))
				&& tag_end
				&& (cid_start=strchr(src,':'))
				&& (cid_end=strchr(cid_start,'"'))
				&& (cid_end < tag_end)
			) {
				/* copy tag and attributes up to src="cid: */
				StrBufAppendBufPlain(converted_msg, ptr, src - ptr, 0);
				cid_start++;

				/* add in /webcit/mimepart/<msgno>/CID/ 
				   trailing / stops dumb URL filters getting excited */
				StrBufAppendPrintf(converted_msg,
						" src=\"/webcit/mimepart/%d/",msgnum);
				StrBufAppendBufPlain(converted_msg, cid_start, cid_end - cid_start, 0);
				StrBufAppendBufPlain(converted_msg, "/\"", -1, 0);

				ptr = cid_end+1;
			}
			StrBufAppendBufPlain(converted_msg, ptr, tag_end - ptr, 0);
			ptr = tag_end;
		}

		/**
		 * Turn anything that looks like a URL into a real link, as long
		 * as it's not inside a tag already
		 */
		else if ( (brak == 0) && (alevel == 0) &&
			  ( (!strncasecmp(ptr, "http://", 7)) ||
			    (!strncasecmp(ptr, "https://", 8)))) {
			/** Find the end of the link */
			int strlenptr;
			linklen = 0;
				
			strlenptr = strlen(ptr);
			for (i=0; i<=strlenptr; ++i) {
				if ((ptr[i]==0)
				    ||(isspace(ptr[i]))
				    ||(ptr[i]==10)
				    ||(ptr[i]==13)
				    ||(ptr[i]=='(')
				    ||(ptr[i]==')')
				    ||(ptr[i]=='<')
				    ||(ptr[i]=='>')
				    ||(ptr[i]=='[')
				    ||(ptr[i]==']')
				    ||(ptr[i]=='"')
				    ||(ptr[i]=='\'')
					) linklen = i;
				/* did s.b. send us an entity? */
				if (ptr[i] == '&') {
					if ((ptr[i+2] ==';') ||
					    (ptr[i+3] ==';') ||
					    (ptr[i+5] ==';') ||
					    (ptr[i+6] ==';') ||
					    (ptr[i+7] ==';'))
						linklen = i;
				}
				if (linklen > 0) break;
			}
			if (linklen > 0) {
				char *ltreviewptr;
				char *nbspreviewptr;
				char linkedchar;
				int len;
					
				len = linklen;
				linkedchar = ptr[len];
				ptr[len] = '\0';
				/* spot for some subject strings tinymce tends to give us. */
				ltreviewptr = strchr(ptr, '<');
				if (ltreviewptr != NULL) {
					*ltreviewptr = '\0';
					linklen = ltreviewptr - ptr;
				}

				nbspreviewptr = strstr(ptr, "&nbsp;");
				if (nbspreviewptr != NULL) {
					/* nbspreviewptr = '\0'; */
					linklen = nbspreviewptr - ptr;
				}
				if (ltreviewptr != 0)
					*ltreviewptr = '<';

				ptr[len] = linkedchar;

				content_length += (32 + linklen);
				StrBufAppendPrintf(converted_msg, "%s\"", new_window);
				StrBufAppendBufPlain(converted_msg, ptr, linklen, 0);
				StrBufAppendPrintf(converted_msg, "\">");
				StrBufAppendBufPlain(converted_msg, ptr, linklen, 0);
				ptr += linklen;
				StrBufAppendPrintf(converted_msg, "</A>");
			}
		}
		else {
			StrBufAppendBufPlain(converted_msg, ptr, 1, 0);
			ptr++;
		}


		if ((ptr >= msg) && (ptr <= msgend)) {
			/*
			 * We need to know when we're inside a tag,
			 * so we don't turn things that look like URL's into
			 * links, when they're already links - or image sources.
			 */
			if ((ptr > msg) && (*(ptr-1) == '<')) {
				++brak;
			}
			if ((ptr > msg) && (*(ptr-1) == '>')) {
				--brak;
				if ((scriptlevel == 0) && (script_start_pos >= 0)) {
					StrBufCutRight(converted_msg, StrLength(converted_msg) - script_start_pos);
					script_start_pos = (-1);
				}
			}
			if (!strncasecmp(ptr, "</A>", 3)) --alevel;
		}
	}

	if (BodyArea != NULL) {
		StrBufAppendBufPlain(converted_msg, HKEY("</td></tr></table>"), 0);  
		FreeStrBuf(&BodyArea);
	}

	/**	uncomment these two lines to override conversion	*/
	/**	memcpy(converted_msg, msg, content_length);		*/
	/**	output_length = content_length;				*/

	/** Output our big pile of markup */
	StrBufAppendBuf(Target, converted_msg, 0);

BAIL:	/** A little trailing vertical whitespace... */
	StrBufAppendPrintf(Target, "<br><br>\n");

	/** Now give back the memory */
	FreeStrBuf(&converted_msg);
	if ((msg != NULL) && (Source == NULL)) free(msg);
}