Пример #1
0
eNextState get_one_mx_host_ip(AsyncIO *IO)
{
	SmtpOutMsg * Msg = IO->Data;
	/*
	 * here we start with the lookup of one host. it might be...
	 * - the relay host *sigh*
	 * - the direct hostname if there was no mx record
	 * - one of the mx'es
	 */
	SetSMTPState(IO, (Msg->pCurrRelay->IPv6)?eSTMPalookup:eSTMPaaaalookup);

	EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);

	EVS_syslog(LOG_DEBUG,
		  "looking up %s-Record %s : %d ...\n",
		  (Msg->pCurrRelay->IPv6)? "aaaa": "a",
		  Msg->pCurrRelay->Host,
		  Msg->pCurrRelay->Port);

	if (!QueueQuery((Msg->pCurrRelay->IPv6)? ns_t_aaaa : ns_t_a,
			Msg->pCurrRelay->Host,
			&Msg->IO,
			&Msg->HostLookup,
			get_one_mx_host_ip_done))
	{
		Msg->MyQEntry->Status = 5;
		StrBufPrintf(Msg->MyQEntry->StatusMessage,
			     "No MX hosts found for <%s>", Msg->node);
		Msg->IO.NextState = eTerminateConnection;
		return IO->NextState;
	}
	IO->NextState = eReadDNSReply;
	return IO->NextState;
}
Пример #2
0
void headers_brief_filter(long msgnum, void *userdata)
{
	long i, l;
	struct CtdlMessage *msg;
	msg_filter *flt = (msg_filter*) userdata;

	l = GetCount(flt->Filter);
	msg = CtdlFetchMessage(msgnum, 0, 1);
	StrBufPrintf(flt->buffer, "%ld", msgnum);
	if (msg == NULL) {
		for (i = 0; i < l; i++) {
			StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0);
		}
	}
	else {
		const char *k;
		long len;
		void *v;
		RewindHashPos(flt->Filter, flt->p, 0);
		while (GetNextHashPos(flt->Filter, flt->p, &len, &k, &v)) {
			eMsgField f = (eMsgField) v;
			
			StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0);
			if (!CM_IsEmpty(msg, f)) {
				StrBufAppendBufPlain(flt->buffer, CM_KEY(msg, f), 0);
			}
		}
	}
	StrBufAppendBufPlain(flt->buffer, HKEY("\n"), 0);
	cputbuf(flt->buffer);
}
Пример #3
0
eNextState POP3C_SendGetOneMsg(pop3aggr *RecvMsg)
{
	AsyncIO *IO = &RecvMsg->IO;
	long HKLen;
	const char *HKey;
	void *vData;

	SetPOP3State(IO, eGetMsg);

	RecvMsg->CurrMsg = NULL;
	while ((RecvMsg->Pos != NULL) && 
	       GetNextHashPos(RecvMsg->MsgNumbers,
			      RecvMsg->Pos,
			      &HKLen, &HKey,
			      &vData) &&
	       (RecvMsg->CurrMsg = (FetchItem*) vData,
		RecvMsg->CurrMsg->NeedFetch == 0))
	{}

	if ((RecvMsg->CurrMsg != NULL ) && (RecvMsg->CurrMsg->NeedFetch == 1))
	{
		/* Message has not been seen.
		 * Tell the server to fetch the message... */
		StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
			     "RETR %ld\r\n", RecvMsg->CurrMsg->MSGID);
		POP3C_DBG_SEND();
		return eReadMessage;
	}
	else {
		RecvMsg->State = ReadQuitState;
		return POP3_C_DispatchWriteDone(&RecvMsg->IO);
	}
}
Пример #4
0
eNextState POP3C_GetOneMessageIDState(pop3aggr *RecvMsg)
{
	AsyncIO *IO = &RecvMsg->IO;
#if 0
	int rc;
	rc = TestValidateHash(RecvMsg->MsgNumbers);
	if (rc != 0)
		EVP3CCS_syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
#endif

	POP3C_DBG_READ();
	if (!POP3C_OK) return eTerminateConnection;
	RecvMsg->CurrMsg->MsgUIDL =
		NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf));
	RecvMsg->CurrMsg->MsgUID =
		NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf) * 2);

	StrBufExtract_token(RecvMsg->CurrMsg->MsgUIDL,
			    RecvMsg->IO.IOBuf, 2, ' ');

	StrBufPrintf(RecvMsg->CurrMsg->MsgUID,
		     "pop3/%s/%s:%s@%s",
		     ChrPtr(RecvMsg->RoomName),
		     ChrPtr(RecvMsg->CurrMsg->MsgUIDL),
		     RecvMsg->IO.ConnectMe->User,
		     RecvMsg->IO.ConnectMe->Host);
	RecvMsg->State --;
	return eSendReply;
}
Пример #5
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");
}
Пример #6
0
/*
 * Fetch the "mortuary" - a list of dead buddies which we keep around forever
 * so we can remove them from any client's roster that still has them listed
 */
