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

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

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

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

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

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

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

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

	/* Now generate the delivery instructions */

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

	/* Now submit the message */
	valid = validate_recipients(ChrPtr(sc->Users[digestrecp]), NULL, 0);
	if (valid != NULL) {
		valid->bounce_to = strdup(bounce_to);
		valid->envelope_from = strdup(bounce_to);
		CtdlSubmitMsg(msg, valid, NULL, 0);
	}
	CM_Free(msg);
	free_recipients(valid);
}
예제 #2
0
파일: database.c 프로젝트: henri14/citadel
time_t CheckIfAlreadySeen(const char *Facility,
			  StrBuf *guid,
			  time_t now,
			  time_t antiexpire,
			  eCheckType cType,
			  long ccid,
			  long ioid)
{
	time_t InDBTimeStamp = 0;
	struct UseTable ut;
	struct cdbdata *cdbut;

	if (cType != eWrite)
	{
		SEEN_syslog(LOG_DEBUG, "Loading [%s]", ChrPtr(guid));
		cdbut = cdb_fetch(CDB_USETABLE, SKEY(guid));
		if ((cdbut != NULL) && (cdbut->ptr != NULL)) {
			memcpy(&ut, cdbut->ptr,
			       ((cdbut->len > sizeof(struct UseTable)) ?
				sizeof(struct UseTable) : cdbut->len));
			InDBTimeStamp = now - ut.ut_timestamp;

			if (InDBTimeStamp < antiexpire)
			{
				SEEN_syslog(LOG_DEBUG, "Found - Not expired %ld < %ld", InDBTimeStamp, antiexpire);
				cdb_free(cdbut);
				return InDBTimeStamp;
			}
			else
			{
				SEEN_syslog(LOG_DEBUG, "Found - Expired. %ld >= %ld", InDBTimeStamp, antiexpire);
				cdb_free(cdbut);
			}
		}
		else
		{
			if (cdbut) cdb_free(cdbut);

			SEENM_syslog(LOG_DEBUG, "not Found");
		}

		if (cType == eCheckExist)
			return InDBTimeStamp;
	}

	memcpy(ut.ut_msgid, SKEY(guid));
	ut.ut_timestamp = now;

	SEENM_syslog(LOG_DEBUG, "Saving new Timestamp");
	/* rewrite the record anyway, to update the timestamp */
	cdb_store(CDB_USETABLE,
		  SKEY(guid),
		  &ut, sizeof(struct UseTable) );

	SEENM_syslog(LOG_DEBUG, "Done Saving");
	return InDBTimeStamp;
}
예제 #3
0
void network_process_participate(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
{
	struct CtdlMessage *msg = NULL;
	int ok_to_participate = 0;
	StrBuf *Buf = NULL;
	recptypes *valid;

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

	msg = CM_Duplicate(omsg);

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

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

		CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias]));
		CtdlSubmitMsg(msg, valid, "", 0);
		free_recipients(valid);
	}
	FreeStrBuf(&Buf);
	CM_Free(msg);
}
예제 #4
0
파일: webcit.c 프로젝트: henri14/citadel
/*
 * A template has been requested
 */
void url_do_template(void) {
	const StrBuf *MimeType;
	const StrBuf *Tmpl = sbstr("template");
	begin_burst();
	MimeType = DoTemplate(SKEY(Tmpl), NULL, &NoCtx);
	http_transmit_thing(ChrPtr(MimeType), 0);
}
예제 #5
0
파일: netconf.c 프로젝트: mingodad/citadel
/*
 * modify an existing node
 */
