Exemplo n.º 1
0
/*
 * Deliver digest messages
 */
void network_deliver_digest(SpoolControl *sc)
{
	struct CitContext *CCC = CC;
	long len;
	char buf[SIZ];
	char *pbuf;
	struct CtdlMessage *msg = NULL;
	long msglen;
	recptypes *valid;
	char bounce_to[256];

	if (sc->Users[digestrecp] == NULL)
		return;

	msg = malloc(sizeof(struct CtdlMessage));
	memset(msg, 0, sizeof(struct CtdlMessage));
	msg->cm_magic = CTDLMESSAGE_MAGIC;
	msg->cm_format_type = FMT_RFC822;
	msg->cm_anon_type = MES_NORMAL;

	CM_SetFieldLONG(msg, eTimestamp, time(NULL));
	CM_SetField(msg, eAuthor, CCC->room.QRname, strlen(CCC->room.QRname));
	len = snprintf(buf, sizeof buf, "[%s]", CCC->room.QRname);
	CM_SetField(msg, eMsgSubject, buf, len);

	CM_SetField(msg, erFc822Addr, SKEY(sc->Users[roommailalias]));
	CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias]));

	/* Set the 'List-ID' header */
	CM_SetField(msg, eListID, SKEY(sc->ListID));

	/*
	 * Go fetch the contents of the digest
	 */
	fseek(sc->digestfp, 0L, SEEK_END);
	msglen = ftell(sc->digestfp);

	pbuf = malloc(msglen + 1);
	fseek(sc->digestfp, 0L, SEEK_SET);
	fread(pbuf, (size_t)msglen, 1, sc->digestfp);
	pbuf[msglen] = '\0';
	CM_SetAsField(msg, eMesageText, &pbuf, msglen);

	/* Now generate the delivery instructions */

	/* Where do we want bounces and other noise to be heard?
	 * Surely not the list members! */
	snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", config.c_fqdn);

	/* Now submit the message */
	valid = validate_recipients(ChrPtr(sc->Users[digestrecp]), NULL, 0);
	if (valid != NULL) {
		valid->bounce_to = strdup(bounce_to);
		valid->envelope_from = strdup(bounce_to);
		CtdlSubmitMsg(msg, valid, NULL, 0);
	}
	CM_Free(msg);
	free_recipients(valid);
}
Exemplo n.º 2
0
void network_process_participate(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
{
	struct CtdlMessage *msg = NULL;
	int ok_to_participate = 0;
	StrBuf *Buf = NULL;
	recptypes *valid;

	/*
	 * Process client-side list participations for this room
	 */
	if (sc->Users[participate] == NULL)
		return;

	msg = CM_Duplicate(omsg);

	/* Only send messages which originated on our own
	 * Citadel network, otherwise we'll end up sending the
	 * remote mailing list's messages back to it, which
	 * is rude...
	 */
	ok_to_participate = 0;
	if (!CM_IsEmpty(msg, eNodeName)) {
		if (!strcasecmp(msg->cm_fields[eNodeName],
				config.c_nodename)) {
			ok_to_participate = 1;
		}
		
		Buf = NewStrBufPlain(CM_KEY(msg, eNodeName));
		if (CtdlIsValidNode(NULL,
				    NULL,
				    Buf,
				    sc->working_ignetcfg,
				    sc->the_netmap) == 0)
		{
			ok_to_participate = 1;
		}
	}
	if (ok_to_participate)
	{
		/* Replace the Internet email address of the
		 * actual author with the email address of the
		 * room itself, so the remote listserv doesn't
		 * reject us.
		 */
		CM_SetField(msg, erFc822Addr, SKEY(sc->Users[roommailalias]));

		valid = validate_recipients(ChrPtr(sc->Users[participate]) , NULL, 0);

		CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias]));
		CtdlSubmitMsg(msg, valid, "", 0);
		free_recipients(valid);
	}
	FreeStrBuf(&Buf);
	CM_Free(msg);
}
Exemplo n.º 3
0
/*
 * Pre-save hook for saving a message in a blog room.
 * (Do we want to only do this for top-level messages?)
 */
int blog_upload_beforesave(struct CtdlMessage *msg, recptypes *recp) {

	/* Only run this hook for blog rooms */
	if (CC->room.QRdefaultview != VIEW_BLOG) {
		return(0);
	}

	/* 
	 * If the message doesn't have an EUID, give it one.
	 */
	if (CM_IsEmpty(msg, eExclusiveID))
	{
		char uuid[SIZ];
		generate_uuid(uuid);
		CM_SetField(msg, eExclusiveID, uuid, strlen(uuid));
	}

	/*
	 * We also want to define a maximum length, whether we generated it or not.
	 */
	CM_CutFieldAt(msg, eExclusiveID, BLOG_EUIDBUF_SIZE - 1);
	
	/* Now allow the save to complete. */
	return(0);
}
Exemplo n.º 4
0
void network_process_list(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
{
	struct CtdlMessage *msg = NULL;

	/*
	 * Process mailing list recipients
	 */
	if (sc->Users[listrecp] == NULL)
		return;

	/* create our own copy of the message.
	 *  We're going to need to modify it
	 * in order to insert the [list name] in it, etc.
	 */

	msg = CM_Duplicate(omsg);


	CM_SetField(msg, eReplyTo, SKEY(sc->Users[roommailalias]));

	/* if there is no other recipient, Set the recipient
	 * of the list message to the email address of the
	 * room itself.
	 */
	if (CM_IsEmpty(msg, eRecipient))
	{
		CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias]));
	}

	/* Set the 'List-ID' header */
	CM_SetField(msg, eListID, SKEY(sc->ListID));


	/* Prepend "[List name]" to the subject */
	ListCalculateSubject(msg);

	/* Handle delivery */
	network_deliver_list(msg, sc, CC->room.QRname);
	CM_Free(msg);
}
Exemplo n.º 5
0
/*
 * Called by JournalRunQueue() to send an individual message.
 */