void xmpp_store_mortuary(HashList *mortuary) {
	HashPos *HashPos;
	long len;
	void *Value;
	const char *Key;
	StrBuf *themsg;

	themsg = NewStrBuf();
	StrBufPrintf(themsg,	"Content-type: " XMPPMORTUARY "\n"
				"Content-transfer-encoding: 7bit\n"
				"\n"
	);

	HashPos = GetNewHashPos(mortuary, 0);
	while (GetNextHashPos(mortuary, HashPos, &len, &Key, &Value) != 0)
	{
		StrBufAppendPrintf(themsg, "%s\n", (char *)Value);
	}
	DeleteHashPos(&HashPos);

	/* Delete the old mortuary */
	CtdlDeleteMessages(USERCONFIGROOM, NULL, 0, XMPPMORTUARY);

	/* And save the new one to disk */
	quickie_message("Citadel", NULL, NULL, USERCONFIGROOM, ChrPtr(themsg), 4, "XMPP Mortuary");
	FreeStrBuf(&themsg);
}
Пример #7
0
StrBuf *SerializeQueueItem(OneQueItem *MyQItem)
{
	StrBuf *QMessage;
	HashPos  *It;
	const char *Key;
	long len;
	void *vQE;

	QMessage = NewStrBufPlain(NULL, SIZ);
	StrBufPrintf(QMessage, "Content-type: %s\n", SPOOLMIME);
//	"attempted|%ld\n"  "retry|%ld\n",, (long)time(NULL), (long)retry );
	StrBufAppendBufPlain(QMessage, HKEY("\nmsgid|"), 0);
	StrBufAppendPrintf(QMessage, "%ld", MyQItem->MessageID);

	StrBufAppendBufPlain(QMessage, HKEY("\nsubmitted|"), 0);
	StrBufAppendPrintf(QMessage, "%ld", MyQItem->Submitted);

	if (StrLength(MyQItem->BounceTo) > 0) {
		StrBufAppendBufPlain(QMessage, HKEY("\nbounceto|"), 0);
		StrBufAppendBuf(QMessage, MyQItem->BounceTo, 0);
	}

	if (StrLength(MyQItem->EnvelopeFrom) > 0) {
		StrBufAppendBufPlain(QMessage, HKEY("\nenvelope_from|"), 0);
		StrBufAppendBuf(QMessage, MyQItem->EnvelopeFrom, 0);
	}

	if (StrLength(MyQItem->SenderRoom) > 0) {
		StrBufAppendBufPlain(QMessage, HKEY("\nsource_room|"), 0);
		StrBufAppendBuf(QMessage, MyQItem->SenderRoom, 0);
	}

	StrBufAppendBufPlain(QMessage, HKEY("\nretry|"), 0);
	StrBufAppendPrintf(QMessage, "%ld",
			   MyQItem->Retry);

	StrBufAppendBufPlain(QMessage, HKEY("\nattempted|"), 0);
	StrBufAppendPrintf(QMessage, "%ld",
			   time(NULL) /*ctdl_ev_now()*/ + MyQItem->Retry);

	It = GetNewHashPos(MyQItem->MailQEntries, 0);
	while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
	{
		MailQEntry *ThisItem = vQE;

		StrBufAppendBufPlain(QMessage, HKEY("\nremote|"), 0);
		StrBufAppendBuf(QMessage, ThisItem->Recipient, 0);
		StrBufAppendBufPlain(QMessage, HKEY("|"), 0);
		StrBufAppendPrintf(QMessage, "%d", ThisItem->Status);
		StrBufAppendBufPlain(QMessage, HKEY("|"), 0);
		if (ThisItem->AllStatusMessages != NULL)
			StrBufAppendBuf(QMessage, ThisItem->AllStatusMessages, 0);
		else
			StrBufAppendBuf(QMessage, ThisItem->StatusMessage, 0);
	}
	DeleteHashPos(&It);
	StrBufAppendBufPlain(QMessage, HKEY("\n"), 0);
	return QMessage;
}
Пример #8
0
eNextState STMPC_send_HELO(SmtpOutMsg *Msg)
{
	AsyncIO *IO = &Msg->IO;
	StrBufPrintf(Msg->IO.SendBuf.Buf, "HELO %s\r\n", CtdlGetConfigStr("c_fqdn"));

	SMTP_DBG_SEND();
	return eReadMessage;
}
Пример #9
0
void SerializeNode(NodeConf *Node, StrBuf *Buf)
{
	StrBufPrintf(Buf, "%s|%s|%s|%s", 
		     ChrPtr(Node->NodeName),
		     ChrPtr(Node->Secret),
		     ChrPtr(Node->Host),
		     ChrPtr(Node->Port));
}
Пример #10
0
void display_editfloorpic(void) {
	StrBuf *PicAction;

	PicAction = NewStrBuf();
	StrBufPrintf(PicAction, "_floorpic_|%s", bstr("which_floor"));
	putbstr("__WHICHPIC", PicAction);
	putbstr("__PICDESC", NewStrBufPlain(_("the icon for this floor"), -1));
	putbstr("__UPLURL", NewStrBufPlain(HKEY("editfloorpic")));
	display_graphics_upload("editfloorpic");
}
Пример #11
0
eNextState POP3C_SendPassword(pop3aggr *RecvMsg)
{
	AsyncIO *IO = &RecvMsg->IO;
	SetPOP3State(IO, ePassword);
	/* Password */
	StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
		     "PASS %s\r\n", ChrPtr(RecvMsg->pop3pass));
	EVP3CM_syslog(LOG_DEBUG, "<PASS <password>\n");