void display_edit_node(void)
{
	WCTemplputParams SubTP;
	HashList *NodeConfig;
	const StrBuf *Index;
	void *vNode;
	const StrBuf *Tmpl;

	Index = sbstr("index");
	if (Index == NULL) {
		AppendImportantMessage(_("Invalid Parameter"), -1);
		url_do_template();
		return;
	}

	NodeConfig = load_netconf(NULL, &NoCtx);
	if (!GetHash(NodeConfig, ChrPtr(Index), StrLength(Index), &vNode) || 
	    (vNode == NULL)) {
		AppendImportantMessage(_("Invalid Parameter"), -1);
		url_do_template();
		DeleteHash(&NodeConfig);
		return;
	}
	StackContext(NULL, &SubTP, vNode, CTX_NODECONF, 0, NULL);
	{
		begin_burst();
		Tmpl = sbstr("template");
		output_headers(1, 0, 0, 0, 1, 0);
		DoTemplate(SKEY(Tmpl), NULL, &SubTP);
		end_burst();
	}
	UnStackContext(&SubTP);
	DeleteHash(&NodeConfig);
	
}
예제 #6
0
OneQueItem *DeserializeQueueItem(StrBuf *RawQItem, long QueMsgID)
{
	OneQueItem *Item;
	const char *pLine = NULL;
	StrBuf *Line;
	StrBuf *Token;
	void *v;

	Item = (OneQueItem*)malloc(sizeof(OneQueItem));
	memset(Item, 0, sizeof(OneQueItem));
	Item->Retry = SMTP_RETRY_INTERVAL;
	Item->MessageID = -1;
	Item->QueMsgID = QueMsgID;

	Token = NewStrBuf();
	Line = NewStrBufPlain(NULL, 128);
	while (pLine != StrBufNOTNULL) {
		const char *pItemPart = NULL;
		void *vHandler;

		StrBufExtract_NextToken(Line, RawQItem, &pLine, '\n');
		if (StrLength(Line) == 0) continue;
		StrBufExtract_NextToken(Token, Line, &pItemPart, '|');
		if (GetHash(QItemHandlers, SKEY(Token), &vHandler))
		{
			QItemHandlerStruct *HS;
			HS = (QItemHandlerStruct*) vHandler;
			HS->H(Item, Line, &pItemPart);
		}
	}
	FreeStrBuf(&Line);
	FreeStrBuf(&Token);

	if (Item->Retry >= MaxRetry)
		Item->FailNow = 1;

	pthread_mutex_lock(&ActiveQItemsLock);
	if (GetHash(ActiveQItems,
		    LKEY(Item->MessageID),
		    &v))
	{
		/* WHOOPS. somebody else is already working on this. */
		pthread_mutex_unlock(&ActiveQItemsLock);
		FreeQueItem(&Item);
		return NULL;
	}
	else {
		/* mark our claim on this. */
		Put(ActiveQItems,
		    LKEY(Item->MessageID),
		    Item,
		    HFreeQueItem);
		pthread_mutex_unlock(&ActiveQItemsLock);
	}

	return Item;
}
예제 #7
0
void SortPregetMatter(HashList *Cals)
{
	disp_cal *Cal;
	void *vCal;
	const char *Key;
        long KLen;
	IcalEnumMap *SortMap[10];
	IcalEnumMap *Map;
	void *vSort;
	const char *Next = NULL;
	const StrBuf *SortVector;
	StrBuf *SortBy;
	int i = 0;
	HashPos *It;

	SortVector = SBSTR("ICALSortVec");
	if (SortVector == NULL)
		return;

	for (i = 0; i < 10; i++) SortMap[i] = NULL;
	SortBy = NewStrBuf();
	while (StrBufExtract_NextToken(SortBy, SortVector, &Next, ':') > 0) {
		GetHash(IcalComponentMap, SKEY(SortBy), &vSort);
		Map = (IcalEnumMap*) vSort;
		SortMap[i] = Map;
		i++;
		if (i > 9)
			break;
	}

	if (i == 0)
		return;

	switch (SortMap[i - 1]->map) {
		/*	case */

	default:
		break;
	}

	It = GetNewHashPos(Cals, 0);
	while (GetNextHashPos(Cals, It, &KLen, &Key, &vCal)) {
		i = 0;
		Cal = (disp_cal*) vCal;
		Cal->Status = icalcomponent_get_status(Cal->cal);
		Cal->SortBy = Cal->cal;
		

		while ((SortMap[i] != NULL) && 
		       (Cal->SortBy != NULL)) 
		{
			/****Cal->SortBy = icalcomponent_get_first_property(Cal->SortBy, SortMap[i++]->map); */
		}
	}
}
예제 #8
0
void network_process_list(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
{
	struct CtdlMessage *msg = NULL;

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

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

	msg = CM_Duplicate(omsg);


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

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

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


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

	/* Handle delivery */
	network_deliver_list(msg, sc, CC->room.QRname);
	CM_Free(msg);
}
예제 #9
0
eNextState FinalizePOP3AggrRun(AsyncIO *IO)
{
	HashPos  *It;
	pop3aggr *cpptr = (pop3aggr *)IO->Data;

	EVP3C_syslog(LOG_INFO,
		     "%s@%s: fetched %ld new of %d messages in %fs. bye.",
		     ChrPtr(cpptr->pop3user),
		     ChrPtr(cpptr->Host),
		     cpptr->count,
		     GetCount(cpptr->MsgNumbers), 
		     IO->Now - cpptr->IOStart 
		);

	It = GetNewHashPos(POP3FetchUrls, 0);
	pthread_mutex_lock(&POP3QueueMutex);
	{
		if (GetHashPosFromKey(POP3FetchUrls, SKEY(cpptr->Url), It))
			DeleteEntryFromHash(POP3FetchUrls, It);
	}
	pthread_mutex_unlock(&POP3QueueMutex);
	DeleteHashPos(&It);
	return eAbort;
}
예제 #10
0
파일: control.c 프로젝트: henri14/citadel
void cmd_gvdn(char *argbuf)
{
	const ConfType *pCfg;
	char *confptr;
	long min = atol(argbuf);
	const char *Pos = NULL;
	const char *PPos = NULL;
	const char *HKey;
	long HKLen;
	StrBuf *Line;
	StrBuf *Config;
	StrBuf *Cfg;
	StrBuf *CfgToken;
	HashList *List;
	HashPos *It;
	void *vptr;
	
	List = NewHash(1, NULL);
	Cfg = NewStrBufPlain(config.c_fqdn, -1);
	Put(List, SKEY(Cfg), Cfg, HFreeStrBuf);
	Cfg = NULL;

	confptr = CtdlGetSysConfig(INTERNETCFG);
	Config = NewStrBufPlain(confptr, -1);
	free(confptr);

	Line = NewStrBufPlain(NULL, StrLength(Config));
	CfgToken = NewStrBufPlain(NULL, StrLength(Config));
	while (StrBufSipLine(Line, Config, &Pos))
	{
		if (Cfg == NULL)
			Cfg = NewStrBufPlain(NULL, StrLength(Line));
		PPos = NULL;
		StrBufExtract_NextToken(Cfg, Line, &PPos, '|');
		StrBufExtract_NextToken(CfgToken, Line, &PPos, '|');
		if (GetHash(CfgNameHash, SKEY(CfgToken), &vptr) &&
		    (vptr != NULL))
		{
			pCfg = (ConfType *) vptr;
			if (pCfg->Type <= min)
			{
				Put(List, SKEY(Cfg), Cfg, HFreeStrBuf);
				Cfg = NULL;
			}
		}
	}

	cprintf("%d Valid Domains\n", LISTING_FOLLOWS);
	It = GetNewHashPos(List, 1);
	while (GetNextHashPos(List, It, &HKLen, &HKey, &vptr))
	{
		cputbuf(vptr);
		cprintf("\n");
	}
	cprintf("000\n");

	DeleteHashPos(&It);
	DeleteHash(&List);
	FreeStrBuf(&Cfg);
	FreeStrBuf(&Line);
	FreeStrBuf(&CfgToken);
	FreeStrBuf(&Config);
}
예제 #11
0
/*
 * Scan a room's netconfig to determine whether it requires POP3 aggregation
 */
void pop3client_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCFG)
{
	const RoomNetCfgLine *pLine;
	void *vptr;

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

	if (server_shutting_down) return;

	pLine = OneRNCFG->NetConfigs[pop3client];

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

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

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

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

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


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

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

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

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

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

	}
}
예제 #12
0
파일: dbase.c 프로젝트: KodersCo/hayalevi
/*
 * Load BBS config file.
 */