void JournalRunQueueMsg(struct jnlq *jmsg) {

	struct CtdlMessage *journal_msg = NULL;
	recptypes *journal_recps = NULL;
	StrBuf *message_text = NULL;
	char mime_boundary[256];
	long mblen;
	long rfc822len;
	char recipient[256];
	char inetemail[256];
	static int seq = 0;
	int i;

	if (jmsg == NULL)
		return;
	journal_recps = validate_recipients(config.c_journal_dest, NULL, 0);
	if (journal_recps != NULL) {

		if (  (journal_recps->num_local > 0)
		   || (journal_recps->num_internet > 0)
		   || (journal_recps->num_ignet > 0)
		   || (journal_recps->num_room > 0)
		) {

			/*
			 * Construct journal message.
			 * Note that we are transferring ownership of some of the memory here.
			 */
			journal_msg = malloc(sizeof(struct CtdlMessage));
			memset(journal_msg, 0, sizeof(struct CtdlMessage));
			journal_msg->cm_magic = CTDLMESSAGE_MAGIC;
			journal_msg->cm_anon_type = MES_NORMAL;
			journal_msg->cm_format_type = FMT_RFC822;
			CM_SetField(journal_msg, eJournal, HKEY("is journal"));
			CM_SetField(journal_msg, eAuthor, jmsg->from, strlen(jmsg->from));
			CM_SetField(journal_msg, eNodeName, jmsg->node, strlen(jmsg->node));
			CM_SetField(journal_msg, erFc822Addr, jmsg->rfca, strlen(jmsg->rfca));
			CM_SetField(journal_msg, eMsgSubject, jmsg->subj, strlen(jmsg->subj));

			mblen = snprintf(mime_boundary, sizeof(mime_boundary),
					 "--Citadel-Journal-%08lx-%04x--", time(NULL), ++seq);
			rfc822len = strlen(jmsg->rfc822);
		       
			message_text = NewStrBufPlain(NULL, rfc822len + sizeof(recptypes) + 1024);

			/*
			 * Here is where we begin to compose the journalized message.
			 * NOTE: the superfluous "Content-Identifer: ExJournalReport" header was
			 *       requested by a paying customer, and yes, it is intentionally
			 *       spelled wrong.  Do NOT remove or change it.
			 */
			StrBufAppendBufPlain(
				message_text, 
				HKEY("Content-type: multipart/mixed; boundary=\""), 0);

			StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);

			StrBufAppendBufPlain(
				message_text, 
				HKEY("\"\r\n"
				     "Content-Identifer: ExJournalReport\r\n"
				     "MIME-Version: 1.0\r\n"
				     "\n"
				     "--"), 0);

			StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);

			StrBufAppendBufPlain(
				message_text, 
				HKEY("\r\n"
				     "Content-type: text/plain\r\n"
				     "\r\n"
				     "Sender: "), 0);

			if (CM_IsEmpty(journal_msg, eAuthor))
				StrBufAppendBufPlain(
					message_text, 
					journal_msg->cm_fields[eAuthor], -1, 0);
			else
				StrBufAppendBufPlain(
					message_text, 
					HKEY("(null)"), 0);

			if (!CM_IsEmpty(journal_msg, erFc822Addr)) {
				StrBufAppendPrintf(message_text, " <%s>",
						   journal_msg->cm_fields[erFc822Addr]);
			}
			else if (!CM_IsEmpty(journal_msg, eNodeName)) {
				StrBufAppendPrintf(message_text, " @ %s",
						   journal_msg->cm_fields[eNodeName]);
			}
			else
				StrBufAppendBufPlain(
					message_text, 
					HKEY(" "), 0);

			StrBufAppendBufPlain(
				message_text, 
				HKEY("\r\n"
				     "Message-ID: <"), 0);

			StrBufAppendBufPlain(message_text, jmsg->msgn, -1, 0);
			StrBufAppendBufPlain(
				message_text, 
				HKEY(">\r\n"
				     "Recipients:\r\n"), 0);

			if (jmsg->recps.num_local > 0) {
				for (i=0; i<jmsg->recps.num_local; ++i) {
					extract_token(recipient, jmsg->recps.recp_local,
							i, '|', sizeof recipient);
					local_to_inetemail(inetemail, recipient, sizeof inetemail);
					StrBufAppendPrintf(message_text, 
							   "	%s <%s>\r\n", recipient, inetemail);
				}
			}

			if (jmsg->recps.num_ignet > 0) {
				for (i=0; i<jmsg->recps.num_ignet; ++i) {
					extract_token(recipient, jmsg->recps.recp_ignet,
							i, '|', sizeof recipient);
					StrBufAppendPrintf(message_text, 
							   "	%s\r\n", recipient);
				}
			}

			if (jmsg->recps.num_internet > 0) {
				for (i=0; i<jmsg->recps.num_internet; ++i) {
					extract_token(recipient, jmsg->recps.recp_internet,
							i, '|', sizeof recipient);
					StrBufAppendPrintf(message_text, 
						"	%s\r\n", recipient);
				}
			}

			StrBufAppendBufPlain(
				message_text, 
				HKEY("\r\n"
				     "--"), 0);

			StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);

			StrBufAppendBufPlain(
				message_text, 
				HKEY("\r\n"
				     "Content-type: message/rfc822\r\n"
				     "\r\n"), 0);

			StrBufAppendBufPlain(message_text, jmsg->rfc822, rfc822len, 0);

			StrBufAppendBufPlain(
				message_text, 
				HKEY("--"), 0);

			StrBufAppendBufPlain(message_text, mime_boundary, mblen, 0);

			StrBufAppendBufPlain(
				message_text, 
				HKEY("--\r\n"), 0);

			CM_SetAsFieldSB(journal_msg, eMesageText, &message_text);
			free(jmsg->rfc822);
			free(jmsg->msgn);
			jmsg->rfc822 = NULL;
			jmsg->msgn = NULL;
			
			/* Submit journal message */
			CtdlSubmitMsg(journal_msg, journal_recps, "", 0);
			CM_Free(journal_msg);
		}

		free_recipients(journal_recps);
	}

	/* We are responsible for freeing this memory. */
	free(jmsg);
}
Exemplo n.º 6
0
/*
 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
 * instructions for "5" codes (permanent fatal errors) and produce/deliver
 * a "bounce" message (delivery status notification).
 */
void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
{
	int i;
	int lines;
	int status;
	char buf[1024];
	char key[1024];
	char addr[1024];
	char dsn[1024];
	char bounceto[1024];
	StrBuf *boundary;
	int num_bounces = 0;
	int bounce_this = 0;
	time_t submitted = 0L;
	struct CtdlMessage *bmsg = NULL;
	int give_up = 0;
	recptypes *valid;
	int successful_bounce = 0;
	static int seq = 0;
	StrBuf *BounceMB;
	long omsgid = (-1);

	syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
	strcpy(bounceto, "");
	boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));

	StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq);

	lines = num_tokens(instr, '\n');

	/* See if it's time to give up on delivery of this message */
	for (i=0; i<lines; ++i) {
		extract_token(buf, instr, i, '\n', sizeof buf);
		extract_token(key, buf, 0, '|', sizeof key);
		extract_token(addr, buf, 1, '|', sizeof addr);
		if (!strcasecmp(key, "submitted")) {
			submitted = atol(addr);
		}
	}

	if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
		give_up = 1;
	}

	/* Start building our bounce message */

	bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
	if (bmsg == NULL) return;
	memset(bmsg, 0, sizeof(struct CtdlMessage));
	BounceMB = NewStrBufPlain(NULL, 1024);

	bmsg->cm_magic = CTDLMESSAGE_MAGIC;
	bmsg->cm_anon_type = MES_NORMAL;
	bmsg->cm_format_type = FMT_RFC822;
	CM_SetField(bmsg, eAuthor, HKEY("Citadel"));
	CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM));
	CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename")));
	CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)"));
	StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
	StrBufAppendBuf(BounceMB, boundary, 0);
	StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);

	StrBufAppendBufPlain(
		BounceMB,
		HKEY("\r\nThis is a multipart message in MIME format."
		     "\r\n\r\n"), 0);

	StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
	StrBufAppendBuf(BounceMB, boundary, 0);
	StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
	StrBufAppendBufPlain(BounceMB,
			     HKEY("Content-type: text/plain\r\n\r\n"), 0);

	if (give_up)
	{
		StrBufAppendBufPlain(
			BounceMB,
			HKEY("A message you sent could not be delivered "
			     "to some or all of its recipients\ndue to "
			     "prolonged unavailability of its destination(s).\n"
			     "Giving up on the following addresses:\n\n"), 0);
	}
	else
	{
		StrBufAppendBufPlain(
			BounceMB,
			HKEY("A message you sent could not be delivered "
			     "to some or all of its recipients.\n"
			     "The following addresses were undeliverable:\n\n"
				), 0);
	}

	/*
	 * Now go through the instructions checking for stuff.
	 */
	for (i=0; i<lines; ++i) {
		long addrlen;
		long dsnlen;
		extract_token(buf, instr, i, '\n', sizeof buf);
		extract_token(key, buf, 0, '|', sizeof key);
		addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
		status = extract_int(buf, 2);
		dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
		bounce_this = 0;

		syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
		       key, addr, status, dsn);

		if (!strcasecmp(key, "bounceto")) {
			strcpy(bounceto, addr);
		}

		if (!strcasecmp(key, "msgid")) {
			omsgid = atol(addr);
		}

		if (!strcasecmp(key, "remote")) {
			if (status == 5) bounce_this = 1;
			if (give_up) bounce_this = 1;
		}

		if (bounce_this) {
			++num_bounces;

			StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
			StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
			StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
			StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);

			remove_token(instr, i, '\n');
			--i;
			--lines;
		}
	}

	/* Attach the original message */
	if (omsgid >= 0) {
		StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
		StrBufAppendBuf(BounceMB, boundary, 0);
		StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);

		StrBufAppendBufPlain(
			BounceMB,
			HKEY("Content-type: message/rfc822\r\n"), 0);

		StrBufAppendBufPlain(
			BounceMB,
			HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);

		StrBufAppendBufPlain(
			BounceMB,
			HKEY("Content-Disposition: inline\r\n"), 0);

		StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);

		if (OMsgTxt == NULL) {
			CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
			CtdlOutputMsg(omsgid,
				      MT_RFC822,
				      HEADERS_ALL,
				      0, 1, NULL, 0,
				      NULL, NULL, NULL);

			StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
			FreeStrBuf(&CC->redirect_buffer);
		}
		else {
			StrBufAppendBuf(BounceMB, OMsgTxt, 0);
		}
	}

	/* Close the multipart MIME scope */
	StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
	StrBufAppendBuf(BounceMB, boundary, 0);
	StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
	CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB);

	/* Deliver the bounce if there's anything worth mentioning */
	syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
	if (num_bounces > 0) {

		/* First try the user who sent the message */
		if (IsEmptyStr(bounceto))
			syslog(LOG_ERR, "No bounce address specified\n");
		else
			syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto);
		/* Can we deliver the bounce to the original sender? */
		valid = validate_recipients(bounceto,
					    smtp_get_Recipients (),
					    0);
		if (valid != NULL) {
			if (valid->num_error == 0) {
				CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
				successful_bounce = 1;
			}
		}

		/* If not, post it in the Aide> room */
		if (successful_bounce == 0) {
			CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR);
		}

		/* Free up the memory we used */
		if (valid != NULL) {
			free_recipients(valid);
		}
	}
	FreeStrBuf(&boundary);
	CM_Free(bmsg);
	syslog(LOG_DEBUG, "Done processing bounces\n");
}
Exemplo n.º 7
0
/*
 * Before allowing a wiki page save to execute, we have to perform version control.
 * This involves fetching the old version of the page if it exists.
 */