//	POP3C_DBG_SEND(); No, we won't write the passvoid to syslog...
	return eReadMessage;
}
Пример #12
0
eNextState SMTPC_send_FROM(SmtpOutMsg *Msg)
{
	AsyncIO *IO = &Msg->IO;
	/* previous command succeeded, now try the MAIL FROM: command */
	StrBufPrintf(Msg->IO.SendBuf.Buf,
		     "MAIL FROM:<%s>\r\n",
		     Msg->envelope_from);

	SMTP_DBG_SEND();
	return eReadMessage;
}
Пример #13
0
eNextState SMTPC_send_EHLO(SmtpOutMsg *Msg)
{
	AsyncIO *IO = &Msg->IO;
	/* At this point we know we are talking to a real SMTP server */

	/* Do a EHLO command.  If it fails, try the HELO command. */
	StrBufPrintf(Msg->IO.SendBuf.Buf, "EHLO %s\r\n", CtdlGetConfigStr("c_fqdn"));

	SMTP_DBG_SEND();
	return eReadMessage;
}
Пример #14
0
eNextState SMTP_C_ConnFail(AsyncIO *IO)
{
	SmtpOutMsg *Msg = IO->Data;

	Msg->MyQEntry->Status = 4;
	EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
	StrBufPrintf(IO->ErrMsg, "Connection failure: %s while talking to %s",
		     ReadErrors[Msg->State].Key,
		     Msg->mx_host);

	return FailOneAttempt(IO);
}
Пример #15
0
eNextState SMTPC_send_RCPT(SmtpOutMsg *Msg)
{
	AsyncIO *IO = &Msg->IO;
	/* MAIL succeeded, now try the RCPT To: command */
	StrBufPrintf(Msg->IO.SendBuf.Buf,
		     "RCPT TO:<%s@%s>\r\n",
		     Msg->user,
		     Msg->node);

	SMTP_DBG_SEND();
	return eReadMessage;
}
Пример #16
0
eNextState SMTP_C_Timeout(AsyncIO *IO)
{
	SmtpOutMsg *Msg = IO->Data;

	Msg->MyQEntry->Status = 4;
	EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
	StrBufPrintf(IO->ErrMsg, "Timeout: %s while talking to %s",
		     ReadErrors[Msg->State].Key,
		     Msg->mx_host);
	if (Msg->State > eRCPT)
		return eAbort;
	else
		return FailOneAttempt(IO);
}
Пример #17
0
eNextState POP3C_SendUser(pop3aggr *RecvMsg)
{
	AsyncIO *IO = &RecvMsg->IO;
	SetPOP3State(IO, eUser);
	/* Identify ourselves.  NOTE: we have to append a CR to each command.
	 *  The LF will automatically be appended by sock_puts().  Believe it
	 * or not, leaving out the CR will cause problems if the server happens
	 * to be Exchange, which is so b0rken it actually barfs on
	 * LF-terminated newlines.
	 */
	StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
		     "USER %s\r\n", ChrPtr(RecvMsg->pop3user));
	POP3C_DBG_SEND();
	return eReadMessage;
}
Пример #18
0
int ReadPostData(void)
{
	int rc;
	int urlencoded_post = 0;
	wcsession *WCC = WC;
	StrBuf *content = NULL;
	
	urlencoded_post = (strncasecmp(ChrPtr(WCC->Hdr->HR.ContentType), "application/x-www-form-urlencoded", 33) == 0) ;

	content = NewStrBufPlain(NULL, WCC->Hdr->HR.ContentLength + 256);

	if (!urlencoded_post)
	{
		StrBufPrintf(content, 
		     "Content-type: %s\n"
			     "Content-length: %ld\n\n",
			     ChrPtr(WCC->Hdr->HR.ContentType), 
			     WCC->Hdr->HR.ContentLength);
	}

	/** Read the entire input data at once. */
	rc = client_read_to(WCC->Hdr, content, 
			    WCC->Hdr->HR.ContentLength,
			    SLEEPING);
	if (rc < 0)
		return rc;
		
	
	if (urlencoded_post) {
		ParseURLParams(content);
	} else if (!strncasecmp(ChrPtr(WCC->Hdr->HR.ContentType), "multipart", 9)) {
		char *Buf;
		char *BufEnd;
		long len;

		len = StrLength(content);
		Buf = SmashStrBuf(&content);
		BufEnd = Buf + len;
		mime_parser(Buf, BufEnd, *upload_handler, NULL, NULL, NULL, 0);
		free(Buf);
	} else if (WCC->Hdr->HR.ContentLength > 0) {
		WCC->upload = content;
		WCC->upload_length = StrLength(WCC->upload);
		content = NULL;
	}
	FreeStrBuf(&content);
	return 1;
}
Пример #19
0
eNextState POP3C_SendDelete(pop3aggr *RecvMsg)
{
	AsyncIO *IO = &RecvMsg->IO;

	SetPOP3State(IO, eDelete);

	if (!RecvMsg->keep) {
		StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
			     "DELE %ld\r\n", RecvMsg->CurrMsg->MSGID);
		POP3C_DBG_SEND();
		return eReadMessage;
	}
	else {
		RecvMsg->State = ReadMessageBodyFollowing;
		return POP3_C_DispatchWriteDone(&RecvMsg->IO);
	}
}
Пример #20
0
void SetConnectStatus(AsyncIO *IO)
{
	SmtpOutMsg *Msg = IO->Data;
	char buf[256];
	void *src;

	buf[0] = '\0';

	if (IO->ConnectMe->IPv6) {
		src = &IO->ConnectMe->Addr.sin6_addr;
	}
	else {
		struct sockaddr_in *addr;

		addr = (struct sockaddr_in *)&IO->ConnectMe->Addr;
		src = &addr->sin_addr.s_addr;
	}

	inet_ntop((IO->ConnectMe->IPv6)?AF_INET6:AF_INET,
		  src,
		  buf,
		  sizeof(buf));

	if (Msg->mx_host == NULL)
		Msg->mx_host = "<no MX-Record>";

	EVS_syslog(LOG_INFO,
		  "connecting to %s [%s]:%d ...\n",
		  Msg->mx_host,
		  buf,
		  Msg->IO.ConnectMe->Port);

	Msg->MyQEntry->Status = 4;
	StrBufPrintf(Msg->MyQEntry->StatusMessage,
		     "Timeout while connecting %s [%s]:%d ",
		     Msg->mx_host,
		     buf,
		     Msg->IO.ConnectMe->Port);
	Msg->IO.NextState = eConnect;
}
Пример #21
0
eNextState SMTPC_send_auth(SmtpOutMsg *Msg)
{
	AsyncIO *IO = &Msg->IO;
	char buf[SIZ];
	char encoded[1024];

	if ((Msg->pCurrRelay == NULL) ||
	    (Msg->pCurrRelay->User == NULL))
		READ_NEXT_STATE(eFROM); /* Skip auth, shouldn't even come here!... */
	else {
		/* Do an AUTH command if necessary */
		if (Msg->SendLogin)
		{
			StrBufPlain(Msg->IO.SendBuf.Buf,
				    HKEY("AUTH LOGIN\r\n"));
		}
		else
		{
			sprintf(buf, "%s%c%s%c%s",
				Msg->pCurrRelay->User, '\0',
				Msg->pCurrRelay->User, '\0',
				Msg->pCurrRelay->Pass);
			
			size_t len = CtdlEncodeBase64(encoded, buf,
						      strlen(Msg->pCurrRelay->User) * 2 +
						      strlen(Msg->pCurrRelay->Pass) + 2, 0);

			if (buf[len - 1] == '\n') {
				buf[len - 1] = '\0';
			}

			StrBufPrintf(Msg->IO.SendBuf.Buf,
				     "AUTH PLAIN %s\r\n",
				     encoded);
		}
	}
	SMTP_DBG_SEND();
	return eReadMessage;
}
Пример #22
0
eNextState resolve_mx_records(AsyncIO *IO)
{
	SmtpOutMsg * Msg = IO->Data;

	SetSMTPState(IO, eSTMPmxlookup);

	EVS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
	/* start resolving MX records here. */
	if (!QueueQuery(ns_t_mx,
			Msg->node,
			&Msg->IO,
			&Msg->MxLookup,
			smtp_resolve_mx_record_done))
	{
		Msg->MyQEntry->Status = 5;
		StrBufPrintf(Msg->MyQEntry->StatusMessage,
			     "No MX hosts found for <%s>", Msg->node);
		return IO->NextState;
	}
	Msg->IO.NextState = eReadDNSReply;
	return IO->NextState;
}
Пример #23
0
eNextState POP3C_GetOneMessagID(pop3aggr *RecvMsg)
{
	AsyncIO *IO = &RecvMsg->IO;
	long HKLen;
	const char *HKey;
	void *vData;

	SetPOP3State(IO, eGetMsgID);
#if 0
	int rc;
	rc = TestValidateHash(RecvMsg->MsgNumbers);
	if (rc != 0)
		EVP3CCS_syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
#endif
	if((RecvMsg->Pos != NULL) &&
	   GetNextHashPos(RecvMsg->MsgNumbers,
			  RecvMsg->Pos,
			  &HKLen, &HKey,
			  &vData))
	{
		RecvMsg->CurrMsg = (FetchItem*) vData;
		/* Find out the UIDL of the message,
		 * to determine whether we've already downloaded it */
		StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
			     "UIDL %ld\r\n", RecvMsg->CurrMsg->MSGID);
		POP3C_DBG_SEND();
	}
	else
	{
		RecvMsg->State++;
		DeleteHashPos(&RecvMsg->Pos);
		/// done receiving uidls.. start looking them up now.
		RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0);
		return EventQueueDBOperation(&RecvMsg->IO,
					     POP3_FetchNetworkUsetableEntry,
					     0);
	}
	return eReadMore; /* TODO */
}
Пример #24
0
static void
IO_recv_callback(struct ev_loop *loop, ev_io *watcher, int revents)
{
	const char *errmsg;
	ssize_t nbytes;
	AsyncIO *IO = watcher->data;

	SET_EV_TIME(IO, event_base);
	switch (IO->NextState) {
	case eReadFile:
		nbytes = FileRecvChunked(&IO->IOB, &errmsg);
		if (nbytes < 0)
			StrBufPlain(IO->ErrMsg, errmsg, -1);
		else
		{
			if (IO->IOB.ChunkSendRemain == 0)
			{
				IO->NextState = eSendReply;
				assert(IO->ReadDone);
				ev_io_stop(event_base, &IO->recv_event);
				PostInbound(IO);
				return;
			}
			else
				return;
		}
		break;
	default:
		nbytes = StrBuf_read_one_chunk_callback(IO->RecvBuf.fd,
							0,
							&IO->RecvBuf);
		break;
	}

#ifdef BIGBAD_IODBG
	{
		long nbytes;
		int rv = 0;
		char fn [SIZ];
		FILE *fd;
		const char *pch = ChrPtr(IO->RecvBuf.Buf);
		const char *pchh = IO->RecvBuf.ReadWritePointer;

		if (pchh == NULL)
			pchh = pch;

		nbytes = StrLength(IO->RecvBuf.Buf) - (pchh - pch);
		snprintf(fn, SIZ, "/tmp/foolog_ev_%s.%d",
			 ((CitContext*)(IO->CitContext))->ServiceName,
			 IO->SendBuf.fd);

		fd = fopen(fn, "a+");
		if (fd == NULL) {
			syslog(LOG_EMERG, "failed to open file %s: %s", fn, strerror(errno));
			cit_backtrace();
			exit(1);
		}
		fprintf(fd, "Read: BufSize: %ld BufContent: [",
			nbytes);
		rv = fwrite(pchh, nbytes, 1, fd);
		if (!rv) printf("failed to write debug to %s!\n", fn);
		fprintf(fd, "]\n");
		fclose(fd);
	}
#endif
	if (nbytes > 0) {
		HandleInbound(IO);
	} else if (nbytes == 0) {
		StopClientWatchers(IO, 1);
		SetNextTimeout(IO, 0.01);
		return;
	} else if (nbytes == -1) {
		if (errno != EAGAIN) {
			// FD is gone. kick it. 
			StopClientWatchers(IO, 1);
			EV_syslog(LOG_DEBUG,
				  "IO_recv_callback(): Socket Invalid! [%d] [%s] [%d]\n",
				  errno, strerror(errno), IO->SendBuf.fd);
			StrBufPrintf(IO->ErrMsg,
				     "Socket Invalid! [%s]",
				     strerror(errno));
			SetNextTimeout(IO, 0.01);
		}
		return;
	}
}
Пример #25
0
/*
 * smtp_do_procmsg()
 *
 * Called by smtp_do_queue() to handle an individual message.
 */