void load_config(void) {
	FILE *fpConfig;
	char *word = '\0';
	bool fMatch= FALSE;

	log_string("Load_config: Loading config file");

	fclose(fpReserve);
	if (!(fpConfig = fopen(CONFIG_FILE, "r"))) {
		bbs_bug("Load_config: Could not open to read %s", CONFIG_FILE);
		fpReserve = fopen(NULL_FILE, "r");
		config.bbs_name = "(Noname)";
		config.bbs_email = "userid@host";
		config.bbs_host = "localhost";
		config.bbs_port = 3000;
		config.bbs_version = "3.0";
		config.bbs_flags = 0;
		config.bbs_state = "Turkiye";
		return;
	}

	for (;;) {
		word = feof(fpConfig) ? "End" : fread_word(fpConfig);
		fMatch = FALSE;

		switch (UPPER(word[0])) {
		case '*':
		case '#':
			fread_to_eol(fpConfig);
			fMatch = TRUE;
			break;

		case 'E':
			SKEY( "Email", config.bbs_email, fget_string(fpConfig))
			;
			if (!str_cmp(word, "End")) {
				fclose(fpConfig);
				fpReserve = fopen(NULL_FILE, "r");
				log_string("Load_config: Done");
				return;
			}
			break;

		case 'F':
			KEY( "Flags", config.bbs_flags, fread_flag(fpConfig))
			;
			break;

		case 'H':
			SKEY( "Host", config.bbs_host, fget_string(fpConfig))
			;
			break;

		case 'N':
			SKEY( "Name", config.bbs_name, fget_string(fpConfig))
			;
			break;

		case 'P':
			KEY( "Port", config.bbs_port, fread_number(fpConfig))
			;
			break;

		case 'S':
			SKEY( "State", config.bbs_state, fget_string(fpConfig))
			;
			break;

		case 'V':
			SKEY( "Version", config.bbs_version, fget_string(fpConfig))
			;
			break;
		}

		if (!fMatch) {
			bbs_bug("Load_config: No match '%s'", word);
			exit(1);
		}
	}

	return;
}
예제 #13
0
파일: tables.c 프로젝트: vcosta/greedmud
/*
 * New code for loading classes from file.
 */