int wiki_upload_beforesave(struct CtdlMessage *msg, recptypes *recp) {
	struct CitContext *CCC = CC;
	long old_msgnum = (-1L);
	struct CtdlMessage *old_msg = NULL;
	long history_msgnum = (-1L);
	struct CtdlMessage *history_msg = NULL;
	char diff_old_filename[PATH_MAX];
	char diff_new_filename[PATH_MAX];
	char diff_out_filename[PATH_MAX];
	char diff_cmd[PATH_MAX];
	FILE *fp;
	int rv;
	char history_page[1024];
	long history_page_len;
	char boundary[256];
	char prefixed_boundary[258];
	char buf[1024];
	char *diffbuf = NULL;
	size_t diffbuf_len = 0;
	char *ptr = NULL;
	long newmsgid;
	StrBuf *msgidbuf;

	if (!CCC->logged_in) return(0);	/* Only do this if logged in. */

	/* Is this a room with a Wiki in it, don't run this hook. */
	if ((CCC->room.QRdefaultview != VIEW_WIKI) &&
	    (CCC->room.QRdefaultview != VIEW_WIKIMD)) {
		return(0);
	}

	/* If this isn't a MIME message, don't bother. */
	if (msg->cm_format_type != 4) return(0);

	/* If there's no EUID we can't do this.  Reject the post. */
	if (CM_IsEmpty(msg, eExclusiveID)) return(1);

	newmsgid = get_new_message_number();
	msgidbuf = NewStrBuf();
	StrBufPrintf(msgidbuf, "%08lX-%08lX@%s/%s",
		     (long unsigned int) time(NULL),
		     (long unsigned int) newmsgid,
		     CtdlGetConfigStr("c_fqdn"),
		     msg->cm_fields[eExclusiveID]
		);

	CM_SetAsFieldSB(msg, emessageId, &msgidbuf);

	history_page_len = snprintf(history_page, sizeof history_page,
				    "%s_HISTORY_", msg->cm_fields[eExclusiveID]);

	/* Make sure we're saving a real wiki page rather than a wiki history page.
	 * This is important in order to avoid recursing infinitely into this hook.
	 */
	if (	(msg->cm_lengths[eExclusiveID] >= 9)
		&& (!strcasecmp(&msg->cm_fields[eExclusiveID][msg->cm_lengths[eExclusiveID]-9], "_HISTORY_"))
	) {
		syslog(LOG_DEBUG, "History page not being historied\n");
		return(0);
	}

	/* If there's no message text, obviously this is all b0rken and shouldn't happen at all */
	if (CM_IsEmpty(msg, eMesageText)) return(0);

	/* Set the message subject identical to the page name */
	CM_CopyField(msg, eMsgSubject, eExclusiveID);

	/* See if we can retrieve the previous version. */
	old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields[eExclusiveID], &CCC->room);
	if (old_msgnum > 0L) {
		old_msg = CtdlFetchMessage(old_msgnum, 1, 1);
	}
	else {
		old_msg = NULL;
	}

	if ((old_msg != NULL) && (CM_IsEmpty(old_msg, eMesageText))) {	/* old version is corrupt? */
		CM_Free(old_msg);
		old_msg = NULL;
	}
	
	/* If no changes were made, don't bother saving it again */
	if ((old_msg != NULL) && (!strcmp(msg->cm_fields[eMesageText], old_msg->cm_fields[eMesageText]))) {
		CM_Free(old_msg);
		return(1);
	}

	/*
	 * Generate diffs
	 */
	CtdlMakeTempFileName(diff_old_filename, sizeof diff_old_filename);
	CtdlMakeTempFileName(diff_new_filename, sizeof diff_new_filename);
	CtdlMakeTempFileName(diff_out_filename, sizeof diff_out_filename);

	if (old_msg != NULL) {
		fp = fopen(diff_old_filename, "w");
		rv = fwrite(old_msg->cm_fields[eMesageText], old_msg->cm_lengths[eMesageText], 1, fp);
		fclose(fp);
		CM_Free(old_msg);
	}

	fp = fopen(diff_new_filename, "w");
	rv = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp);
	fclose(fp);

	snprintf(diff_cmd, sizeof diff_cmd,
		DIFF " -u %s %s >%s",
		diff_new_filename,
		((old_msg != NULL) ? diff_old_filename : "/dev/null"),
		diff_out_filename
	);
	syslog(LOG_DEBUG, "diff cmd: %s", diff_cmd);
	rv = system(diff_cmd);
	syslog(LOG_DEBUG, "diff cmd returned %d", rv);

	diffbuf_len = 0;
	diffbuf = NULL;
	fp = fopen(diff_out_filename, "r");
	if (fp == NULL) {
		fp = fopen("/dev/null", "r");
	}
	if (fp != NULL) {
		fseek(fp, 0L, SEEK_END);
		diffbuf_len = ftell(fp);
		fseek(fp, 0L, SEEK_SET);
		diffbuf = malloc(diffbuf_len + 1);
		fread(diffbuf, diffbuf_len, 1, fp);
		diffbuf[diffbuf_len] = '\0';
		fclose(fp);
	}

	syslog(LOG_DEBUG, "diff length is "SIZE_T_FMT" bytes", diffbuf_len);

	unlink(diff_old_filename);
	unlink(diff_new_filename);
	unlink(diff_out_filename);

	/* Determine whether this was a bogus (empty) edit */
	if ((diffbuf_len = 0) && (diffbuf != NULL)) {
		free(diffbuf);
		diffbuf = NULL;
	}
	if (diffbuf == NULL) {
		return(1);		/* No changes at all?  Abandon the post entirely! */
	}

	/* Now look for the existing edit history */

	history_msgnum = CtdlLocateMessageByEuid(history_page, &CCC->room);
	history_msg = NULL;
	if (history_msgnum > 0L) {
		history_msg = CtdlFetchMessage(history_msgnum, 1, 1);
	}

	/* Create a new history message if necessary */
	if (history_msg == NULL) {
		char *buf;
		long len;

		history_msg = malloc(sizeof(struct CtdlMessage));
		memset(history_msg, 0, sizeof(struct CtdlMessage));
		history_msg->cm_magic = CTDLMESSAGE_MAGIC;
		history_msg->cm_anon_type = MES_NORMAL;
		history_msg->cm_format_type = FMT_RFC822;
		CM_SetField(history_msg, eAuthor, HKEY("Citadel"));
		if (!IsEmptyStr(CCC->room.QRname)){
			CM_SetField(history_msg, eRecipient, CCC->room.QRname, strlen(CCC->room.QRname));
		}
		CM_SetField(history_msg, eExclusiveID, history_page, history_page_len);
		CM_SetField(history_msg, eMsgSubject, history_page, history_page_len);
		CM_SetField(history_msg, eSuppressIdx, HKEY("1")); /* suppress full text indexing */
		snprintf(boundary, sizeof boundary, "Citadel--Multipart--%04x--%08lx", getpid(), time(NULL));
		buf = (char*) malloc(1024);
		len = snprintf(buf, 1024,
			       "Content-type: multipart/mixed; boundary=\"%s\"\n\n"
			       "This is a Citadel wiki history encoded as multipart MIME.\n"
			       "Each part is comprised of a diff script representing one change set.\n"
			       "\n"
			       "--%s--\n",
			       boundary, boundary
		);
		CM_SetAsField(history_msg, eMesageText, &buf, len);
	}

	/* Update the history message (regardless of whether it's new or existing) */

	/* Remove the Message-ID from the old version of the history message.  This will cause a brand
	 * new one to be generated, avoiding an uninitentional hit of the loop zapper when we replicate.
	 */
	CM_FlushField(history_msg, emessageId);

	/* Figure out the boundary string.  We do this even when we generated the
	 * boundary string in the above code, just to be safe and consistent.
	 */
	*boundary = '\0';

	ptr = history_msg->cm_fields[eMesageText];
	do {
		ptr = memreadline(ptr, buf, sizeof buf);
		if (*ptr != 0) {
			striplt(buf);
			if (!IsEmptyStr(buf) && (!strncasecmp(buf, "Content-type:", 13))) {
				if (
					(bmstrcasestr(buf, "multipart") != NULL)
					&& (bmstrcasestr(buf, "boundary=") != NULL)
				) {
					safestrncpy(boundary, bmstrcasestr(buf, "\""), sizeof boundary);
					char *qu;
					qu = strchr(boundary, '\"');
					if (qu) {
						strcpy(boundary, ++qu);
					}
					qu = strchr(boundary, '\"');
					if (qu) {
						*qu = 0;
					}
				}
			}
		}
	} while ( (IsEmptyStr(boundary)) && (*ptr != 0) );

	/*
	 * Now look for the first boundary.  That is where we need to insert our fun.
	 */
	if (!IsEmptyStr(boundary)) {
		char *MsgText;
		long MsgTextLen;
		time_t Now = time(NULL);

		snprintf(prefixed_boundary, sizeof(prefixed_boundary), "--%s", boundary);
		
		CM_GetAsField(history_msg, eMesageText, &MsgText, &MsgTextLen);

		ptr = bmstrcasestr(MsgText, prefixed_boundary);
		if (ptr != NULL) {
			StrBuf *NewMsgText;
			char uuid[64];
			char memo[512];
			long memolen;
			char encoded_memo[1024];
			
			NewMsgText = NewStrBufPlain(NULL, MsgTextLen + diffbuf_len + 1024);

			generate_uuid(uuid);
			memolen = snprintf(memo, sizeof(memo), "%s|%ld|%s|%s", 
					   uuid,
					   Now,
					   CCC->user.fullname,
					   CtdlGetConfigStr("c_nodename"));

			memolen = CtdlEncodeBase64(encoded_memo, memo, memolen, 0);

			StrBufAppendBufPlain(NewMsgText, HKEY("--"), 0);
			StrBufAppendBufPlain(NewMsgText, boundary, -1, 0);
			StrBufAppendBufPlain(
				NewMsgText, 
				HKEY("\n"
				     "Content-type: text/plain\n"
				     "Content-Disposition: inline; filename=\""), 0);

			StrBufAppendBufPlain(NewMsgText, encoded_memo, memolen, 0);

			StrBufAppendBufPlain(
				NewMsgText, 
				HKEY("\"\n"
				     "Content-Transfer-Encoding: 8bit\n"
				     "\n"), 0);

			StrBufAppendBufPlain(NewMsgText, diffbuf, diffbuf_len, 0);
			StrBufAppendBufPlain(NewMsgText, HKEY("\n"), 0);

			StrBufAppendBufPlain(NewMsgText, ptr, MsgTextLen - (ptr - MsgText), 0);
			free(MsgText);
			CM_SetAsFieldSB(history_msg, eMesageText, &NewMsgText); 
		}
		else
		{
			CM_SetAsField(history_msg, eMesageText, &MsgText, MsgTextLen); 
		}

		CM_SetFieldLONG(history_msg, eTimestamp, Now);
	
		CtdlSubmitMsg(history_msg, NULL, "", 0);
	}
	else {
		syslog(LOG_ALERT, "Empty boundary string in history message.  No history!\n");
	}

	free(diffbuf);
	CM_Free(history_msg);
	return(0);
}
Exemplo n.º 8
0
/*
 * Fetch a specific revision of a wiki page.  The "operation" string may be set to "fetch" in order
 * to simply fetch the desired revision and store it in a temporary location for viewing, or "revert"
 * to revert the currently active page to that revision.
 */
