예제 #1
0
/*
 * Spools out one message from the list.
 */
void network_spool_msg(long msgnum,
		       void *userdata)
{
	struct CitContext *CCC = CC;
	struct CtdlMessage *msg = NULL;
	long delete_after_send = 0;	/* Set to 1 to delete after spooling */
	SpoolControl *sc;

	sc = (SpoolControl *)userdata;

	msg = CtdlFetchMessage(msgnum, 1);

	if (msg == NULL)
	{
		QN_syslog(LOG_ERR,
			  "failed to load Message <%ld> from disk\n",
			  msgnum);
		return;
	}
	network_process_list(sc, msg, &delete_after_send);
	network_process_digest(sc, msg, &delete_after_send);
	network_process_participate(sc, msg, &delete_after_send);
	network_process_ignetpush(sc, msg, &delete_after_send);
	
	CM_Free(msg);

	/* update lastsent */
	sc->lastsent = msgnum;

	/* Delete this message if delete-after-send is set */
	if (delete_after_send) {
		CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, "");
	}
}
예제 #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
void inetcfg_init_backend(long msgnum, void *userdata) {
	struct CtdlMessage *msg;

       	msg = CtdlFetchMessage(msgnum, 1, 1);
       	if (msg != NULL) {
		inetcfg_setTo(msg);
               	CM_Free(msg);
	}
}
예제 #4
0
void dspam_do_msg(long msgnum, void *userdata) 
{
	char *msgtext;
	DSPAM_CTX *CTX;               	/* DSPAM Context */
	struct CtdlMessage *msg;
	struct _ds_spam_signature SIG;        /* signature */

	CTX = *(DSPAM_CTX**) userdata;
	msg = CtdlFetchMessage(msgnum, 0);
	if (msg == NULL) return;


	/* Message */
	CC->redirect_buffer = malloc(SIZ);
	CC->redirect_len = 0;
	CC->redirect_alloc = SIZ;
	CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, 0);
	msgtext = CC->redirect_buffer;
// don't need?	msglen = CC->redirect_len;
	CC->redirect_buffer = NULL;
	CC->redirect_len = 0;
	CC->redirect_alloc = 0;

	/* Call DSPAM's processor with the message text */
	if (dspam_process (CTX, msgtext) != 0)
	{
		free(msgtext);
		syslog(LOG_CRIT, "ERROR: dspam_process failed");
		return;
	}
	if (CTX->signature == NULL)
	{
		syslog(LOG_CRIT,"No signature provided\n");
	}
	else
	{
/* Copy to a safe place */
		// TODO: len -> cm_fields?
		msg->cm_fields[eErrorMsg] = malloc (CTX->signature->length * 2);
		size_t len = CtdlEncodeBase64(msg->cm_fields[eErrorMsg], CTX->signature->data, CTX->signature->length, 0);

		if (msg->cm_fields[eErrorMsg][len - 1] == '\n') {
			msg->cm_fields[eErrorMsg][len - 1] = '\0';
		}
	}
	free(msgtext);

	SIG.length = CTX->signature->length;
	/* Print processing results */
	syslog(LOG_DEBUG, "Probability: %2.4f Confidence: %2.4f, Result: %s\n",
		CTX->probability,
		CTX->confidence,
		(CTX->result == DSR_ISSPAM) ? "Spam" : "Innocent");

	//// todo: put signature into the citadel message
	//// todo: save message; destroy message.
}
예제 #5
0
/*
 * Purge the EUID Index of old records.
 *
 */