bool fread_class( char *filename )
{
           FILE        *fp;
    static CLASS_TYPE   class_zero;
           CLASS_TYPE  *cclass;
     const char        *word;
           char         buf [ MAX_STRING_LENGTH ];
           bool         fMatch;
           int          stat;
           int          level;
           int          i;

    sprintf( buf, "%s%s", CLASS_DIR, filename );
    if ( !( fp = fopen( buf, "r" ) ) )
    {
        perror( buf );
        return FALSE;
    }

    strcpy( strArea, filename );
    fpArea = fp;

    cclass = (CLASS_TYPE *) alloc_mem ( sizeof( CLASS_TYPE ) );

    *cclass = class_zero;

    cclass->skill_level	 = (int *) alloc_mem( sizeof( int ) * MAX_SKILL );
    cclass->skill_adept	 = (int *) alloc_mem( sizeof( int ) * MAX_SKILL );
    cclass->skill_rating = (int *) alloc_mem( sizeof( int ) * MAX_SKILL );

    /* Initialize MAX_SPELL marker so noone can use it. */
    cclass->skill_level[MAX_SPELL] = MAX_LEVEL+1;

    for ( i = 0; i < MAX_SKILL; i++ )
    {
	cclass->skill_level[i] = L_APP;
	cclass->skill_adept[i] = 0;
    }

    for ( i = 0; i <= MAX_LEVEL; i++ )
    {
	cclass->title[i][0] = str_dup( "" );
	cclass->title[i][1] = str_dup( "" );
    }
	
    for ( i = 0; i < MAX_POSE; i++ )
    {
	cclass->pose[i][0] = str_dup( "" );
	cclass->pose[i][1] = str_dup( "" );
    }

    for ( ; ; )
    {
    	GET_TOKEN( fp, word, "End" );
	fMatch = FALSE;

	switch ( UPPER( word[0] ) )
	{
	case '*':
	    fMatch = TRUE;
	    fread_to_eol( fp );
	    break;

	case 'A':
            KEY( "AtrPrm", cclass->attr_prime, fread_number( fp, &stat ) );
	    break;

	case 'C':
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
	    {
		fclose( fp );

		if ( !class_first )
		    class_first      = cclass;

		if ( class_last )
		    class_last->next = cclass;

		class_last           = cclass;

		fpArea = NULL;
		return TRUE;
	    }
	    break;

	case 'G':
            KEY( "Guild", cclass->guild, fread_number( fp, &stat ) );
	    break;

	case 'H':
            KEY( "Hpmin", cclass->hp_min, fread_number( fp, &stat ) );
            KEY( "Hpmax", cclass->hp_max, fread_number( fp, &stat ) );
	    break;

	case 'M':
            KEY( "Mana", cclass->fMana, fread_number( fp, &stat ) );
	    break;

	case 'N':
            SKEY( "Nm", cclass->name );
	    break;

	case 'P':
	    if ( !str_cmp( word, "Pose" ) )
	    {
		level = fread_number( fp, &stat );
		i     = fread_number( fp, &stat );

		if ( level < MAX_POSE )
		{
                    free_string( cclass->pose[level][i] );
                    cclass->pose[level][i] = fread_string( fp, &stat );
		}
		else
		    bugf( "Fread_class: invalid pose." );
		fMatch = TRUE;
	    }

	    break;

	case 'S':
            KEY( "SkllAdpt", cclass->max_adept, fread_number( fp, &stat ) );

	    if ( !str_cmp( word, "Skll" ) )
	    {
		int   sn;
		int   value1;
		int   value2;
		int   value3;

		value1 = fread_number( fp, &stat );
		value2 = fread_number( fp, &stat );
		value3 = fread_number( fp, &stat );
		word   = fread_word( fp, &stat );
		sn     = skill_lookup( word );
		if ( sn == -1 )
		{
		    bugf( "Fread_class: unknown skill: %s.", word );
		}
		else
		{
		    cclass->skill_level [sn] = value1;
		    cclass->skill_rating[sn] = value2;
		    cclass->skill_adept [sn] = value3;
		}
		fMatch = TRUE;
	    }

	    break;

	case 'T':
	    KEY( "Thac0", cclass->thac0_00, fread_number( fp, &stat ) );
	    KEY( "Thac47", cclass->thac0_47, fread_number( fp, &stat ) );

	    if ( !str_cmp( word, "Ttle" ) )
	    {
		i  = fread_number( fp, &stat );

		if ( i <= MAX_LEVEL )
		{
                    free_string( cclass->title[i][0] );
                    free_string( cclass->title[i][1] );
                    cclass->title[i][0] = fread_string( fp, &stat );
                    cclass->title[i][1] = fread_string( fp, &stat );
		}
		else
		    bugf( "Fread_class: too many titles." );
		fMatch = TRUE;
	    }

	    break;

	case 'W':
	    SKEY( "WhoNm", cclass->who_name );
	    KEY( "Wpn", cclass->weapon, fread_number( fp, &stat ) );
	    break;
	}

	if ( !fMatch )
	{
            bugf( "load_class_file: no match: %s", word );
	}
    }

    return FALSE;
}
예제 #14
0
파일: tables.c 프로젝트: vcosta/greedmud
/*
 * New code for loading clans from file.
 */