void wiki_rev(char *pagename, char *rev, char *operation)
{
	struct CitContext *CCC = CC;
	int r;
	char history_page_name[270];
	long msgnum;
	char temp[PATH_MAX];
	struct CtdlMessage *msg;
	FILE *fp;
	struct HistoryEraserCallBackData hecbd;
	long len = 0L;
	int rv;

	r = CtdlDoIHavePermissionToReadMessagesInThisRoom();
	if (r != om_ok) {
		if (r == om_not_logged_in) {
			cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
		}
		else {
			cprintf("%d An unknown error has occurred.\n", ERROR);
		}
		return;
	}

	if (!strcasecmp(operation, "revert")) {
		r = CtdlDoIHavePermissionToPostInThisRoom(temp, sizeof temp, NULL, POST_LOGGED_IN, 0);
		if (r != 0) {
			cprintf("%d %s\n", r, temp);
			return;
		}
	}

	/* Begin by fetching the current version of the page.  We're going to patch
	 * backwards through the diffs until we get the one we want.
	 */
	msgnum = CtdlLocateMessageByEuid(pagename, &CCC->room);
	if (msgnum > 0L) {
		msg = CtdlFetchMessage(msgnum, 1, 1);
	}
	else {
		msg = NULL;
	}

	if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) {
		CM_Free(msg);
		msg = NULL;
	}

	if (msg == NULL) {
		cprintf("%d Page '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename);
		return;
	}

	/* Output it to a temporary file */

	CtdlMakeTempFileName(temp, sizeof temp);
	fp = fopen(temp, "w");
	if (fp != NULL) {
		r = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp);
		fclose(fp);
	}
	else {
		syslog(LOG_ALERT, "Cannot open %s: %s\n", temp, strerror(errno));
	}
	CM_Free(msg);

	/* Get the revision history */

	snprintf(history_page_name, sizeof history_page_name, "%s_HISTORY_", pagename);
	msgnum = CtdlLocateMessageByEuid(history_page_name, &CCC->room);
	if (msgnum > 0L) {
		msg = CtdlFetchMessage(msgnum, 1, 1);
	}
	else {
		msg = NULL;
	}

	if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) {
		CM_Free(msg);
		msg = NULL;
	}

	if (msg == NULL) {
		cprintf("%d Revision history for '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename);
		return;
	}

	/* Start patching backwards (newest to oldest) through the revision history until we
	 * hit the revision uuid requested by the user.  (The callback will perform each one.)
	 */

	memset(&hecbd, 0, sizeof(struct HistoryEraserCallBackData));
	hecbd.tempfilename = temp;
	hecbd.stop_when = rev;
	striplt(hecbd.stop_when);

	mime_parser(CM_RANGE(msg, eMesageText), *wiki_rev_callback, NULL, NULL, (void *)&hecbd, 0);
	CM_Free(msg);

	/* Were we successful? */
	if (hecbd.done == 0) {
		cprintf("%d Revision '%s' of page '%s' was not found.\n",
			ERROR + MESSAGE_NOT_FOUND, rev, pagename
		);
	}

	/* We have the desired revision on disk.  Now do something with it. */

	else if ( (!strcasecmp(operation, "fetch")) || (!strcasecmp(operation, "revert")) ) {
		msg = malloc(sizeof(struct CtdlMessage));
		memset(msg, 0, sizeof(struct CtdlMessage));
		msg->cm_magic = CTDLMESSAGE_MAGIC;
		msg->cm_anon_type = MES_NORMAL;
		msg->cm_format_type = FMT_RFC822;
		fp = fopen(temp, "r");
		if (fp) {
			char *msgbuf;
			fseek(fp, 0L, SEEK_END);
			len = ftell(fp);
			fseek(fp, 0L, SEEK_SET);
			msgbuf = malloc(len + 1);
			rv = fread(msgbuf, len, 1, fp);
			syslog(LOG_DEBUG, "did %d blocks of %ld bytes\n", rv, len);
			msgbuf[len] = '\0';
			CM_SetAsField(msg, eMesageText, &msgbuf, len);
			fclose(fp);
		}
		if (len <= 0) {
			msgnum = (-1L);
		}
		else if (!strcasecmp(operation, "fetch")) {
			CM_SetField(msg, eAuthor, HKEY("Citadel"));
			CtdlCreateRoom(wwm, 5, "", 0, 1, 1, VIEW_BBS);	/* Not an error if already exists */
			msgnum = CtdlSubmitMsg(msg, NULL, wwm, 0);	/* Store the revision here */

			/*
			 * WARNING: VILE SLEAZY HACK
			 * This will avoid the 'message xxx is not in this room' security error,
			 * but only if the client fetches the message we just generated immediately
			 * without first trying to perform other fetch operations.
			 */
			if (CCC->cached_msglist != NULL) {
				free(CCC->cached_msglist);
				CCC->cached_msglist = NULL;
				CCC->cached_num_msgs = 0;
			}
			CCC->cached_msglist = malloc(sizeof(long));
			if (CCC->cached_msglist != NULL) {
				CCC->cached_num_msgs = 1;
				CCC->cached_msglist[0] = msgnum;
			}

		}
		else if (!strcasecmp(operation, "revert")) {
			CM_SetFieldLONG(msg, eTimestamp, time(NULL));
			if (!IsEmptyStr(CCC->user.fullname)) {
				CM_SetField(msg, eAuthor, CCC->user.fullname, strlen(CCC->user.fullname));
			}

			if (!IsEmptyStr(CCC->cs_inet_email)) {
				CM_SetField(msg, erFc822Addr, CCC->cs_inet_email, strlen(CCC->cs_inet_email));
			}

			if (!IsEmptyStr(CCC->room.QRname)) {
				CM_SetField(msg, eOriginalRoom, CCC->room.QRname, strlen(CCC->room.QRname));
			}

			CM_SetField(msg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename")));
			
			if (!IsEmptyStr(pagename)) {
				CM_SetField(msg, eExclusiveID, pagename, strlen(pagename));
			}
			msgnum = CtdlSubmitMsg(msg, NULL, "", 0);	/* Replace the current revision */
		}
		else {
			/* Theoretically it is impossible to get here, but throw an error anyway */
			msgnum = (-1L);
		}
		CM_Free(msg);
		if (msgnum >= 0L) {
			cprintf("%d %ld\n", CIT_OK, msgnum);		/* Give the client a msgnum */
		}
		else {
			cprintf("%d Error %ld has occurred.\n", ERROR+INTERNAL_ERROR, msgnum);
		}
	}

	/* We did all this work for nothing.  Express anguish to the caller. */
	else {
		cprintf("%d An unknown operation was requested.\n", ERROR+CMD_NOT_SUPPORTED);
	}

	unlink(temp);
	return;
}
Exemplo n.º 9
0
void network_process_ignetpush(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
{
	StrBuf *Recipient;
	StrBuf *RemoteRoom;
	const char *Pos = NULL;
	struct CtdlMessage *msg = NULL;
	struct CitContext *CCC = CC;
	struct ser_ret sermsg;
	char buf[SIZ];
	char filename[PATH_MAX];
	FILE *fp;
	StrBuf *Buf = NULL;
	int i;
	int bang = 0;
	int send = 1;

	if (sc->Users[ignet_push_share] == NULL)
		return;

	/*
	 * Process IGnet push shares
	 */
	msg = CM_Duplicate(omsg);

	/* Prepend our node name to the Path field whenever
	 * sending a message to another IGnet node
	 */
	Netmap_AddMe(msg, HKEY("username"));

	/*
	 * Determine if this message is set to be deleted
	 * after sending out on the network
	 */
	if (!CM_IsEmpty(msg, eSpecialField)) {
		if (!strcasecmp(msg->cm_fields[eSpecialField], "CANCEL")) {
			*delete_after_send = 1;
		}
	}

	/* Now send it to every node */
	Recipient = NewStrBufPlain(NULL, StrLength(sc->Users[ignet_push_share]));
	RemoteRoom = NewStrBufPlain(NULL, StrLength(sc->Users[ignet_push_share]));
	while ((Pos != StrBufNOTNULL) &&
	       StrBufExtract_NextToken(Recipient, sc->Users[ignet_push_share], &Pos, ','))
	{
		StrBufExtract_NextToken(RemoteRoom, sc->Users[ignet_push_share], &Pos, ',');
		send = 1;
		NewStrBufDupAppendFlush(&Buf, Recipient, NULL, 1);
			
		/* Check for valid node name */
		if (CtdlIsValidNode(NULL,
				    NULL,
				    Buf,
				    sc->working_ignetcfg,
				    sc->the_netmap) != 0)
		{
			QN_syslog(LOG_ERR,
				  "Invalid node <%s>\n",
				  ChrPtr(Recipient));
			
			send = 0;
		}
		
		/* Check for split horizon */
		QN_syslog(LOG_DEBUG, "Path is %s\n", msg->cm_fields[eMessagePath]);
		bang = num_tokens(msg->cm_fields[eMessagePath], '!');
		if (bang > 1) {
			for (i=0; i<(bang-1); ++i) {
				extract_token(buf,
					      msg->cm_fields[eMessagePath],
					      i, '!',
					      sizeof buf);

				QN_syslog(LOG_DEBUG, "Compare <%s> to <%s>\n",
					  buf, ChrPtr(Recipient)) ;
				if (!strcasecmp(buf, ChrPtr(Recipient))) {
					send = 0;
					break;
				}
			}
			
			QN_syslog(LOG_INFO,
				  " %sSending to %s\n",
				  (send)?"":"Not ",
				  ChrPtr(Recipient));
		}
		
		/* Send the message */
		if (send == 1)
		{
			/*
			 * Force the message to appear in the correct
			 * room on the far end by setting the C field
			 * correctly
			 */
			if (StrLength(RemoteRoom) > 0) {
				CM_SetField(msg, eRemoteRoom, SKEY(RemoteRoom));
			}
			else {
				CM_SetField(msg, eRemoteRoom, CCC->room.QRname, strlen(CCC->room.QRname));
			}
			
			/* serialize it for transmission */
			CtdlSerializeMessage(&sermsg, msg);
			if (sermsg.len > 0) {
				
				/* write it to a spool file */
				snprintf(filename,
					 sizeof(filename),
					 "%s/%s@%lx%x",
					 ctdl_netout_dir,
					 ChrPtr(Recipient),
					 time(NULL),
					 rand()
					);
					
				QN_syslog(LOG_DEBUG,
					  "Appending to %s\n",
					  filename);
				
				fp = fopen(filename, "ab");
				if (fp != NULL) {
					fwrite(sermsg.ser,
					       sermsg.len, 1, fp);
					fclose(fp);
				}
				else {
					QN_syslog(LOG_ERR,
						  "%s: %s\n",
						  filename,
						  strerror(errno));
				}

				/* free the serialized version */
				free(sermsg.ser);
			}
		}
	}
	FreeStrBuf(&Buf);
	FreeStrBuf(&Recipient);
	FreeStrBuf(&RemoteRoom);
	CM_Free(msg);
}
Exemplo n.º 10
0
/******************************************************************************
 * So, we're finished with sending (regardless of success or failure)         *
 * This Message might be referenced by several Queue-Items, if we're the last,*
 * we need to free the memory and send bounce messages (on terminal failure)  *
 * else we just free our SMTP-Message struct.                                 *
 ******************************************************************************/
eNextState FinalizeMessageSend_DB(AsyncIO *IO)
{
	const char *Status;
	SmtpOutMsg *Msg = IO->Data;
	StrBuf *StatusMessage;

	if (Msg->MyQEntry->AllStatusMessages != NULL)
		StatusMessage = Msg->MyQEntry->AllStatusMessages;
	else
		StatusMessage = Msg->MyQEntry->StatusMessage;


	if (Msg->MyQEntry->Status == 2) {
		SetSMTPState(IO, eSTMPfinished);
		Status = "Delivery successful.";
	}
	else if (Msg->MyQEntry->Status == 5) {
		SetSMTPState(IO, eSMTPFailTotal);
		Status = "Delivery failed permanently; giving up.";
	}
	else {
		SetSMTPState(IO, eSMTPFailTemporary);
		Status = "Delivery failed temporarily; will retry later.";
	}
			
	EVS_syslog(LOG_INFO,
		   "%s Time[%fs] Recipient <%s> @ <%s> (%s) Status message: %s\n",
		   Status,
		   Msg->IO.Now - Msg->IO.StartIO,
		   Msg->user,
		   Msg->node,
		   Msg->name,
		   ChrPtr(StatusMessage));


	Msg->IDestructQueItem = DecreaseQReference(Msg->MyQItem);

	Msg->nRemain = CountActiveQueueEntries(Msg->MyQItem, 0);

	if (Msg->MyQEntry->Active && 
	    !Msg->MyQEntry->StillActive &&
	    CheckQEntryIsBounce(Msg->MyQEntry))
	{
		/* are we casue for a bounce mail? */
		Msg->MyQItem->SendBounceMail |= (1<<Msg->MyQEntry->Status);
	}

	if ((Msg->nRemain > 0) || Msg->IDestructQueItem)
		Msg->QMsgData = SerializeQueueItem(Msg->MyQItem);
	else
		Msg->QMsgData = NULL;

	/*
	 * Uncompleted delivery instructions remain, so delete the old
	 * instructions and replace with the updated ones.
	 */
	EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
	CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, "");
	Msg->MyQItem->QueMsgID = -1;

	if (Msg->IDestructQueItem)
		smtpq_do_bounce(Msg->MyQItem, Msg->msgtext, Msg->pCurrRelay);

	if (Msg->nRemain > 0)
	{
		struct CtdlMessage *msg;
		msg = malloc(sizeof(struct CtdlMessage));
		memset(msg, 0, sizeof(struct CtdlMessage));
		msg->cm_magic = CTDLMESSAGE_MAGIC;
		msg->cm_anon_type = MES_NORMAL;
		msg->cm_format_type = FMT_RFC822;
		CM_SetAsFieldSB(msg, eMesageText, &Msg->QMsgData);
		CM_SetField(msg, eMsgSubject, HKEY("QMSG"));
		Msg->MyQItem->QueMsgID =
			CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
		EVS_syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
		CM_Free(msg);
	}
	else {
		CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM,
				   &Msg->MyQItem->MessageID,
				   1,
				   "");
		FreeStrBuf(&Msg->QMsgData);
	}

	RemoveContext(Msg->IO.CitContext);
	return eAbort;
}
Exemplo n.º 11
0
/*
 * Before-save hook searches for two different types of notes (legacy Kolab/Aethera notes
 * and modern vNote format notes) and does its best to learn the subject (summary)
 * and EUID (uid) of the note for Citadel's own nefarious purposes.
 */