int PurgeEuidIndexTable(void) {
	int purged = 0;
	struct cdbdata *cdbei;
	struct EPurgeList *el = NULL;
	struct EPurgeList *eptr; 
	long msgnum;
	struct CtdlMessage *msg = NULL;

	/* Phase 1: traverse through the table, discovering old records... */
	syslog(LOG_DEBUG, "Purge EUID index: phase 1");
	cdb_rewind(CDB_EUIDINDEX);
	while(cdbei = cdb_next_item(CDB_EUIDINDEX), cdbei != NULL) {

		memcpy(&msgnum, cdbei->ptr, sizeof(long));

		msg = CtdlFetchMessage(msgnum, 0);
		if (msg != NULL) {
			CM_Free(msg);	/* it still exists, so do nothing */
		}
		else {
			eptr = (struct EPurgeList *) malloc(sizeof(struct EPurgeList));
			if (eptr != NULL) {
				eptr->next = el;
				eptr->ep_keylen = cdbei->len - sizeof(long);
				eptr->ep_key = malloc(cdbei->len);
				memcpy(eptr->ep_key, &cdbei->ptr[sizeof(long)], eptr->ep_keylen);
				el = eptr;
			}
			++purged;
		}

	       cdb_free(cdbei);

	}

	/* Phase 2: delete the records */
	syslog(LOG_DEBUG, "Purge euid index: phase 2");
	while (el != NULL) {
		cdb_delete(CDB_EUIDINDEX, el->ep_key, el->ep_keylen);
		free(el->ep_key);
		eptr = el->next;
		free(el);
		el = eptr;
	}

	syslog(LOG_DEBUG, "Purge euid index: finished (purged %d records)", purged);
	return(purged);
}
예제 #6
0
/*
 * Back end for the MSGS command: output EUID header.
 */