bool fread_clan( CLAN_DATA *clan, FILE *fp )
{
    const char *word;
    bool        fMatch;
    int         stat;

    for ( ; ; )
    {
    	GET_TOKEN( fp, word, "End" );
	fMatch = FALSE;

	switch ( UPPER( word[0] ) )
	{
	case '*':
	    fMatch = TRUE;
	    fread_to_eol( fp );
	    break;

	case 'C':
            SKEY( "Chieftain",   clan->chieftain );
             if ( !str_cmp( word, "Class" ) )
             {
		clan->cclass = class_lookup( temp_fread_string( fp, &stat ) );
             	fMatch = TRUE;
             	break;
             }
            KEY( "ClanHeros",    clan->clanheros, fread_number( fp, &stat ) );
            KEY( "ClanType",     clan->clan_type, fread_number( fp, &stat ) );
            KEY( "ClanObjOne",   clan->clanobj1,  fread_number( fp, &stat ) );
            KEY( "ClanObjTwo",   clan->clanobj2,  fread_number( fp, &stat ) );
            KEY( "ClanObjThree", clan->clanobj3,  fread_number( fp, &stat ) );
	    break;

	case 'D':
            SKEY( "Desc",    clan->description );
            KEY( "Donation", clan->donation,  fread_number( fp, &stat ) );
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
		return TRUE;
	    break;

	case 'I':
            KEY( "IllegalPK",   clan->illegal_pk,  fread_number( fp, &stat ) );
	    break;

	case 'M':
            KEY( "Members",     clan->members, fread_number( fp, &stat ) );
            KEY( "MKills",      clan->mkills,  fread_number( fp, &stat ) );
            KEY( "MDeaths",     clan->mdeaths, fread_number( fp, &stat ) );
            SKEY( "Motto",      clan->motto );
	    break;

	case 'N':
            SKEY( "Name",      clan->name    );
	    break;

	case 'O':
            SKEY( "Overlord", clan->overlord );
	    break;

	case 'P':
            KEY( "PKills",  clan->pkills,  fread_number( fp, &stat ) );
            KEY( "PDeaths", clan->pdeaths, fread_number( fp, &stat ) );
	    break;

	case 'R':
            KEY( "Recall",  clan->recall,  fread_number( fp, &stat ) );
	    break;

	case 'S':
            KEY( "Score",     clan->score,     fread_number( fp, &stat ) );
            KEY( "Subchiefs", clan->subchiefs, fread_number( fp, &stat ) );
	    break;

	case 'W':
            SKEY( "WhoName",   clan->who_name    );
	    break;

	}

	if ( !fMatch )
	{
            bugf( "Load_clan_file: no match: %s", word );
	}
    }

    return FALSE;
}
예제 #15
0
void network_process_ignetpush(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
{
	StrBuf *Recipient;
	StrBuf *RemoteRoom;
	const char *Pos = NULL;
	struct CtdlMessage *msg = NULL;
	struct CitContext *CCC = CC;
	struct ser_ret sermsg;
	char buf[SIZ];
	char filename[PATH_MAX];
	FILE *fp;
	StrBuf *Buf = NULL;
	int i;
	int bang = 0;
	int send = 1;

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

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

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

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

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

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

				/* free the serialized version */
				free(sermsg.ser);
			}
		}
	}
	FreeStrBuf(&Buf);
	FreeStrBuf(&Recipient);
	FreeStrBuf(&RemoteRoom);
	CM_Free(msg);
}
예제 #16
0
int ReadHttpSubject(ParsedHttpHdrs *Hdr, StrBuf *Line, StrBuf *Buf)
{
	const char *Args;
	void *vLine, *vHandler;
	const char *Pos = NULL;

	Hdr->HR.ReqLine = Line;
	/* The requesttype... GET, POST... */
	StrBufExtract_token(Buf, Hdr->HR.ReqLine, 0, ' ');
	if (GetHash(HttpReqTypes, SKEY(Buf), &vLine) &&
	    (vLine != NULL))
	{
		Hdr->HR.eReqType = *(long*)vLine;
	}
	else {
		Hdr->HR.eReqType = eGET;
		return 1;
	}
	StrBufCutLeft(Hdr->HR.ReqLine, StrLength(Buf) + 1);

	/* the HTTP Version... */
	StrBufExtract_token(Buf, Hdr->HR.ReqLine, 1, ' ');
	StrBufCutRight(Hdr->HR.ReqLine, StrLength(Buf) + 1);
	
	if (StrLength(Buf) == 0) {
		Hdr->HR.eReqType = eGET;
		return 1;
	}

	StrBufAppendBuf(Hdr->this_page, Hdr->HR.ReqLine, 0);

	/* chop Filename / query arguments */
	Args = strchr(ChrPtr(Hdr->HR.ReqLine), '?');
	if (Args == NULL) /* whe're not that picky about params... TODO: this will spoil '&' in filenames.*/
		Args = strchr(ChrPtr(Hdr->HR.ReqLine), '&');
	if (Args != NULL) {
		Args ++; /* skip the ? */
		StrBufPlain(Hdr->PlainArgs, 
			    Args, 
			    StrLength(Hdr->HR.ReqLine) -
			    (Args - ChrPtr(Hdr->HR.ReqLine)));
		StrBufCutAt(Hdr->HR.ReqLine, 0, Args - 1);
	} /* don't parse them yet, maybe we don't even care... */
	
	/* now lookup what we are going to do with this... */
	/* skip first slash */
	StrBufExtract_NextToken(Buf, Hdr->HR.ReqLine, &Pos, '/');
	do {
		StrBufExtract_NextToken(Buf, Hdr->HR.ReqLine, &Pos, '/');

		GetHash(HandlerHash, SKEY(Buf), &vHandler),
		Hdr->HR.Handler = (WebcitHandler*) vHandler;
		if (Hdr->HR.Handler == NULL)
			break;
		/*
		 * If the request is prefixed by "/webcit" then chop that off.  This
		 * allows a front end web server to forward all /webcit requests to us
		 * while still using the same web server port for other things.
		 */
		if ((Hdr->HR.Handler->Flags & URLNAMESPACE) != 0)
			continue;
		break;
	} while (1);
	/* remove the handlername from the URL */
	if ((Pos != NULL) && (Pos != StrBufNOTNULL)){
		StrBufCutLeft(Hdr->HR.ReqLine, 
			      Pos - ChrPtr(Hdr->HR.ReqLine));
	}

	if (Hdr->HR.Handler != NULL) {
		if ((Hdr->HR.Handler->Flags & BOGUS) != 0) {
			return 1;
		}
		Hdr->HR.DontNeedAuth = (
			((Hdr->HR.Handler->Flags & ISSTATIC) != 0) ||
			((Hdr->HR.Handler->Flags & ANONYMOUS) != 0)
		);
	}
	else {
		/* If this is a "flat" request for the root, display the configured landing page. */
		int return_value;
		StrBuf *NewLine = NewStrBuf();
		Hdr->HR.DontNeedAuth = 1;
		StrBufAppendPrintf(NewLine, "GET /landing?go=%s?failvisibly=1 HTTP/1.0", ChrPtr(Buf));
		if (verbose) syslog(LOG_DEBUG, "Replacing with: %s", ChrPtr(NewLine));
		return_value = ReadHttpSubject(Hdr, NewLine, Buf);
		FreeStrBuf(&NewLine);
		return return_value;
	}

	return 0;
}
예제 #17
0
void network_process_digest(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send)
{

	struct CtdlMessage *msg = NULL;

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

	/* If there are digest recipients, we have to build a digest */
	if (sc->digestfp == NULL) {
		
		sc->digestfp = create_digest_file(&sc->room, 1);

		if (sc->digestfp == NULL)
			return;

		sc->haveDigest = ftell(sc->digestfp) > 0;
		if (!sc->haveDigest) {
			fprintf(sc->digestfp, "Content-type: text/plain\n\n");
		}
		sc->haveDigest = 1;
	}

	msg = CM_Duplicate(omsg);
	if (msg != NULL) {
		sc->haveDigest = 1;
		fprintf(sc->digestfp,
			" -----------------------------------"
			"------------------------------------"
			"-------\n");
		fprintf(sc->digestfp, "From: ");
		if (!CM_IsEmpty(msg, eAuthor)) {
			fprintf(sc->digestfp,
				"%s ",
				msg->cm_fields[eAuthor]);
		}
		if (!CM_IsEmpty(msg, erFc822Addr)) {
			fprintf(sc->digestfp,
				"<%s> ",
				msg->cm_fields[erFc822Addr]);
		}
		else if (!CM_IsEmpty(msg, eNodeName)) {
			fprintf(sc->digestfp,
				"@%s ",
				msg->cm_fields[eNodeName]);
		}
		fprintf(sc->digestfp, "\n");
		if (!CM_IsEmpty(msg, eMsgSubject)) {
			fprintf(sc->digestfp,
				"Subject: %s\n",
				msg->cm_fields[eMsgSubject]);
		}

		CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);

		safestrncpy(CC->preferred_formats,
			    "text/plain",
			    sizeof CC->preferred_formats);

		CtdlOutputPreLoadedMsg(msg,
				       MT_CITADEL,
				       HEADERS_NONE,
				       0, 0, 0);

		StrBufTrim(CC->redirect_buffer);
		fwrite(HKEY("\n"), 1, sc->digestfp);
		fwrite(SKEY(CC->redirect_buffer), 1, sc->digestfp);
		fwrite(HKEY("\n"), 1, sc->digestfp);

		FreeStrBuf(&CC->redirect_buffer);

		sc->num_msgs_spooled += 1;
		CM_Free(msg);
	}
}
예제 #18
0
/*
 * Read in the request
 */