int serv_notes_beforesave(struct CtdlMessage *msg, recptypes *recp)
{
	char *p;
	int a, i;
	char uuid[512];
	struct vnote *v = NULL;

	/* First determine if this room has the "notes" view set */

	if (CC->room.QRdefaultview != VIEW_NOTES) {
		return(0);			/* not notes; do nothing */
	}

	/* It must be an RFC822 message! */
	if (msg->cm_format_type != 4) {
		return(0);	/* You tried to save a non-RFC822 message! */
	}
	
	/*
	 * If we are in a "notes" view room, and the client has sent an RFC822
	 * message containing an X-KOrg-Note-Id: field (Aethera does this, as
	 * do some Kolab clients) then set both the Subject and the Exclusive ID
	 * of the message to that.  It's going to be a UUID so we want to replace
	 * any existing message containing that UUID.
	 */
	strcpy(uuid, "");
	p = msg->cm_fields[eMesageText];
	a = msg->cm_lengths[eMesageText];
	while (--a > 0) {
		if (!strncasecmp(p, "X-KOrg-Note-Id: ", 16)) {	/* Found it */
			safestrncpy(uuid, p + 16, sizeof(uuid));
			for (i = 0; uuid[i]; ++i) {
				if ( (uuid[i] == '\r') || (uuid[i] == '\n') ) {
					uuid[i] = 0;
					break;
				}
			}

			syslog(LOG_DEBUG, "UUID of note is: %s\n", uuid);
			if (!IsEmptyStr(uuid)) {
				CM_SetField(msg, eExclusiveID, uuid, strlen(uuid));

				CM_CopyField(msg, eMsgSubject, eExclusiveID);
			}
		}
		p++;
	}

	/* Modern clients are using vNote format.  Check for one... */

	mime_parser(CM_RANGE(msg, eMesageText),
		    *notes_extract_vnote,
		    NULL, NULL,
		    &v,		/* user data ptr - put the vnote here */
		    0
	);

	if (v == NULL) return(0);	/* no vNotes were found in this message */

	/* Set the message EUID to the vNote UID */

	if ((v->uid) && (!IsEmptyStr(v->uid))) {
		syslog(LOG_DEBUG, "UID of vNote is: %s\n", v->uid);
		CM_SetField(msg, eExclusiveID, v->uid, strlen(v->uid));
	}

	/* Set the message Subject to the vNote Summary */

	if ((v->summary) && (!IsEmptyStr(v->summary))) {
		CM_SetField(msg, eMsgSubject, v->summary, strlen(v->summary));

		if (msg->cm_lengths[eMsgSubject] > 72) {
			strcpy(&msg->cm_fields[eMsgSubject][68], "...");
			CM_CutFieldAt(msg, eMsgSubject, 72);
		}
	}

	vnote_free(v);
	
	return(0);
}
Exemplo n.º 12
0
/*
 * This is the back end for flush_conversations_to_disk()
 * At this point we've isolated a single conversation (struct imlog)
 * and are ready to write it to disk.
 */