void headers_euid(long msgnum, void *userdata)
{
	struct CtdlMessage *msg;

	msg = CtdlFetchMessage(msgnum, 0, 1);
	if (msg == NULL) {
		cprintf("%ld||\n", msgnum);
		return;
	}

	cprintf("%ld|%s|%s\n", 
		msgnum, 
		(!CM_IsEmpty(msg, eExclusiveID) ? msg->cm_fields[eExclusiveID] : ""),
		(!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"));
	CM_Free(msg);
}
예제 #7
0
/*
 * Fetch a list of revisions for a particular wiki page
 */
void wiki_history(char *pagename) {
	int r;
	char history_page_name[270];
	long msgnum;
	struct CtdlMessage *msg;

	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;
	}

	snprintf(history_page_name, sizeof history_page_name, "%s_HISTORY_", pagename);
	msgnum = CtdlLocateMessageByEuid(history_page_name, &CC->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;
	}

	
	cprintf("%d Revision history for '%s'\n", LISTING_FOLLOWS, pagename);
	mime_parser(CM_RANGE(msg, eMesageText), *wiki_history_callback, NULL, NULL, NULL, 0);
	cprintf("000\n");

	CM_Free(msg);
	return;
}
예제 #8
0
/*
 * Back end for the MSGS command: output header summary.
 */
void headers_listing(long msgnum, void *userdata)
{
	struct CtdlMessage *msg;

	msg = CtdlFetchMessage(msgnum, 0, 1);
	if (msg == NULL) {
		cprintf("%ld|0|||||\n", msgnum);
		return;
	}

	cprintf("%ld|%s|%s|%s|%s|%s|\n",
		msgnum,
		(!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"),
		(!CM_IsEmpty(msg, eAuthor) ? msg->cm_fields[eAuthor] : ""),
		(!CM_IsEmpty(msg, eNodeName) ? msg->cm_fields[eNodeName] : ""),
		(!CM_IsEmpty(msg, erFc822Addr) ? msg->cm_fields[erFc822Addr] : ""),
		(!CM_IsEmpty(msg, eMsgSubject) ? msg->cm_fields[eMsgSubject] : "")
	);
	CM_Free(msg);
}
예제 #9
0
//
// back end for the XOVER command , called for each message number
//
void nntp_xover_backend(long msgnum, void *userdata) {

	struct listgroup_range *lr = (struct listgroup_range *)userdata;

	// check range if supplied
	if (msgnum < lr->lo) return;
	if ((lr->hi != 0) && (msgnum > lr->hi)) return;

	struct CtdlMessage *msg = CtdlFetchMessage(msgnum, 0, 1);
	if (msg == NULL) {
		return;
	}

	// Teh RFC says we need:
	// -------------------------
	// Subject header content
	// From header content
	// Date header content
	// Message-ID header content
	// References header content
	// :bytes metadata item
	// :lines metadata item

	time_t msgtime = atol(msg->cm_fields[eTimestamp]);
	char strtimebuf[26];
	ctime_r(&msgtime, strtimebuf);

	// here we go -- print the line o'data
	cprintf("%ld\t%s\t%s <%s>\t%s\t%s\t%s\t100\t10\r\n",
		msgnum,
		msg->cm_fields[eMsgSubject],
		msg->cm_fields[eAuthor],
		msg->cm_fields[erFc822Addr],
		strtimebuf,
		msg->cm_fields[emessageId],
		msg->cm_fields[eWeferences]
	);

	CM_Free(msg);
}
예제 #10
0
void xmpp_fetch_mortuary_backend(long msgnum, void *userdata) {
	HashList *mortuary = (HashList *) userdata;
	struct CtdlMessage *msg;
	char *ptr = NULL;
	char *lasts = NULL;

	msg = CtdlFetchMessage(msgnum, 1);
	if (msg == NULL) {
		return;
	}

	/* now add anyone we find into the hashlist */

	/* skip past the headers */
	ptr = strstr(msg->cm_fields[eMesageText], "\n\n");
	if (ptr != NULL) {
		ptr += 2;
	}
	else {
		ptr = strstr(msg->cm_fields[eMesageText], "\n\r\n");
		if (ptr != NULL) {
			ptr += 3;
		}
	}

	/* the remaining lines are addresses */
	if (ptr != NULL) {
		ptr = strtok_r(ptr, "\n", &lasts);
		while (ptr != NULL) {
			char *pch = strdup(ptr);
			Put(mortuary, pch, strlen(pch), pch, NULL);
			ptr = strtok_r(NULL, "\n", &lasts);
		}
	}

	CM_Free(msg);
}
예제 #11
0
/*
 * First phase of message purge -- gather the locations of messages which
 * qualify for purging and write them to a temp file.
 */
void GatherPurgeMessages(struct ctdlroom *qrbuf, void *data) {
	struct ExpirePolicy epbuf;
	long delnum;
	time_t xtime, now;
	struct CtdlMessage *msg = NULL;
	int a;
	struct cdbdata *cdbfr;
	long *msglist = NULL;
	int num_msgs = 0;
	FILE *purgelist;

	purgelist = (FILE *)data;
	fprintf(purgelist, "r=%s\n", qrbuf->QRname);

	time(&now);
	GetExpirePolicy(&epbuf, qrbuf);

	/* If the room is set to never expire messages ... do nothing */
	if (epbuf.expire_mode == EXPIRE_NEXTLEVEL) return;
	if (epbuf.expire_mode == EXPIRE_MANUAL) return;

	/* Don't purge messages containing system configuration, dumbass. */
	if (!strcasecmp(qrbuf->QRname, SYSCONFIGROOM)) return;

	/* Ok, we got this far ... now let's see what's in the room */
	cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf->QRnumber, sizeof(long));

	if (cdbfr != NULL) {
		msglist = malloc(cdbfr->len);
		memcpy(msglist, cdbfr->ptr, cdbfr->len);
		num_msgs = cdbfr->len / sizeof(long);
		cdb_free(cdbfr);
	}

	/* Nothing to do if there aren't any messages */
	if (num_msgs == 0) {
		if (msglist != NULL) free(msglist);
		return;
	}


	/* If the room is set to expire by count, do that */
	if (epbuf.expire_mode == EXPIRE_NUMMSGS) {
		if (num_msgs > epbuf.expire_value) {
			for (a=0; a<(num_msgs - epbuf.expire_value); ++a) {
				fprintf(purgelist, "m=%ld\n", msglist[a]);
				++messages_purged;
			}
		}
	}

	/* If the room is set to expire by age... */
	if (epbuf.expire_mode == EXPIRE_AGE) {
		for (a=0; a<num_msgs; ++a) {
			delnum = msglist[a];

			msg = CtdlFetchMessage(delnum, 0); /* dont need body */
			if (msg != NULL) {
				xtime = atol(msg->cm_fields[eTimestamp]);
				CM_Free(msg);
			} else {
				xtime = 0L;
			}

			if ((xtime > 0L)
			   && (now - xtime > (time_t)(epbuf.expire_value * 86400L))) {
				fprintf(purgelist, "m=%ld\n", delnum);
				++messages_purged;
			}
		}
	}

	if (msglist != NULL) free(msglist);
}
예제 #12
0
/*
 * imap_do_search() calls imap_do_search_msg() to search an individual
 * message after it has been fetched from the disk.  This function returns
 * nonzero if there is a match.
 *
 * supplied_msg MAY be used to pass a pointer to the message in memory,
 * if for some reason it's already been loaded.  If not, the message will
 * be loaded only if one or more search criteria require it.
 */
int imap_do_search_msg(int seq, struct CtdlMessage *supplied_msg,
			int num_items, ConstStr *itemlist, int is_uid) {

	citimap *Imap = IMAP;
	int match = 0;
	int is_not = 0;
	int is_or = 0;
	int pos = 0;
	int i;
	char *fieldptr;
	struct CtdlMessage *msg = NULL;
	int need_to_free_msg = 0;

	if (num_items == 0) {
		return(0);
	}
	msg = supplied_msg;

	/* Initially we start at the beginning. */
	pos = 0;

	/* Check for the dreaded NOT criterion. */
	if (!strcasecmp(itemlist[0].Key, "NOT")) {
		is_not = 1;
		pos = 1;
	}

	/* Check for the dreaded OR criterion. */
	if (!strcasecmp(itemlist[0].Key, "OR")) {
		is_or = 1;
		pos = 1;
	}

	/* Now look for criteria. */
	if (!strcasecmp(itemlist[pos].Key, "ALL")) {
		match = 1;
		++pos;
	}
	
	else if (!strcasecmp(itemlist[pos].Key, "ANSWERED")) {
		if (Imap->flags[seq-1] & IMAP_ANSWERED) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "BCC")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Bcc");
			if (fieldptr != NULL) {
				if (bmstrcasestr(fieldptr, itemlist[pos+1].Key)) {
					match = 1;
				}
				free(fieldptr);
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "BEFORE")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (!CM_IsEmpty(msg, eTimestamp)) {
				if (imap_datecmp(itemlist[pos+1].Key,
						atol(msg->cm_fields[eTimestamp])) < 0) {
					match = 1;
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "BODY")) {

		/* If fulltext indexing is active, on this server,
		 *  all messages have already been qualified.
		 */
		if (config.c_enable_fulltext) {
			match = 1;
		}

		/* Otherwise, we have to do a slow search. */
		else {
			if (msg == NULL) {
				msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
				need_to_free_msg = 1;
			}
			if (msg != NULL) {
				if (bmstrcasestr(msg->cm_fields[eMesageText], itemlist[pos+1].Key)) {
					match = 1;
				}
			}
		}

		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "CC")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			fieldptr = msg->cm_fields[eCarbonCopY];
			if (fieldptr != NULL) {
				if (bmstrcasestr(fieldptr, itemlist[pos+1].Key)) {
					match = 1;
				}
			}
			else {
				fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Cc");
				if (fieldptr != NULL) {
					if (bmstrcasestr(fieldptr, itemlist[pos+1].Key)) {
						match = 1;
					}
					free(fieldptr);
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "DELETED")) {
		if (Imap->flags[seq-1] & IMAP_DELETED) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "DRAFT")) {
		if (Imap->flags[seq-1] & IMAP_DRAFT) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "FLAGGED")) {
		if (Imap->flags[seq-1] & IMAP_FLAGGED) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "FROM")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (bmstrcasestr(msg->cm_fields[eAuthor], itemlist[pos+1].Key)) {
				match = 1;
			}
			if (bmstrcasestr(msg->cm_fields[erFc822Addr], itemlist[pos+1].Key)) {
				match = 1;
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "HEADER")) {

		/* We've got to do a slow search for this because the client
		 * might be asking for an RFC822 header field that has not been
		 * converted into a Citadel header field.  That requires
		 * examining the message body.
		 */
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}

		if (msg != NULL) {
	
			CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
			CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_FAST, 0, 1, 0);
	
			fieldptr = rfc822_fetch_field(ChrPtr(CC->redirect_buffer), itemlist[pos+1].Key);
			if (fieldptr != NULL) {
				if (bmstrcasestr(fieldptr, itemlist[pos+2].Key)) {
					match = 1;
				}
				free(fieldptr);
			}
	
			FreeStrBuf(&CC->redirect_buffer);
		}

		pos += 3;	/* Yes, three */
	}

	else if (!strcasecmp(itemlist[pos].Key, "KEYWORD")) {
		/* not implemented */
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "LARGER")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (msg->cm_lengths[eMesageText] > atoi(itemlist[pos+1].Key)) {
				match = 1;
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "NEW")) {
		if ( (Imap->flags[seq-1] & IMAP_RECENT) && (!(Imap->flags[seq-1] & IMAP_SEEN))) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "OLD")) {
		if (!(Imap->flags[seq-1] & IMAP_RECENT)) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "ON")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (!CM_IsEmpty(msg, eTimestamp)) {
				if (imap_datecmp(itemlist[pos+1].Key,
						atol(msg->cm_fields[eTimestamp])) == 0) {
					match = 1;
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "RECENT")) {
		if (Imap->flags[seq-1] & IMAP_RECENT) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "SEEN")) {
		if (Imap->flags[seq-1] & IMAP_SEEN) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "SENTBEFORE")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (!CM_IsEmpty(msg, eTimestamp)) {
				if (imap_datecmp(itemlist[pos+1].Key,
						atol(msg->cm_fields[eTimestamp])) < 0) {
					match = 1;
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "SENTON")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (!CM_IsEmpty(msg, eTimestamp)) {
				if (imap_datecmp(itemlist[pos+1].Key,
						atol(msg->cm_fields[eTimestamp])) == 0) {
					match = 1;
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "SENTSINCE")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (!CM_IsEmpty(msg, eTimestamp)) {
				if (imap_datecmp(itemlist[pos+1].Key,
						atol(msg->cm_fields[eTimestamp])) >= 0) {
					match = 1;
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "SINCE")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (!CM_IsEmpty(msg, eTimestamp)) {
				if (imap_datecmp(itemlist[pos+1].Key,
						atol(msg->cm_fields[eTimestamp])) >= 0) {
					match = 1;
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "SMALLER")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (msg->cm_lengths[eMesageText] < atoi(itemlist[pos+1].Key)) {
				match = 1;
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "SUBJECT")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (bmstrcasestr(msg->cm_fields[eMsgSubject], itemlist[pos+1].Key)) {
				match = 1;
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "TEXT")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			for (i='A'; i<='Z'; ++i) {
				if (bmstrcasestr(msg->cm_fields[i], itemlist[pos+1].Key)) {
					match = 1;
				}
			}
		}
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "TO")) {
		if (msg == NULL) {
			msg = CtdlFetchMessage(Imap->msgids[seq-1], 1);
			need_to_free_msg = 1;
		}
		if (msg != NULL) {
			if (bmstrcasestr(msg->cm_fields[eRecipient], itemlist[pos+1].Key)) {
				match = 1;
			}
		}
		pos += 2;
	}

	/* FIXME this is b0rken.  fix it. */
	else if (imap_is_message_set(itemlist[pos].Key)) {
		if (is_msg_in_sequence_set(itemlist[pos].Key, seq)) {
			match = 1;
		}
		pos += 1;
	}

	/* FIXME this is b0rken.  fix it. */
	else if (!strcasecmp(itemlist[pos].Key, "UID")) {
		if (is_msg_in_sequence_set(itemlist[pos+1].Key, Imap->msgids[seq-1])) {
			match = 1;
		}
		pos += 2;
	}

	/* Now here come the 'UN' criteria.  Why oh why do we have to
	 * implement *both* the 'UN' criteria *and* the 'NOT' keyword?  Why
	 * can't there be *one* way to do things?  More gratuitous complexity.
	 */

	else if (!strcasecmp(itemlist[pos].Key, "UNANSWERED")) {
		if ((Imap->flags[seq-1] & IMAP_ANSWERED) == 0) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "UNDELETED")) {
		if ((Imap->flags[seq-1] & IMAP_DELETED) == 0) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "UNDRAFT")) {
		if ((Imap->flags[seq-1] & IMAP_DRAFT) == 0) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "UNFLAGGED")) {
		if ((Imap->flags[seq-1] & IMAP_FLAGGED) == 0) {
			match = 1;
		}
		++pos;
	}

	else if (!strcasecmp(itemlist[pos].Key, "UNKEYWORD")) {
		/* FIXME */
		pos += 2;
	}

	else if (!strcasecmp(itemlist[pos].Key, "UNSEEN")) {
		if ((Imap->flags[seq-1] & IMAP_SEEN) == 0) {
			match = 1;
		}
		++pos;
	}

	/* Remember to negate if we were told to */
	if (is_not) {
		match = !match;
	}

	/* Keep going if there are more criteria! */
	if (pos < num_items) {

		if (is_or) {
			match = (match || imap_do_search_msg(seq, msg,
				num_items - pos, &itemlist[pos], is_uid));
		}
		else {
			match = (match && imap_do_search_msg(seq, msg,
				num_items - pos, &itemlist[pos], is_uid));
		}

	}

	if (need_to_free_msg) {
		CM_Free(msg);
	}
	return(match);
}
예제 #13
0
/*
 * Back end for cmd_auto()
 */