void smtp_do_procmsg(long msgnum, void *userdata) {
	time_t now;
	int mynumsessions = num_sessions;
	struct CtdlMessage *msg = NULL;
	char *Author = NULL;
	char *Address = NULL;
	char *instr = NULL;
	StrBuf *PlainQItem;
	OneQueItem *MyQItem;
	char *pch;
	HashPos  *It;
	void *vQE;
	long len;
	const char *Key;
	int HaveBuffers = 0;
	StrBuf *Msg =NULL;

	if (mynumsessions > max_sessions_for_outbound_smtp) {
		SMTPC_syslog(LOG_INFO,
			     "skipping because of num jobs %d > %d max_sessions_for_outbound_smtp",
			     mynumsessions,
			     max_sessions_for_outbound_smtp);
	}

	SMTPC_syslog(LOG_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
	///strcpy(envelope_from, "");

	msg = CtdlFetchMessage(msgnum, 1, 1);
	if (msg == NULL) {
		SMTPC_syslog(LOG_ERR, "tried %ld but no such message!\n",
		       msgnum);
		return;
	}

	pch = instr = msg->cm_fields[eMesageText];

	/* Strip out the headers (no not amd any other non-instruction) line */
	while (pch != NULL) {
		pch = strchr(pch, '\n');
		if ((pch != NULL) &&
		    ((*(pch + 1) == '\n') ||
		     (*(pch + 1) == '\r')))
		{
			instr = pch + 2;
			pch = NULL;
		}
	}
	PlainQItem = NewStrBufPlain(instr, -1);
	CM_Free(msg);
	MyQItem = DeserializeQueueItem(PlainQItem, msgnum);
	FreeStrBuf(&PlainQItem);

	if (MyQItem == NULL) {
		SMTPC_syslog(LOG_ERR,
			     "Msg No %ld: already in progress!\n",
			     msgnum);
		return; /* s.b. else is already processing... */
	}

	/*
	 * Postpone delivery if we've already tried recently.
	 */
	now = time(NULL);
	if ((MyQItem->ReattemptWhen != 0) && 
	    (now < MyQItem->ReattemptWhen) &&
	    (run_queue_now == 0))
	{
		SMTPC_syslog(LOG_DEBUG, 
			     "Retry time not yet reached. %ld seconds left.",
			     MyQItem->ReattemptWhen - now);

		It = GetNewHashPos(MyQItem->MailQEntries, 0);
		pthread_mutex_lock(&ActiveQItemsLock);
		{
			if (GetHashPosFromKey(ActiveQItems,
					      LKEY(MyQItem->MessageID),
					      It))
			{
				DeleteEntryFromHash(ActiveQItems, It);
			}
		}
		pthread_mutex_unlock(&ActiveQItemsLock);
		////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this?
		DeleteHashPos(&It);
		return;
	}

	/*
	 * Bail out if there's no actual message associated with this
	 */
	if (MyQItem->MessageID < 0L) {
		SMTPCM_syslog(LOG_ERR, "no 'msgid' directive found!\n");
		It = GetNewHashPos(MyQItem->MailQEntries, 0);
		pthread_mutex_lock(&ActiveQItemsLock);
		{
			if (GetHashPosFromKey(ActiveQItems,
					      LKEY(MyQItem->MessageID),
					      It))
			{
				DeleteEntryFromHash(ActiveQItems, It);
			}
		}
		pthread_mutex_unlock(&ActiveQItemsLock);
		DeleteHashPos(&It);
		////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this?
		return;
	}


	It = GetNewHashPos(MyQItem->MailQEntries, 0);
	while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
	{
		MailQEntry *ThisItem = vQE;
		SMTPC_syslog(LOG_DEBUG, "SMTP Queue: Task: <%s> %d\n",
			     ChrPtr(ThisItem->Recipient),
			     ThisItem->Active);
	}
	DeleteHashPos(&It);

	MyQItem->NotYetShutdownDeliveries = 
		MyQItem->ActiveDeliveries = CountActiveQueueEntries(MyQItem, 1);

	/* failsafe against overload: 
	 * will we exceed the limit set? 
	 */
	if ((MyQItem->ActiveDeliveries + mynumsessions > max_sessions_for_outbound_smtp) && 
	    /* if yes, did we reach more than half of the quota? */
	    ((mynumsessions * 2) > max_sessions_for_outbound_smtp) && 
	    /* if... would we ever fit into half of the quota?? */
	    (((MyQItem->ActiveDeliveries * 2)  < max_sessions_for_outbound_smtp)))
	{
		/* abort delivery for another time. */
		SMTPC_syslog(LOG_INFO,
			     "SMTP Queue: skipping because of num jobs %d + %ld > %d max_sessions_for_outbound_smtp",
			     mynumsessions,
			     MyQItem->ActiveDeliveries,
			     max_sessions_for_outbound_smtp);

		It = GetNewHashPos(MyQItem->MailQEntries, 0);
		pthread_mutex_lock(&ActiveQItemsLock);
		{
			if (GetHashPosFromKey(ActiveQItems,
					      LKEY(MyQItem->MessageID),
					      It))
			{
				DeleteEntryFromHash(ActiveQItems, It);
			}
		}
		pthread_mutex_unlock(&ActiveQItemsLock);

		return;
	}


	if (MyQItem->ActiveDeliveries > 0)
	{
		ParsedURL *RelayUrls = NULL;
		int nActivated = 0;
		int n = MsgCount++;
		int m = MyQItem->ActiveDeliveries;
		int i = 1;

		It = GetNewHashPos(MyQItem->MailQEntries, 0);

		Msg = smtp_load_msg(MyQItem, n, &Author, &Address);
		RelayUrls = LoadRelayUrls(MyQItem, Author, Address);
		if ((RelayUrls == NULL) && MyQItem->HaveRelay) {

			while ((i <= m) &&
			       (GetNextHashPos(MyQItem->MailQEntries,
					       It, &len, &Key, &vQE)))
			{
				int KeepBuffers = (i == m);
				MailQEntry *ThisItem = vQE;
				StrBufPrintf(ThisItem->StatusMessage,
					     "No relay configured matching %s / %s", 
					     (Author != NULL)? Author : "",
					     (Address != NULL)? Address : "");
				ThisItem->Status = 5;

				nActivated++;

				if (i > 1) n = MsgCount++;
				SMTPC_syslog(LOG_INFO,
					     "SMTPC: giving up on <%ld> <%s> %d / %d \n",
					     MyQItem->MessageID,
					     ChrPtr(ThisItem->Recipient),
					     i,
					     m);
				(*((int*) userdata)) ++;
				smtp_try_one_queue_entry(MyQItem,
							 ThisItem,
							 Msg,
							 KeepBuffers,
							 n,
							 RelayUrls);

				if (KeepBuffers) HaveBuffers++;

				i++;
			}
			if (Author != NULL) free (Author);
			if (Address != NULL) free (Address);
			DeleteHashPos(&It);

			return;
		}
		if (Author != NULL) free (Author);
		if (Address != NULL) free (Address);

		while ((i <= m) &&
		       (GetNextHashPos(MyQItem->MailQEntries,
				       It, &len, &Key, &vQE)))
		{
			MailQEntry *ThisItem = vQE;

			if (ThisItem->Active == 1)
			{
				int KeepBuffers = (i == m);

				nActivated++;
				if (nActivated % ndelay_count == 0)
					usleep(delay_msec);

				if (i > 1) n = MsgCount++;
				SMTPC_syslog(LOG_DEBUG,
					     "SMTPC: Trying <%ld> <%s> %d / %d \n",
					     MyQItem->MessageID,
					     ChrPtr(ThisItem->Recipient),
					     i,
					     m);
				(*((int*) userdata)) ++;
				smtp_try_one_queue_entry(MyQItem,
							 ThisItem,
							 Msg,
							 KeepBuffers,
							 n,
							 RelayUrls);

				if (KeepBuffers) HaveBuffers++;

				i++;
			}
		}
		DeleteHashPos(&It);
	}
	else
	{
		It = GetNewHashPos(MyQItem->MailQEntries, 0);
		pthread_mutex_lock(&ActiveQItemsLock);
		{
			if (GetHashPosFromKey(ActiveQItems,
					      LKEY(MyQItem->MessageID),
					      It))
			{
				DeleteEntryFromHash(ActiveQItems, It);
			}
			else
			{
				long len;
				const char* Key;
				void *VData;

				SMTPC_syslog(LOG_WARNING,
					     "unable to find QItem with ID[%ld]",
					     MyQItem->MessageID);
				while (GetNextHashPos(ActiveQItems,
						      It,
						      &len,
						      &Key,
						      &VData))
				{
					SMTPC_syslog(LOG_WARNING,
						     "have: ID[%ld]",
						     ((OneQueItem *)VData)->MessageID);
				}
			}

		}
		pthread_mutex_unlock(&ActiveQItemsLock);
		DeleteHashPos(&It);
		////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this?

// TODO: bounce & delete?

	}
	if (!HaveBuffers) {
		FreeStrBuf (&Msg);
// TODO : free RelayUrls
	}
}
Пример #26
0
eNextState EvConnectSock(AsyncIO *IO,
			 double conn_timeout,
			 double first_rw_timeout,
			 int ReadFirst)
{
	struct sockaddr_in egress_sin;
	int fdflags;
	int rc = -1;

	SetEVState(IO, eIOConnectSock);
	become_session(IO->CitContext);

	if (ReadFirst) {
		IO->NextState = eReadMessage;
	}
	else {
		IO->NextState = eSendReply;
	}

	IO->SendBuf.fd = IO->RecvBuf.fd =
		socket(
			(IO->ConnectMe->IPv6)?PF_INET6:PF_INET,
			SOCK_STREAM,
			IPPROTO_TCP);

	if (IO->SendBuf.fd < 0) {
		EV_syslog(LOG_ERR,
			  "EVENT: socket() failed: %s\n",
			  strerror(errno));

		StrBufPrintf(IO->ErrMsg,
			     "Failed to create socket: %s",
			     strerror(errno));
		IO->SendBuf.fd = IO->RecvBuf.fd = 0;
		return eAbort;
	}
	fdflags = fcntl(IO->SendBuf.fd, F_GETFL);
	if (fdflags < 0) {
		EV_syslog(LOG_ERR,
			  "EVENT: unable to get socket %d flags! %s \n",
			  IO->SendBuf.fd,
			  strerror(errno));
		StrBufPrintf(IO->ErrMsg,
			     "Failed to get socket %d flags: %s",
			     IO->SendBuf.fd,
			     strerror(errno));
		close(IO->SendBuf.fd);
		IO->SendBuf.fd = IO->RecvBuf.fd = 0;
		return eAbort;
	}
	fdflags = fdflags | O_NONBLOCK;
	if (fcntl(IO->SendBuf.fd, F_SETFL, fdflags) < 0) {
		EV_syslog(
			LOG_ERR,
			"EVENT: unable to set socket %d nonblocking flags! %s \n",
			IO->SendBuf.fd,
			strerror(errno));
		StrBufPrintf(IO->ErrMsg,
			     "Failed to set socket flags: %s",
			     strerror(errno));
		close(IO->SendBuf.fd);
		IO->SendBuf.fd = IO->RecvBuf.fd = 0;
		return eAbort;
	}
/* TODO: maye we could use offsetof() to calc the position of data...
 * http://doc.dvgu.ru/devel/ev.html#associating_custom_data_with_a_watcher
 */
	ev_io_init(&IO->recv_event, IO_recv_callback, IO->RecvBuf.fd, EV_READ);
	IO->recv_event.data = IO;
	ev_io_init(&IO->send_event, IO_send_callback, IO->SendBuf.fd, EV_WRITE);
	IO->send_event.data = IO;

	ev_timer_init(&IO->conn_fail, IO_connfail_callback, conn_timeout, 0);
	IO->conn_fail.data = IO;
	ev_timer_init(&IO->rw_timeout, IO_Timeout_callback, first_rw_timeout,0);
	IO->rw_timeout.data = IO;




	/* for debugging you may bypass it like this:
	 * IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	 * ((struct sockaddr_in)IO->ConnectMe->Addr).sin_addr.s_addr =
	 *   inet_addr("127.0.0.1");
	 */
	if (IO->ConnectMe->IPv6) {
		rc = connect(IO->SendBuf.fd,
			     &IO->ConnectMe->Addr,
			     sizeof(struct sockaddr_in6));
	}
	else {
		/* If citserver is bound to a specific IP address on the host, make
		 * sure we use that address for outbound connections.
		 */
	
		memset(&egress_sin, 0, sizeof(egress_sin));
		egress_sin.sin_family = AF_INET;
		if (!IsEmptyStr(CtdlGetConfigStr("c_ip_addr"))) {
			egress_sin.sin_addr.s_addr = inet_addr(CtdlGetConfigStr("c_ip_addr"));
			if (egress_sin.sin_addr.s_addr == !INADDR_ANY) {
				egress_sin.sin_addr.s_addr = INADDR_ANY;
			}

			/* If this bind fails, no problem; we can still use INADDR_ANY */
			bind(IO->SendBuf.fd, (struct sockaddr *)&egress_sin, sizeof(egress_sin));
		}
		rc = connect(IO->SendBuf.fd,
			     (struct sockaddr_in *)&IO->ConnectMe->Addr,
			     sizeof(struct sockaddr_in));
	}

	if (rc >= 0){
		SetEVState(IO, eIOConnNow);
		EV_syslog(LOG_DEBUG, "connect() = %d immediate success.\n", IO->SendBuf.fd);
		set_start_callback(event_base, IO, 0);
		return IO->NextState;
	}
	else if (errno == EINPROGRESS) {
		SetEVState(IO, eIOConnWait);
		EV_syslog(LOG_DEBUG, "connect() = %d have to wait now.\n", IO->SendBuf.fd);

		ev_io_init(&IO->conn_event,
			   IO_connestd_callback,
			   IO->SendBuf.fd,
			   EV_READ|EV_WRITE);

		IO->conn_event.data = IO;

		ev_io_start(event_base, &IO->conn_event);
		ev_timer_start(event_base, &IO->conn_fail);
		return IO->NextState;
	}
	else {
		SetEVState(IO, eIOConnfail);
		ev_idle_init(&IO->conn_fail_immediate,
			     IO_connfailimmediate_callback);
		IO->conn_fail_immediate.data = IO;
		ev_idle_start(event_base, &IO->conn_fail_immediate);

		EV_syslog(LOG_ERR,
			  "connect() = %d failed: %s\n",
			  IO->SendBuf.fd,
			  strerror(errno));

		StrBufPrintf(IO->ErrMsg,
			     "Failed to connect: %s",
			     strerror(errno));
		return IO->NextState;
	}
	return IO->NextState;
}
Пример #27
0
static void
IO_send_callback(struct ev_loop *loop, ev_io *watcher, int revents)
{
	int rc;
	AsyncIO *IO = watcher->data;
	const char *errmsg = NULL;

	SET_EV_TIME(IO, event_base);
	become_session(IO->CitContext);
#ifdef BIGBAD_IODBG
	{
		int rv = 0;
		char fn [SIZ];
		FILE *fd;
		const char *pch = ChrPtr(IO->SendBuf.Buf);
		const char *pchh = IO->SendBuf.ReadWritePointer;
		long nbytes;

		if (pchh == NULL)
			pchh = pch;

		nbytes = StrLength(IO->SendBuf.Buf) - (pchh - pch);
		snprintf(fn, SIZ, "/tmp/foolog_ev_%s.%d",
			 ((CitContext*)(IO->CitContext))->ServiceName,
			 IO->SendBuf.fd);

		fd = fopen(fn, "a+");
		if (fd == NULL) {
			syslog(LOG_EMERG, "failed to open file %s: %s", fn, strerror(errno));
			cit_backtrace();
			exit(1);
		}
		fprintf(fd, "Send: BufSize: %ld BufContent: [",
			nbytes);
		rv = fwrite(pchh, nbytes, 1, fd);
		if (!rv) printf("failed to write debug to %s!\n", fn);
		fprintf(fd, "]\n");
#endif
		switch (IO->NextState) {
		case eSendFile:
			rc = FileSendChunked(&IO->IOB, &errmsg);
			if (rc < 0)
				StrBufPlain(IO->ErrMsg, errmsg, -1);
			break;
		default:
			rc = StrBuf_write_one_chunk_callback(IO->SendBuf.fd,
							     0,
							     &IO->SendBuf);
		}

#ifdef BIGBAD_IODBG
		fprintf(fd, "Sent: BufSize: %d bytes.\n", rc);
		fclose(fd);
	}
#endif
	if (rc == 0)
	{
		ev_io_stop(event_base, &IO->send_event);
		switch (IO->NextState) {
		case eSendMore:
			assert(IO->SendDone);
			IO->NextState = IO->SendDone(IO);

			if ((IO->NextState == eTerminateConnection) ||
			    (IO->NextState == eAbort) )
				ShutDownCLient(IO);
			else {
				ev_io_start(event_base, &IO->send_event);
			}
			break;
		case eSendFile:
			if (IO->IOB.ChunkSendRemain > 0) {
				ev_io_start(event_base, &IO->recv_event);
				SetNextTimeout(IO, 100.0);

			} else {
				assert(IO->ReadDone);
				IO->NextState = IO->ReadDone(IO);
				switch(IO->NextState) {
				case eSendDNSQuery:
				case eReadDNSReply:
				case eDBQuery:
				case eConnect:
					break;
				case eSendReply:
				case eSendMore:
				case eSendFile:
					ev_io_start(event_base,
						    &IO->send_event);
					break;
				case eReadMessage:
				case eReadMore:
				case eReadPayload:
				case eReadFile:
					break;
				case eTerminateConnection:
				case eAbort:
					break;
				}
			}
			break;
		case eSendReply:
		    if (StrBufCheckBuffer(&IO->SendBuf) != eReadSuccess)
			break;
		    IO->NextState = eReadMore;
		case eReadMore:
		case eReadMessage:
		case eReadPayload:
		case eReadFile:
			if (StrBufCheckBuffer(&IO->RecvBuf) == eBufferNotEmpty)
			{
				HandleInbound(IO);
			}
			else {
				ev_io_start(event_base, &IO->recv_event);
			}

			break;
		case eDBQuery:
			/*
			 * we now live in another queue,
			 * so we have to unregister.
			 */
			ev_cleanup_stop(loop, &IO->abort_by_shutdown);
			break;
		case eSendDNSQuery:
		case eReadDNSReply:
		case eConnect:
		case eTerminateConnection:
		case eAbort:
			break;
		}
	}
	else if (rc < 0) {
		if (errno != EAGAIN) {
			StopClientWatchers(IO, 1);
			EV_syslog(LOG_DEBUG,
				  "IO_send_callback(): Socket Invalid! [%d] [%s] [%d]\n",
				  errno, strerror(errno), IO->SendBuf.fd);
			StrBufPrintf(IO->ErrMsg,
				     "Socket Invalid! [%s]",
				     strerror(errno));
			SetNextTimeout(IO, 0.01);
		}
	}
	/* else : must write more. */
}
Пример #28
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);
}
Пример #29
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;
}
Пример #30
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");
}