void flush_individual_conversation(struct imlog *im) {
	struct CtdlMessage *msg;
	long msgnum = 0;
	char roomname[ROOMNAMELEN];
	StrBuf *MsgBuf, *FullMsgBuf;

	StrBufAppendBufPlain(im->conversation, HKEY(
		"</body>\r\n"
		"</html>\r\n"
		), 0
	);

	MsgBuf = StrBufRFC2047encodeMessage(im->conversation);
	FlushStrBuf(im->conversation);
	FullMsgBuf = NewStrBufPlain(NULL, StrLength(im->conversation) + 100);

	StrBufAppendBufPlain(FullMsgBuf, HKEY(
			"Content-type: text/html; charset=UTF-8\r\n"
			"Content-Transfer-Encoding: quoted-printable\r\n"
			"\r\n"
			), 0);
	StrBufAppendBuf (FullMsgBuf, MsgBuf, 0);
	FreeStrBuf(&MsgBuf);

	msg = malloc(sizeof(struct CtdlMessage));
	memset(msg, 0, sizeof(struct CtdlMessage));
	msg->cm_magic = CTDLMESSAGE_MAGIC;
	msg->cm_anon_type = MES_NORMAL;
	msg->cm_format_type = FMT_RFC822;
	if (!IsEmptyStr(im->usernames[0])) {
		CM_SetField(msg, eAuthor, im->usernames[0], strlen(im->usernames[0]));
	} else {
		CM_SetField(msg, eAuthor, HKEY("Citadel"));
	}
	if (!IsEmptyStr(im->usernames[1])) {
		CM_SetField(msg, eRecipient, im->usernames[1], strlen(im->usernames[1]));
	}

	CM_SetField(msg, eOriginalRoom, HKEY(PAGELOGROOM));
	CM_SetField(msg, eNodeName, CFG_KEY(c_nodename));
	CM_SetAsFieldSB(msg, eMesageText, &FullMsgBuf);	/* we own this memory now */

	/* Start with usernums[1] because it's guaranteed to be higher than usernums[0],
	 * so if there's only one party, usernums[0] will be zero but usernums[1] won't.
	 * Create the room if necessary.  Note that we create as a type 5 room rather
	 * than 4, which indicates that it's a personal room but we've already supplied
	 * the namespace prefix.
	 *
	 * In the unlikely event that usernums[1] is zero, a room with an invalid namespace
	 * prefix will be created.  That's ok because the auto-purger will clean it up later.
	 */
	snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[1], PAGELOGROOM);
	CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS);
	msgnum = CtdlSubmitMsg(msg, NULL, roomname, 0);
	CM_Free(msg);

	/* If there is a valid user number in usernums[0], save a copy for them too. */
	if (im->usernums[0] > 0) {
		snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[0], PAGELOGROOM);
		CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS);
		CtdlSaveMsgPointerInRoom(roomname, msgnum, 0, NULL);
	}

	/* Finally, if we're logging instant messages globally, do that now. */
	if (!IsEmptyStr(config.c_logpages)) {
		CtdlCreateRoom(config.c_logpages, 3, "", 0, 1, 1, VIEW_BBS);
		CtdlSaveMsgPointerInRoom(config.c_logpages, msgnum, 0, NULL);
	}

}
Exemplo n.º 13
0
/*
 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
 * instructions for "5" codes (permanent fatal errors) and produce/deliver
 * a "bounce" message (delivery status notification).
 */