void hunt_for_autocomplete(long msgnum, char *search_string) {
	struct CtdlMessage *msg;
	struct vCard *v;
	char *value = NULL;
	char *value2 = NULL;
	int i = 0;
	char *nnn = NULL;

	msg = CtdlFetchMessage(msgnum, 1);
	if (msg == NULL) return;

	v = vcard_load(msg->cm_fields[eMesageText]);
	CM_Free(msg);

	/*
	 * Try to match from a friendly name (the "fn" field).  If there is
	 * a match, return the entry in the form of:
	 *     Display Name <*****@*****.**>
	 */
	value = vcard_get_prop(v, "fn", 0, 0, 0);
	if (value != NULL) if (bmstrcasestr(value, search_string)) {
		value2 = vcard_get_prop(v, "email", 1, 0, 0);
		if (value2 == NULL) value2 = "";
		cprintf("%s <%s>\n", value, value2);
		vcard_free(v);
		return;
	}

	/*
	 * Try to match from a structured name (the "n" field).  If there is
	 * a match, return the entry in the form of:
	 *     Display Name <*****@*****.**>
	 */
	value = vcard_get_prop(v, "n", 0, 0, 0);
	if (value != NULL) if (bmstrcasestr(value, search_string)) {

		value2 = vcard_get_prop(v, "email", 1, 0, 0);
		if (value2 == NULL) value2 = "";
		nnn = n_to_fn(value);
		cprintf("%s <%s>\n", nnn, value2);
		free(nnn);
		vcard_free(v);
		return;
	}

	/*
	 * Try a partial match on all listed email addresses.
	 */
	i = 0;
	while (value = vcard_get_prop(v, "email", 1, i++, 0), value != NULL) {
		if (bmstrcasestr(value, search_string)) {
			if (vcard_get_prop(v, "fn", 0, 0, 0)) {
				cprintf("%s <%s>\n", vcard_get_prop(v, "fn", 0, 0, 0), value);
			}
			else if (vcard_get_prop(v, "n", 0, 0, 0)) {
				nnn = n_to_fn(vcard_get_prop(v, "n", 0, 0, 0));
				cprintf("%s <%s>\n", nnn, value);
				free(nnn);
			
			}
			else {
				cprintf("%s\n", value);
			}
			vcard_free(v);
			return;
		}
	}

	vcard_free(v);
}
예제 #14
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);
}
예제 #15
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;
}
예제 #16
0
/*
 * Index or de-index a message.  (op == 1 to index, 0 to de-index)
 */