int ReadHTTPRequest (ParsedHttpHdrs *Hdr)
{
	const char *pch, *pchs, *pche;
	OneHttpHeader *pHdr;
	StrBuf *Line, *LastLine, *HeaderName;
	int nLine = 0;
	void *vF;
	int isbogus = 0;

	HeaderName = NewStrBuf();
	LastLine = NULL;
	do {
		nLine ++;
		Line = NewStrBufPlain(NULL, SIZ / 4);

		if (ClientGetLine(Hdr, Line) < 0) return 1;

		if (StrLength(Line) == 0) {
			FreeStrBuf(&Line);
			continue;
		}
		if (nLine == 1) {
			Hdr->HTTPHeaders = NewHash(1, NULL);
			pHdr = (OneHttpHeader*) malloc(sizeof(OneHttpHeader));
			memset(pHdr, 0, sizeof(OneHttpHeader));
			pHdr->Val = Line;
			Put(Hdr->HTTPHeaders, HKEY("GET /"), pHdr, DestroyHttpHeaderHandler);
			if (verbose || strstr(ChrPtr(Line), "sslg") == NULL)
				syslog(LOG_DEBUG, "%s", ChrPtr(Line));
			isbogus = ReadHttpSubject(Hdr, Line, HeaderName);
			if (isbogus) break;
			continue;
		}

		/* Do we need to Unfold? */
		if ((LastLine != NULL) && 
		    (isspace(*ChrPtr(Line)))) {
			pch = pchs = ChrPtr(Line);
			pche = pchs + StrLength(Line);
			while (isspace(*pch) && (pch < pche))
				pch ++;
			StrBufCutLeft(Line, pch - pchs);
			StrBufAppendBuf(LastLine, Line, 0);

			FreeStrBuf(&Line);
			continue;
		}

		StrBufSanitizeAscii(Line, (char)0xa7);
		StrBufExtract_token(HeaderName, Line, 0, ':');

		pchs = ChrPtr(Line);
		pche = pchs + StrLength(Line);
		pch = pchs + StrLength(HeaderName) + 1;
		pche = pchs + StrLength(Line);
		while ((pch < pche) && isspace(*pch))
			pch ++;
		StrBufCutLeft(Line, pch - pchs);

		StrBufUpCase(HeaderName);

		pHdr = (OneHttpHeader*) malloc(sizeof(OneHttpHeader));
		memset(pHdr, 0, sizeof(OneHttpHeader));
		pHdr->Val = Line;

		if (GetHash(HttpHeaderHandler, SKEY(HeaderName), &vF) &&
		    (vF != NULL))
		{
			OneHttpHeader *FHdr = (OneHttpHeader*) vF;
			pHdr->H = FHdr->H;
			pHdr->HaveEvaluator = 1;
		}
		Put(Hdr->HTTPHeaders, SKEY(HeaderName), pHdr, DestroyHttpHeaderHandler);
		LastLine = Line;
	} while (Line != NULL);

	FreeStrBuf(&HeaderName);

	return isbogus;
}
예제 #19
0
파일: imap_acl.c 프로젝트: mingodad/citadel
/*
 * Implements the GETACL command.
 */