void smtpq_do_bounce(OneQueItem *MyQItem, StrBuf *OMsgTxt, ParsedURL *Relay)
{
	static int seq = 0;
	
	struct CtdlMessage *bmsg = NULL;
	StrBuf *boundary;
	StrBuf *Msg = NULL;
	StrBuf *BounceMB;
	recptypes *valid;
	time_t now;

	HashPos *It;
	void *vQE;
	long len;
	const char *Key;

	int first_attempt = 0;
	int successful_bounce = 0;
	int num_bounces = 0;
	int give_up = 0;

	SMTPCM_syslog(LOG_DEBUG, "smtp_do_bounce() called\n");

	if (MyQItem->SendBounceMail == 0)
		return;

	now = time (NULL); //ev_time();

	if ( (now - MyQItem->Submitted) > SMTP_GIVE_UP ) {
		give_up = 1;
	}

	if (MyQItem->Retry == SMTP_RETRY_INTERVAL) {
		first_attempt = 1;
	}

	/*
	 * Now go through the instructions checking for stuff.
	 */
	Msg = NewStrBufPlain(NULL, 1024);
	It = GetNewHashPos(MyQItem->MailQEntries, 0);
	while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
	{
		MailQEntry *ThisItem = vQE;
		if ((ThisItem->Active && (ThisItem->Status == 5)) || /* failed now? */
		    ((give_up == 1) && (ThisItem->Status != 2)) ||
		    ((first_attempt == 1) && (ThisItem->Status != 2)))
			/* giving up after failed attempts... */
		{
			++num_bounces;

			StrBufAppendBufPlain(Msg, HKEY(" "), 0);
			StrBufAppendBuf(Msg, ThisItem->Recipient, 0);
			StrBufAppendBufPlain(Msg, HKEY(": "), 0);
			if (ThisItem->AllStatusMessages != NULL)
				StrBufAppendBuf(Msg, ThisItem->AllStatusMessages, 0);
			else
				StrBufAppendBuf(Msg, ThisItem->StatusMessage, 0);
			StrBufAppendBufPlain(Msg, HKEY("\r\n"), 0);
		}
	}
	DeleteHashPos(&It);

	/* Deliver the bounce if there's anything worth mentioning */
	SMTPC_syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);

	if (num_bounces == 0) {
		FreeStrBuf(&Msg);
		return;
	}

	if ((StrLength(MyQItem->SenderRoom) == 0) && MyQItem->HaveRelay) {
		const char *RelayUrlStr = "[not found]";
		/* one message that relaying is broken is enough; no extra room error message. */
		StrBuf *RelayDetails = NewStrBuf();

		if (Relay != NULL)
			RelayUrlStr = ChrPtr(Relay->URL);

		StrBufPrintf(RelayDetails,
			     "Relaying via %s failed permanently. \n Reason:\n%s\n Revalidate your relay configuration.",
			     RelayUrlStr,
			     ChrPtr(Msg));
                CtdlAideMessage(ChrPtr(RelayDetails), "Relaying Failed");
		FreeStrBuf(&RelayDetails);
	}

	boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
	StrBufAppendPrintf(boundary,
			   "%s_%04x%04x",
			   CtdlGetConfigStr("c_fqdn"),
			   getpid(),
			   ++seq);

	/* Start building our bounce message; go shopping for memory first. */
	BounceMB = NewStrBufPlain(
		NULL,
		1024 + /* mime stuff.... */
		StrLength(Msg) +  /* the bounce information... */
		StrLength(OMsgTxt)); /* the original message */
	if (BounceMB == NULL) {
		FreeStrBuf(&boundary);
		SMTPCM_syslog(LOG_ERR, "Failed to alloc() bounce message.\n");

		return;
	}

	bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
	if (bmsg == NULL) {
		FreeStrBuf(&boundary);
		FreeStrBuf(&BounceMB);
		SMTPCM_syslog(LOG_ERR, "Failed to alloc() bounce message.\n");

		return;
	}
	memset(bmsg, 0, sizeof(struct CtdlMessage));


	StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
	StrBufAppendBuf(BounceMB, boundary, 0);
	StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
	StrBufAppendBuf(BounceMB, boundary, 0);
	StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);

	if (give_up)
		StrBufAppendBufPlain(
			BounceMB,
			HKEY(
				"A message you sent could not be delivered "
				"to some or all of its recipients\n"
				"due to prolonged unavailability "
				"of its destination(s).\n"
				"Giving up on the following addresses:\n\n"
				), 0);
	else
		StrBufAppendBufPlain(
			BounceMB,
			HKEY(
				"A message you sent could not be delivered "
				"to some or all of its recipients.\n"
				"The following addresses "
				"were undeliverable:\n\n"
				), 0);

	StrBufAppendBuf(BounceMB, Msg, 0);
	FreeStrBuf(&Msg);

	if (StrLength(MyQItem->SenderRoom) > 0)
	{
		StrBufAppendBufPlain(
			BounceMB,
			HKEY("The message was originaly posted in: "), 0);
		StrBufAppendBuf(BounceMB, MyQItem->SenderRoom, 0);
		StrBufAppendBufPlain(
			BounceMB,
			HKEY("\n"), 0);
	}

	/* Attach the original message */
	StrBufAppendBufPlain(BounceMB, HKEY("\r\n--"), 0);
	StrBufAppendBuf(BounceMB, boundary, 0);
	StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
	StrBufAppendBufPlain(BounceMB,
			     HKEY("Content-type: message/rfc822\r\n"), 0);
	StrBufAppendBufPlain(BounceMB,
			     HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
	StrBufAppendBufPlain(BounceMB,
			     HKEY("Content-Disposition: inline\r\n"), 0);
	StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
	StrBufAppendBuf(BounceMB, OMsgTxt, 0);

	/* Close the multipart MIME scope */
	StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
	StrBufAppendBuf(BounceMB, boundary, 0);
	StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);

	bmsg->cm_magic = CTDLMESSAGE_MAGIC;
	bmsg->cm_anon_type = MES_NORMAL;
	bmsg->cm_format_type = FMT_RFC822;

	CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM));
	CM_SetField(bmsg, eAuthor, HKEY("Citadel"));
	CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename")));
	CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)"));
	CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB);

	/* First try the user who sent the message */
	if (StrLength(MyQItem->BounceTo) == 0) {
		SMTPCM_syslog(LOG_ERR, "No bounce address specified\n");
	}
	else {
		SMTPC_syslog(LOG_DEBUG, "bounce to user? <%s>\n",
		       ChrPtr(MyQItem->BounceTo));
	}

	/* Can we deliver the bounce to the original sender? */
	valid = validate_recipients(ChrPtr(MyQItem->BounceTo), NULL, 0);
	if ((valid != NULL) && (valid->num_error == 0)) {
		CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
		successful_bounce = 1;
	}

	/* If not, post it in the Aide> room */
	if (successful_bounce == 0) {
		CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR);
	}

	/* Free up the memory we used */
	free_recipients(valid);
	FreeStrBuf(&boundary);
	CM_Free(bmsg);
	SMTPCM_syslog(LOG_DEBUG, "Done processing bounces\n");
}
Exemplo n.º 14
0
int smtp_aftersave(struct CtdlMessage *msg,
		   recptypes *recps)
{
	/* For internet mail, generate delivery instructions.
	 * Yes, this is recursive.  Deal with it.  Infinite recursion does
	 * not happen because the delivery instructions message does not
	 * contain a recipient.
	 */
	if ((recps != NULL) && (recps->num_internet > 0)) {
		struct CtdlMessage *imsg = NULL;
		char recipient[SIZ];
		CitContext *CCC = MyContext();
		StrBuf *SpoolMsg = NewStrBuf();
		long nTokens;
		int i;

		MSGM_syslog(LOG_DEBUG, "Generating delivery instructions\n");

		StrBufPrintf(SpoolMsg,
			     "Content-type: "SPOOLMIME"\n"
			     "\n"
			     "msgid|%s\n"
			     "submitted|%ld\n"
			     "bounceto|%s\n",
			     msg->cm_fields[eVltMsgNum],
			     (long)time(NULL),
			     recps->bounce_to);

		if (recps->envelope_from != NULL) {
			StrBufAppendBufPlain(SpoolMsg, HKEY("envelope_from|"), 0);
			StrBufAppendBufPlain(SpoolMsg, recps->envelope_from, -1, 0);
			StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
		}
		if (recps->sending_room != NULL) {
			StrBufAppendBufPlain(SpoolMsg, HKEY("source_room|"), 0);
			StrBufAppendBufPlain(SpoolMsg, recps->sending_room, -1, 0);
			StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
		}

		nTokens = num_tokens(recps->recp_internet, '|');
	  	for (i = 0; i < nTokens; i++) {
			long len;
			len = extract_token(recipient, recps->recp_internet, i, '|', sizeof recipient);
			if (len > 0) {
				StrBufAppendBufPlain(SpoolMsg, HKEY("remote|"), 0);
				StrBufAppendBufPlain(SpoolMsg, recipient, len, 0);
				StrBufAppendBufPlain(SpoolMsg, HKEY("|0||\n"), 0);
			}
		}

		imsg = malloc(sizeof(struct CtdlMessage));
		memset(imsg, 0, sizeof(struct CtdlMessage));
		imsg->cm_magic = CTDLMESSAGE_MAGIC;
		imsg->cm_anon_type = MES_NORMAL;
		imsg->cm_format_type = FMT_RFC822;
		CM_SetField(imsg, eMsgSubject, HKEY("QMSG"));
		CM_SetField(imsg, eAuthor, HKEY("Citadel"));
		CM_SetField(imsg, eJournal, HKEY("do not journal"));
		CM_SetAsFieldSB(imsg, eMesageText, &SpoolMsg);
		CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
		CM_Free(imsg);
	}
	return 0;
}
Exemplo n.º 15
0
/*
 * convert_field() is a helper function for convert_internet_message().
 * Given start/end positions for an rfc822 field, it converts it to a Citadel
 * field if it wants to, and unfolds it if necessary.
 *
 * Returns 1 if the field was converted and inserted into the Citadel message
 * structure, implying that the source field should be removed from the
 * message text.
 */