void ft_index_message(long msgnum, int op) {
	int num_tokens = 0;
	int *tokens = NULL;
	int i, j;
	struct cdbdata *cdb_bucket;
	StrBuf *msgtext;
	char *txt;
	int tok;
	struct CtdlMessage *msg = NULL;

	msg = CtdlFetchMessage(msgnum, 1, 1);
	if (msg == NULL) {
		syslog(LOG_ERR, "ft_index_message() could not load msg %ld", msgnum);
		return;
	}

	if (!CM_IsEmpty(msg, eSuppressIdx)) {
		syslog(LOG_DEBUG, "ft_index_message() excluded msg %ld", msgnum);
		CM_Free(msg);
		return;
	}

	syslog(LOG_DEBUG, "ft_index_message() %s msg %ld", (op ? "adding" : "removing") , msgnum);

	/* Output the message as text before indexing it, so we don't end up
	 * indexing a bunch of encoded base64, etc.
	 */
	CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
	CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_ALL, 0, 1, 0);
	CM_Free(msg);
	msgtext = CC->redirect_buffer;
	CC->redirect_buffer = NULL;
	if (msgtext != NULL) {
		syslog(LOG_DEBUG, "Wordbreaking message %ld (%d bytes)", msgnum, StrLength(msgtext));
	}
	txt = SmashStrBuf(&msgtext);
	wordbreaker(txt, &num_tokens, &tokens);
	free(txt);

	syslog(LOG_DEBUG, "Indexing message %ld [%d tokens]", msgnum, num_tokens);
	if (num_tokens > 0) {
		for (i=0; i<num_tokens; ++i) {

			/* Add the message to the relevant token bucket */

			/* search for tokens[i] */
			tok = tokens[i];

			if ( (tok >= 0) && (tok <= 65535) ) {
				/* fetch the bucket, Liza */
				if (ftc_msgs[tok] == NULL) {
					cdb_bucket = cdb_fetch(CDB_FULLTEXT, &tok, sizeof(int));
					if (cdb_bucket != NULL) {
						ftc_num_msgs[tok] = cdb_bucket->len / sizeof(long);
						ftc_msgs[tok] = (long *)cdb_bucket->ptr;
						cdb_bucket->ptr = NULL;
						cdb_free(cdb_bucket);
					}
					else {
						ftc_num_msgs[tok] = 0;
						ftc_msgs[tok] = malloc(sizeof(long));
					}
				}
	
	
				if (op == 1) {	/* add to index */
					++ftc_num_msgs[tok];
					ftc_msgs[tok] = realloc(ftc_msgs[tok],
								ftc_num_msgs[tok]*sizeof(long));
					ftc_msgs[tok][ftc_num_msgs[tok] - 1] = msgnum;
				}
	
				if (op == 0) {	/* remove from index */
					if (ftc_num_msgs[tok] >= 1) {
						for (j=0; j<ftc_num_msgs[tok]; ++j) {
							if (ftc_msgs[tok][j] == msgnum) {
								memmove(&ftc_msgs[tok][j], &ftc_msgs[tok][j+1], ((ftc_num_msgs[tok] - j - 1)*sizeof(long)));
								--ftc_num_msgs[tok];
								--j;
							}
						}
					}
				}
			}
			else {
				syslog(LOG_ALERT, "Invalid token %d !!", tok);
			}
		}

		free(tokens);
	}
}
예제 #17
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
	}
}