void imap_getacl(int num_parms, ConstStr *Params) {
	char roomname[ROOMNAMELEN];
	char savedroom[ROOMNAMELEN];
	int msgs, new;
	int ret;
	struct ctdluser temp;
	struct cdbdata *cdbus;
	int ra;
	StrBuf *rights;

	if (num_parms != 3) {
		IReply("BAD usage error");
		return;
	}

	/*
	 * Search for the specified room or folder
	 */
	ret = imap_grabroom(roomname, Params[2].Key, 1);
	if (ret != 0) {
		IReply("NO Invalid mailbox name or access denied");
		return;
	}

	/*
	 * CtdlUserGoto() formally takes us to the desired room.  (If another
	 * folder is selected, save its name so we can return there!!!!!)
	 */
	if (IMAP->selected) {
		strcpy(savedroom, CC->room.QRname);
	}
	CtdlUserGoto(roomname, 0, 0, &msgs, &new, NULL, NULL);

	IAPuts("* ACL ");
	IPutCParamStr(2);

	/*
	 * Traverse the userlist
	 */
	rights = NewStrBuf();
	cdb_rewind(CDB_USERS);
	while (cdbus = cdb_next_item(CDB_USERS), cdbus != NULL) 
	{
		memset(&temp, 0, sizeof temp);
		memcpy(&temp, cdbus->ptr, sizeof temp);
		cdb_free(cdbus);

		CtdlRoomAccess(&CC->room, &temp, &ra, NULL);
		if (!IsEmptyStr(temp.fullname)) {
			imap_acl_flags(rights, ra);
			if (StrLength(rights) > 0) {
				IAPuts(" ");
				IPutStr(temp.fullname, strlen(temp.fullname));
				IAPuts(" ");
				iaputs(SKEY( rights));
			}
		}
	}
	FreeStrBuf(&rights);
	IAPuts("\r\n");

	/*
	 * If another folder is selected, go back to that room so we can resume
	 * our happy day without violent explosions.
	 */
	if (IMAP->selected) {
		CtdlUserGoto(savedroom, 0, 0, &msgs, &new, NULL, NULL);
	}
예제 #20
0
파일: tables.c 프로젝트: vcosta/greedmud
void fread_social( FILE *fp )
{
    const char     *word;
    SOC_INDEX_DATA *social;
    bool            fMatch;
    int             stat;

    social = new_social( );

    for ( ; ; )
    {
    	GET_TOKEN( fp, word, "End" );
	fMatch = FALSE;

	switch ( UPPER( word[0] ) )
	{
	case '*':
	    fMatch = TRUE;
	    fread_to_eol( fp );
	    break;

	case 'C':
	    SKEY( "CharNoArg", social->char_no_arg );
	    SKEY( "CharFound", social->char_found  );
	    SKEY( "CharAuto",  social->char_auto   );
	    break;

	case 'E':
	    if ( !str_cmp( word, "End" ) )
	    {
		if ( !social->name )
		{
		    bugf( "Fread_social: Name not found" );
		    free_social( social );
		    return;
		}
		if ( !social->char_no_arg )
		{
		    bugf( "Fread_social: CharNoArg not found" );
		    free_social( social );
		    return;
		}
		add_social( social );
		return;
	    }
	    break;

	case 'N':
	    SKEY( "Name", social->name );
	    break;

	case 'O':
	    SKEY( "OthersNoArg", social->others_no_arg );
	    SKEY( "OthersFound", social->others_found  );
	    SKEY( "OthersAuto",	 social->others_auto   );
	    break;

	case 'V':
	    SKEY( "VictFound", social->vict_found );
	    break;
	}
	
	if ( !fMatch )
	{
            bugf( "Fread_social: no match: %s. Skipping to next line.", word );
	    fread_to_eol( fp );
	}
    }

    return;
}
예제 #21
0
//
// Implements the ARTICLE, HEAD, BODY, and STAT commands.
// (These commands all accept the same parameters; they differ only in how they output the retrieved message.)
//
void nntp_article(const char *cmd) {
	if (CtdlAccessCheck(ac_logged_in_or_guest)) return;

	citnntp *nntpstate = (citnntp *) CC->session_specific_data;
	char which_command[16];
	int acmd = 0;
	char requested_article[256];
	long requested_msgnum = 0;
	char *lb, *rb = NULL;
	int must_change_currently_selected_article = 0;

	// We're going to store one of these values in the variable 'acmd' so that
	// we can quickly check later which version of the output we want.
	enum {
		ARTICLE,
		HEAD,
		BODY,
		STAT
	};

	extract_token(which_command, cmd, 0, ' ', sizeof which_command);

	if (!strcasecmp(which_command, "article")) {
		acmd = ARTICLE;
	}
	else if (!strcasecmp(which_command, "head")) {
		acmd = HEAD;
	}
	else if (!strcasecmp(which_command, "body")) {
		acmd = BODY;
	}
	else if (!strcasecmp(which_command, "stat")) {
		acmd = STAT;
	}
	else {
		cprintf("500 I'm afraid I can't do that.\r\n");
		return;
	}

	// Which NNTP command was issued, determines whether we will fetch headers, body, or both.
	int			headers_only = HEADERS_ALL;
	if (acmd == HEAD)	headers_only = HEADERS_FAST;
	else if (acmd == BODY)	headers_only = HEADERS_NONE;
	else if (acmd == STAT)	headers_only = HEADERS_FAST;

	// now figure out what the client is asking for.
	extract_token(requested_article, cmd, 1, ' ', sizeof requested_article);
	lb = strchr(requested_article, '<');
	rb = strchr(requested_article, '>');
	requested_msgnum = atol(requested_article);

	// If no article number or message-id is specified, the client wants the "currently selected article"
	if (IsEmptyStr(requested_article)) {
		if (nntpstate->current_article_number < 1) {
			cprintf("420 No current article selected\r\n");
			return;
		}
		requested_msgnum = nntpstate->current_article_number;
		must_change_currently_selected_article = 1;
		// got it -- now fall through and keep going
	}

	// If the requested article is numeric, it maps directly to a message number.  Good.
	else if (requested_msgnum > 0) {
		must_change_currently_selected_article = 1;
		// good -- fall through and keep going
	}

	// If the requested article has angle brackets, the client wants a specific message-id.
	// We don't know how to do that yet.
	else if ( (lb != NULL) && (rb != NULL) && (lb < rb) ) {
		must_change_currently_selected_article = 0;
		cprintf("500 I don't know how to fetch by message-id yet.\r\n");	// FIXME
		return;
	}

	// Anything else is noncompliant gobbledygook and should die in a car fire.
	else {
		must_change_currently_selected_article = 0;
		cprintf("500 syntax error\r\n");
		return;
	}

	// At this point we know the message number of the "article" being requested.
	// We have an awesome API call that does all the heavy lifting for us.
	char *fetched_message_id = NULL;
	CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
	int fetch = CtdlOutputMsg(requested_msgnum,
			MT_RFC822,		// output in RFC822 format ... sort of
			headers_only,		// headers, body, or both?
			0,			// don't do Citadel protocol responses
			1,			// CRLF newlines
			NULL,			// teh whole thing, not just a section
			0,			// no flags yet ... maybe new ones for Path: etc ?
			NULL,
			NULL,
			&fetched_message_id	// extract the message ID from the message as we go...
	);
	StrBuf *msgtext = CC->redirect_buffer;
	CC->redirect_buffer = NULL;

	if (fetch != om_ok) {
		cprintf("423 no article with that number\r\n");
		FreeStrBuf(&msgtext);
		return;
	}

	// RFC3977 6.2.1.2 specifes conditions under which the "currently selected article"
	// MUST or MUST NOT be set to the message we just referenced.
	if (must_change_currently_selected_article) {
		nntpstate->current_article_number = requested_msgnum;
	}

	// Now give the client what it asked for.
	if (acmd == ARTICLE) {
		cprintf("220 %ld <%s>\r\n", requested_msgnum, fetched_message_id);
	}
	if (acmd == HEAD) {
		cprintf("221 %ld <%s>\r\n", requested_msgnum, fetched_message_id);
	}
	if (acmd == BODY) {
		cprintf("222 %ld <%s>\r\n", requested_msgnum, fetched_message_id);
	}
	if (acmd == STAT) {
		cprintf("223 %ld <%s>\r\n", requested_msgnum, fetched_message_id);
		FreeStrBuf(&msgtext);
		return;
	}

	client_write(SKEY(msgtext));
	cprintf(".\r\n");			// this protocol uses a dot terminator
	FreeStrBuf(&msgtext);
	if (fetched_message_id) free(fetched_message_id);
}