int convert_field(struct CtdlMessage *msg, const char *beg, const char *end) {
	char *key, *value, *valueend;
	long len;
	const char *pos;
	int i;
	const char *colonpos = NULL;
	int processed = 0;
	char user[1024];
	char node[1024];
	char name[1024];
	char addr[1024];
	time_t parsed_date;
	long valuelen;

	for (pos = end; pos >= beg; pos--) {
		if (*pos == ':') colonpos = pos;
	}

	if (colonpos == NULL) return(0);	/* no colon? not a valid header line */

	len = end - beg;
	key = malloc(len + 2);
	memcpy(key, beg, len + 1);
	key[len] = '\0';
	valueend = key + len;
	* ( key + (colonpos - beg) ) = '\0';
	value = &key[(colonpos - beg) + 1];
/*	printf("Header: [%s]\nValue: [%s]\n", key, value); */
	unfold_rfc822_field(&value, &valueend);
	valuelen = valueend - value + 1;
/*	printf("UnfoldedValue: [%s]\n", value); */

	/*
	 * Here's the big rfc822-to-citadel loop.
	 */

	/* Date/time is converted into a unix timestamp.  If the conversion
	 * fails, we replace it with the time the message arrived locally.
	 */
	if (!strcasecmp(key, "Date")) {
		parsed_date = parsedate(value);
		if (parsed_date < 0L) parsed_date = time(NULL);

		if (CM_IsEmpty(msg, eTimestamp))
			CM_SetFieldLONG(msg, eTimestamp, parsed_date);
		processed = 1;
	}

	else if (!strcasecmp(key, "From")) {
		process_rfc822_addr(value, user, node, name);
		syslog(LOG_DEBUG, "Converted to <%s@%s> (%s)\n", user, node, name);
		snprintf(addr, sizeof(addr), "%s@%s", user, node);
		if (CM_IsEmpty(msg, eAuthor) && !IsEmptyStr(name))
			CM_SetField(msg, eAuthor, name, strlen(name));
		if (CM_IsEmpty(msg, erFc822Addr) && !IsEmptyStr(addr))
			CM_SetField(msg, erFc822Addr, addr, strlen(addr));
		processed = 1;
	}

	else if (!strcasecmp(key, "Subject")) {
		if (CM_IsEmpty(msg, eMsgSubject))
			CM_SetField(msg, eMsgSubject, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "List-ID")) {
		if (CM_IsEmpty(msg, eListID))
			CM_SetField(msg, eListID, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "To")) {
		if (CM_IsEmpty(msg, eRecipient))
			CM_SetField(msg, eRecipient, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "CC")) {
		if (CM_IsEmpty(msg, eCarbonCopY))
			CM_SetField(msg, eCarbonCopY, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "Message-ID")) {
		if (!CM_IsEmpty(msg, emessageId)) {
			syslog(LOG_WARNING, "duplicate message id\n");
		}
		else {
			char *pValue;
			long pValueLen;

			pValue = value;
			pValueLen = valuelen;
			/* Strip angle brackets */
			while (haschar(pValue, '<') > 0) {
				pValue ++;
				pValueLen --;
			}

			for (i = 0; i <= pValueLen; ++i)
				if (pValue[i] == '>') {
					pValueLen = i;
					break;
				}

			CM_SetField(msg, emessageId, pValue, pValueLen);
		}

		processed = 1;
	}

	else if (!strcasecmp(key, "Return-Path")) {
		if (CM_IsEmpty(msg, eMessagePath))
			CM_SetField(msg, eMessagePath, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "Envelope-To")) {
		if (CM_IsEmpty(msg, eenVelopeTo))
			CM_SetField(msg, eenVelopeTo, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "References")) {
		CM_SetField(msg, eWeferences, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "Reply-To")) {
		CM_SetField(msg, eReplyTo, value, valuelen);
		processed = 1;
	}

	else if (!strcasecmp(key, "In-reply-to")) {
		if (CM_IsEmpty(msg, eWeferences)) /* References: supersedes In-reply-to: */
			CM_SetField(msg, eWeferences, value, valuelen);
		processed = 1;
	}



	/* Clean up and move on. */
	free(key);	/* Don't free 'value', it's actually the same buffer */
	return processed;
}