コード例 #1
0
ファイル: serv_user.c プロジェクト: mingodad/citadel
/*
 * Administrative Get User Parameters
 */
void cmd_agup(char *cmdbuf)
{
	struct ctdluser usbuf;
	char requested_user[128];

	if (CtdlAccessCheck(ac_aide)) {
		return;
	}

	extract_token(requested_user, cmdbuf, 0, '|', sizeof requested_user);
	if (CtdlGetUser(&usbuf, requested_user) != 0) {
		cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
		return;
	}
	cprintf("%d %s|%s|%u|%ld|%ld|%d|%ld|%ld|%d\n",
		CIT_OK,
		usbuf.fullname,
		usbuf.password,
		usbuf.flags,
		usbuf.timescalled,
		usbuf.posted,
		(int) usbuf.axlevel,
		usbuf.usernum,
		(long)usbuf.lastcall,
		usbuf.USuserpurge);
}
コード例 #2
0
ファイル: serv_user.c プロジェクト: mingodad/citadel
/*
 * assorted info we need to check at login
 */
void cmd_chek(char *argbuf)
{
	int mail = 0;
	int regis = 0;
	int vali = 0;

	if (CtdlAccessCheck(ac_logged_in)) {
		return;
	}

	CtdlGetUser(&CC->user, CC->curr_user);	/* no lock is needed here */
	if ((REGISCALL != 0) && ((CC->user.flags & US_REGIS) == 0))
		regis = 1;

	if (CC->user.axlevel >= AxAideU) {
		if (CtdlGetConfigInt("MMflags") & MM_VALID) {
			vali = 1;
		}
	}

	/* check for mail */
	mail = InitialMailCheck();

	cprintf("%d %d|%d|%d|%s|\n", CIT_OK, mail, regis, vali, CC->cs_inet_email);
}
コード例 #3
0
ファイル: serv_user.c プロジェクト: mingodad/citadel
/*
 * get user parameters
 */
void cmd_getu(char *cmdbuf)
{
	if (CtdlAccessCheck(ac_logged_in))
		return;

	CtdlGetUser(&CC->user, CC->curr_user);
	cprintf("%d 80|24|%d|\n", CIT_OK, (CC->user.flags & US_USER_SET));
}
コード例 #4
0
ファイル: serv_user.c プロジェクト: mingodad/citadel
/*
 * check to see if a user exists
 */
void cmd_qusr(char *who)
{
	struct ctdluser usbuf;

	if (CtdlGetUser(&usbuf, who) == 0) {
		cprintf("%d %s\n", CIT_OK, usbuf.fullname);
	} else {
		cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
	}
}
コード例 #5
0
ファイル: serv_nntp.c プロジェクト: mingodad/citadel
//
// Implements the LIST commands
//
void nntp_list(const char *cmd) {
	if (CtdlAccessCheck(ac_logged_in_or_guest)) return;

	char list_format[64];
	char wildmat_pattern[1024];
	struct nntp_list_data nld;

	extract_token(list_format, cmd, 1, ' ', sizeof list_format);
	extract_token(wildmat_pattern, cmd, 2, ' ', sizeof wildmat_pattern);

	if (strlen(wildmat_pattern) > 0) {
		nld.wildmat_pattern = wildmat_pattern;
	}
	else {
		nld.wildmat_pattern = NULL;
	}

	if ( (strlen(cmd) < 6) || (!strcasecmp(list_format, "ACTIVE")) ) {
		nld.list_format = NNTP_LIST_ACTIVE;
	}
	else if (!strcasecmp(list_format, "NEWSGROUPS")) {
		nld.list_format = NNTP_LIST_NEWSGROUPS;
	}
	else if (!strcasecmp(list_format, "OVERVIEW.FMT")) {
		nld.list_format = NNTP_LIST_OVERVIEW_FMT;
	}
	else {
		cprintf("501 syntax error , unsupported list format\r\n");
		return;
	}

	// OVERVIEW.FMT delivers a completely different type of data than all of the
	// other LIST commands.  It's a stupid place to put it.  But that's how it's
	// written into RFC3977, so we have to handle it here.
	if (nld.list_format == NNTP_LIST_OVERVIEW_FMT) {
		cprintf("215 Order of fields in overview database.\r\n");
		cprintf("Subject:\r\n");
		cprintf("From:\r\n");
		cprintf("Date:\r\n");
		cprintf("Message-ID:\r\n");
		cprintf("References:\r\n");
		cprintf("Bytes:\r\n");
		cprintf("Lines:\r\n");
		cprintf(".\r\n");
		return;
	}

	cprintf("215 list of newsgroups follows\r\n");
	CtdlGetUser(&CC->user, CC->curr_user);
	CtdlForEachRoom(nntp_list_backend, &nld);
	cprintf(".\r\n");
}
コード例 #6
0
ファイル: serv_rooms.c プロジェクト: zcw159357/citadel
void cmd_lrms(char *argbuf)
{
	int FloorBeingSearched = (-1);
	if (!IsEmptyStr(argbuf))
		FloorBeingSearched = extract_int(argbuf, 0);

	if (CtdlAccessCheck(ac_logged_in_or_guest)) return;

	CtdlGetUser(&CC->user, CC->curr_user);
	cprintf("%d Accessible rooms:\n", LISTING_FOLLOWS);

	CtdlForEachRoom(cmd_lrms_backend, &FloorBeingSearched);
	cprintf("000\n");
}
コード例 #7
0
ファイル: journaling.c プロジェクト: henri14/citadel
/*
 * TODODRW: change this into a CtdlModuleDo type function that returns alternative address info
 * for this local user. Something like CtdlModuleGoGetAddr(char *localuser, int type, char *alt_addr, size_t alt_addr_len)
 * where type can be ADDR_EMAIL, ADDR_FIDO, ADDR_UUCP, ADDR_WEB, ADDR_POSTAL etc etc.
 * This then begs the question of what should be returned. Is it wise to return a single char* using a comma as a
 * delimiter? Or would it be better to return a linked list of some kind?
 */
void local_to_inetemail(char *inetemail, char *localuser, size_t inetemail_len) {
	struct ctdluser us;
	struct vCard *v;

	strcpy(inetemail, "");
	if (CtdlGetUser(&us, localuser) != 0) {
		return;
	}

	v = vcard_get_user(&us);
	if (v == NULL) {
		return;
	}

	extract_inet_email_addrs(inetemail, inetemail_len, NULL, 0, v, 1);
	vcard_free(v);
}
コード例 #8
0
ファイル: serv_rooms.c プロジェクト: zcw159357/citadel
/* 
 * set the room admin for this room
 */
void cmd_seta(char *new_ra)
{
	struct ctdluser usbuf;
	long newu;
	char buf[SIZ];
	int post_notice;

	if (CtdlAccessCheck(ac_room_aide)) return;

	if (CtdlGetUser(&usbuf, new_ra) != 0) {
		newu = (-1L);
	} else {
		newu = usbuf.usernum;
	}

	CtdlGetRoomLock(&CC->room, CC->room.QRname);
	post_notice = 0;
	if (CC->room.QRroomaide != newu) {
		post_notice = 1;
	}
	CC->room.QRroomaide = newu;
	CtdlPutRoomLock(&CC->room);

	/*
	 * We have to post the change notice _after_ writing changes to 
	 * the room table, otherwise it would deadlock!
	 */
	if (post_notice == 1) {
		if (!IsEmptyStr(usbuf.fullname))
			snprintf(buf, sizeof buf,
				"%s is now the room admin for \"%s\".\n",
				usbuf.fullname, CC->room.QRname);
		else
			snprintf(buf, sizeof buf,
				"There is now no room admin for \"%s\".\n",
				CC->room.QRname);
		CtdlAideMessage(buf, "Admin Room Modification");
	}
	cprintf("%d Ok\n", CIT_OK);
}
コード例 #9
0
ファイル: serv_nntp.c プロジェクト: mingodad/citadel
//
// Implements the NEWGROUPS command
//
void nntp_newgroups(const char *cmd) {
	if (CtdlAccessCheck(ac_logged_in_or_guest)) return;

	char stringy_date[16];
	char stringy_time[16];
	char stringy_gmt[16];
	struct tm tm;
	time_t thetime;

	extract_token(stringy_date, cmd, 1, ' ', sizeof stringy_date);
	extract_token(stringy_time, cmd, 2, ' ', sizeof stringy_time);
	extract_token(stringy_gmt, cmd, 3, ' ', sizeof stringy_gmt);

	memset(&tm, 0, sizeof tm);
	if (strlen(stringy_date) == 6) {
		sscanf(stringy_date, "%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
		tm.tm_year += 100;
	}
	else {
		sscanf(stringy_date, "%4d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
		tm.tm_year -= 1900;
	}
	tm.tm_mon -= 1;		// tm_mon is zero based (0=January)
	tm.tm_isdst = (-1);	// let the C library figure out whether DST is in effect
	sscanf(stringy_time, "%2d%2d%2d", &tm.tm_hour, &tm.tm_min ,&tm.tm_sec);
	thetime = mktime(&tm);
	if (!strcasecmp(stringy_gmt, "GMT")) {
		tzset();
		thetime += timezone;
	}


	cprintf("231 list of new newsgroups follows\r\n");
	CtdlGetUser(&CC->user, CC->curr_user);
	CtdlForEachRoom(nntp_newgroups_backend, &thetime);
	cprintf(".\r\n");
}
コード例 #10
0
ファイル: serv_openid_rp.c プロジェクト: mingodad/citadel
/*
 * Attempt to auto-create a new Citadel account using the nickname from Attribute Exchange
 */
int openid_create_user_via_ax(StrBuf *claimed_id, HashList *sreg_keys)
{
	char *nickname = NULL;
	char *firstname = NULL;
	char *lastname = NULL;
	char new_password[32];
	long len;
	const char *Key;
	void *Value;

	if (CtdlGetConfigInt("c_auth_mode") != AUTHMODE_NATIVE) return(1);
	if (CtdlGetConfigInt("c_disable_newu")) return(2);
	if (CC->logged_in) return(3);

	HashPos *HashPos = GetNewHashPos(sreg_keys, 0);
	while (GetNextHashPos(sreg_keys, HashPos, &len, &Key, &Value) != 0) {
		syslog(LOG_DEBUG, "%s = %s", Key, (char *)Value);

		if (cbmstrcasestr(Key, "value.nickname") != NULL) {
			nickname = (char *)Value;
		}
		else if ( (nickname == NULL) && (cbmstrcasestr(Key, "value.nickname") != NULL)) {
			nickname = (char *)Value;
		}
		else if (cbmstrcasestr(Key, "value.firstname") != NULL) {
			firstname = (char *)Value;
		}
		else if (cbmstrcasestr(Key, "value.lastname") != NULL) {
			lastname = (char *)Value;
		}

	}
	DeleteHashPos(&HashPos);

	if (nickname == NULL) {
		if ((firstname != NULL) || (lastname != NULL)) {
			char fullname[1024] = "";
			if (firstname) strcpy(fullname, firstname);
			if (firstname && lastname) strcat(fullname, " ");
			if (lastname) strcat(fullname, lastname);
			nickname = fullname;
		}
	}

	if (nickname == NULL) {
		return(4);
	}
	syslog(LOG_DEBUG, "The desired account name is <%s>", nickname);

	len = cutuserkey(nickname);
	if (!CtdlGetUser(&CC->user, nickname)) {
		syslog(LOG_DEBUG, "<%s> is already taken by another user.", nickname);
		memset(&CC->user, 0, sizeof(struct ctdluser));
		return(5);
	}

	/* The desired account name is available.  Create the account and log it in! */
	if (create_user(nickname, len, 1)) return(6);

	/* Generate a random password.
	 * The user doesn't care what the password is since he is using OpenID.
	 */
	snprintf(new_password, sizeof new_password, "%08lx%08lx", random(), random());
	CtdlSetPassword(new_password);

	/* Now attach the verified OpenID to this account. */
	attach_openid(&CC->user, claimed_id);

	return(0);
}
コード例 #11
0
ファイル: serv_rooms.c プロジェクト: zcw159357/citadel
/*
 * RDIR command for room directory
 */
void cmd_rdir(char *cmdbuf)
{
	char buf[256];
	char comment[256];
	FILE *fd;
	struct stat statbuf;
	DIR *filedir = NULL;
	struct dirent *filedir_entry;
	int d_namelen;
	char buf2[SIZ];
	char mimebuf[64];
	long len;
	
	if (CtdlAccessCheck(ac_logged_in)) return;
	
	CtdlGetRoom(&CC->room, CC->room.QRname);
	CtdlGetUser(&CC->user, CC->curr_user);

	if ((CC->room.QRflags & QR_DIRECTORY) == 0) {
		cprintf("%d not here.\n", ERROR + NOT_HERE);
		return;
	}
	if (((CC->room.QRflags & QR_VISDIR) == 0)
	    && (CC->user.axlevel < AxAideU)
	    && (CC->user.usernum != CC->room.QRroomaide)) {
		cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
		return;
	}

	snprintf(buf, sizeof buf, "%s/%s", ctdl_file_dir, CC->room.QRdirname);
	filedir = opendir (buf);
	
	if (filedir == NULL) {
		cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED);
		return;
	}
	cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS, CtdlGetConfigStr("c_fqdn"), ctdl_file_dir, CC->room.QRdirname);
	
	snprintf(buf, sizeof buf, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname);
	fd = fopen(buf, "r");
	if (fd == NULL)
		fd = fopen("/dev/null", "r");
	while ((filedir_entry = readdir(filedir)))
	{
		if (strcasecmp(filedir_entry->d_name, "filedir") && filedir_entry->d_name[0] != '.')
		{
#ifdef _DIRENT_HAVE_D_NAMELEN
			d_namelen = filedir_entry->d_namlen;
#else
			d_namelen = strlen(filedir_entry->d_name);
#endif
			snprintf(buf, sizeof buf, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filedir_entry->d_name);
			stat(buf, &statbuf);	/* stat the file */
			if (!(statbuf.st_mode & S_IFREG))
			{
				snprintf(buf2, sizeof buf2,
					"\"%s\" appears in the file directory for room \"%s\" but is not a regular file.  Directories, named pipes, sockets, etc. are not usable in Citadel room directories.\n",
					buf, CC->room.QRname
				);
				CtdlAideMessage(buf2, "Unusable data found in room directory");
				continue;	/* not a useable file type so don't show it */
			}
			safestrncpy(comment, "", sizeof comment);
			fseek(fd, 0L, 0);	/* rewind descriptions file */
			/* Get the description from the descriptions file */
			while ((fgets(buf, sizeof buf, fd) != NULL) && (IsEmptyStr(comment))) 
			{
				buf[strlen(buf) - 1] = 0;
				if ((!strncasecmp(buf, filedir_entry->d_name, d_namelen)) && (buf[d_namelen] == ' '))
					safestrncpy(comment, &buf[d_namelen + 1], sizeof comment);
			}
			len = extract_token (mimebuf, comment, 0,' ', 64);
			if ((len <0) || strchr(mimebuf, '/') == NULL)
			{
				snprintf (mimebuf, 64, "application/octetstream");
				len = 0;
			}
			cprintf("%s|%ld|%s|%s\n", 
				filedir_entry->d_name, 
				(long)statbuf.st_size, 
				mimebuf, 
				&comment[len]);
		}
	}
	fclose(fd);
	closedir(filedir);
	
	cprintf("000\n");
}
コード例 #12
0
ファイル: serv_rooms.c プロジェクト: zcw159357/citadel
/* 
 * cmd_goto()  -  goto a new room
 */
void cmd_goto(char *gargs)
{
	struct ctdlroom QRscratch;
	int c;
	int ok = 0;
	int ra;
	char augmented_roomname[ROOMNAMELEN];
	char towhere[ROOMNAMELEN];
	char password[32];
	int transiently = 0;

	if (CtdlAccessCheck(ac_logged_in_or_guest)) return;

	extract_token(towhere, gargs, 0, '|', sizeof towhere);
	extract_token(password, gargs, 1, '|', sizeof password);
	transiently = extract_int(gargs, 2);

	CtdlGetUser(&CC->user, CC->curr_user);

	/*
	 * Handle some of the macro named rooms
	 */
	convert_room_name_macros(towhere, sizeof towhere);

	/* First try a regular match */
	c = CtdlGetRoom(&QRscratch, towhere);

	/* Then try a mailbox name match */
	if (c != 0) {
		CtdlMailboxName(augmented_roomname, sizeof augmented_roomname,
			    &CC->user, towhere);
		c = CtdlGetRoom(&QRscratch, augmented_roomname);
		if (c == 0)
			safestrncpy(towhere, augmented_roomname, sizeof towhere);
	}

	/* And if the room was found... */
	if (c == 0) {

		/* Let internal programs go directly to any room. */
		if (CC->internal_pgm) {
			memcpy(&CC->room, &QRscratch,
				sizeof(struct ctdlroom));
			CtdlUserGoto(NULL, 1, transiently, NULL, NULL, NULL, NULL);
			return;
		}

		/* See if there is an existing user/room relationship */
		CtdlRoomAccess(&QRscratch, &CC->user, &ra, NULL);

		/* normal clients have to pass through security */
		if (ra & UA_GOTOALLOWED) {
			ok = 1;
		}

		if (ok == 1) {
			if ((QRscratch.QRflags & QR_MAILBOX) &&
			    ((ra & UA_GOTOALLOWED))) {
				memcpy(&CC->room, &QRscratch,
					sizeof(struct ctdlroom));
				CtdlUserGoto(NULL, 1, transiently, NULL, NULL, NULL, NULL);
				return;
			} else if ((QRscratch.QRflags & QR_PASSWORDED) &&
			    ((ra & UA_KNOWN) == 0) &&
			    (strcasecmp(QRscratch.QRpasswd, password)) &&
			    (CC->user.axlevel < AxAideU)
			    ) {
				cprintf("%d wrong or missing passwd\n",
					ERROR + PASSWORD_REQUIRED);
				return;
			} else if ((QRscratch.QRflags & QR_PRIVATE) &&
				   ((QRscratch.QRflags & QR_PASSWORDED) == 0) &&
				   ((QRscratch.QRflags & QR_GUESSNAME) == 0) &&
				   ((ra & UA_KNOWN) == 0) &&
			           (CC->user.axlevel < AxAideU)
                                  ) {
				syslog(LOG_DEBUG, "Failed to acquire private room\n");
			} else {
				memcpy(&CC->room, &QRscratch,
					sizeof(struct ctdlroom));
				CtdlUserGoto(NULL, 1, transiently, NULL, NULL, NULL, NULL);
				return;
			}
		}
	}

	cprintf("%d room '%s' not found\n", ERROR + ROOM_NOT_FOUND, towhere);
}
コード例 #13
0
ファイル: serv_instmsg.c プロジェクト: henri14/citadel
/*
 * send instant messages
 */
void cmd_sexp(char *argbuf)
{
	int message_sent = 0;
	char x_user[USERNAME_SIZE];
	char x_msg[1024];
	char *lun;
	char *lem;
	char *x_big_msgbuf = NULL;

	if ((!(CC->logged_in)) && (!(CC->internal_pgm))) {
		cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
		return;
	}
	if (CC->fake_username[0])
		lun = CC->fake_username;
	else
		lun = CC->user.fullname;

	lem = CC->cs_inet_email;

	extract_token(x_user, argbuf, 0, '|', sizeof x_user);
	extract_token(x_msg, argbuf, 1, '|', sizeof x_msg);

	if (!x_user[0]) {
		cprintf("%d You were not previously paged.\n", ERROR + NO_SUCH_USER);
		return;
	}
	if ((!strcasecmp(x_user, "broadcast")) && (CC->user.axlevel < AxAideU)) {
		cprintf("%d Higher access required to send a broadcast.\n",
			ERROR + HIGHER_ACCESS_REQUIRED);
		return;
	}
	/* This loop handles text-transfer pages */
	if (!strcmp(x_msg, "-")) {
		message_sent = PerformXmsgHooks(lun, lem, x_user, "");
		if (message_sent == 0) {
			if (CtdlGetUser(NULL, x_user))
				cprintf("%d '%s' does not exist.\n",
						ERROR + NO_SUCH_USER, x_user);
			else
				cprintf("%d '%s' is not logged in "
						"or is not accepting pages.\n",
						ERROR + RESOURCE_NOT_OPEN, x_user);
			return;
		}
		unbuffer_output();
		cprintf("%d Transmit message (will deliver to %d users)\n",
			SEND_LISTING, message_sent);
		x_big_msgbuf = malloc(SIZ);
		memset(x_big_msgbuf, 0, SIZ);
		while (client_getln(x_msg, sizeof x_msg) >= 0 && strcmp(x_msg, "000")) {
			x_big_msgbuf = realloc(x_big_msgbuf,
			       strlen(x_big_msgbuf) + strlen(x_msg) + 4);
			if (!IsEmptyStr(x_big_msgbuf))
			   if (x_big_msgbuf[strlen(x_big_msgbuf)] != '\n')
				strcat(x_big_msgbuf, "\n");
			strcat(x_big_msgbuf, x_msg);
		}
		PerformXmsgHooks(lun, lem, x_user, x_big_msgbuf);
		free(x_big_msgbuf);

		/* This loop handles inline pages */
	} else {
		message_sent = PerformXmsgHooks(lun, lem, x_user, x_msg);

		if (message_sent > 0) {
			if (!IsEmptyStr(x_msg))
				cprintf("%d Message sent", CIT_OK);
			else
				cprintf("%d Ok to send message", CIT_OK);
			if (message_sent > 1)
				cprintf(" to %d users", message_sent);
			cprintf(".\n");
		} else {
			if (CtdlGetUser(NULL, x_user))
				cprintf("%d '%s' does not exist.\n",
						ERROR + NO_SUCH_USER, x_user);
			else
				cprintf("%d '%s' is not logged in "
						"or is not accepting pages.\n",
						ERROR + RESOURCE_NOT_OPEN, x_user);
		}


	}
}
コード例 #14
0
/*
 * Validate recipients, count delivery types and errors, and handle aliasing
 * FIXME check for dupes!!!!!
 *
 * Returns 0 if all addresses are ok, ret->num_error = -1 if no addresses 
 * were specified, or the number of addresses found invalid.
 *
 * Caller needs to free the result using free_recipients()
 */
recptypes *validate_recipients(const char *supplied_recipients, 
			       const char *RemoteIdentifier, 
			       int Flags) {
	struct CitContext *CCC = CC;
	recptypes *ret;
	char *recipients = NULL;
	char *org_recp;
	char this_recp[256];
	char this_recp_cooked[256];
	char append[SIZ];
	long len;
	int num_recps = 0;
	int i, j;
	int mailtype;
	int invalid;
	struct ctdluser tempUS;
	struct ctdlroom tempQR;
	struct ctdlroom tempQR2;
	int err = 0;
	char errmsg[SIZ];
	int in_quotes = 0;

	/* Initialize */
	ret = (recptypes *) malloc(sizeof(recptypes));
	if (ret == NULL) return(NULL);

	/* Set all strings to null and numeric values to zero */
	memset(ret, 0, sizeof(recptypes));

	if (supplied_recipients == NULL) {
		recipients = strdup("");
	}
	else {
		recipients = strdup(supplied_recipients);
	}

	/* Allocate some memory.  Yes, this allocates 500% more memory than we will
	 * actually need, but it's healthier for the heap than doing lots of tiny
	 * realloc() calls instead.
	 */
	len = strlen(recipients) + 1024;
	ret->errormsg = malloc(len);
	ret->recp_local = malloc(len);
	ret->recp_internet = malloc(len);
	ret->recp_ignet = malloc(len);
	ret->recp_room = malloc(len);
	ret->display_recp = malloc(len);
	ret->recp_orgroom = malloc(len);
	org_recp = malloc(len);

	ret->errormsg[0] = 0;
	ret->recp_local[0] = 0;
	ret->recp_internet[0] = 0;
	ret->recp_ignet[0] = 0;
	ret->recp_room[0] = 0;
	ret->recp_orgroom[0] = 0;
	ret->display_recp[0] = 0;

	ret->recptypes_magic = RECPTYPES_MAGIC;

	/* Change all valid separator characters to commas */
	for (i=0; !IsEmptyStr(&recipients[i]); ++i) {
		if ((recipients[i] == ';') || (recipients[i] == '|')) {
			recipients[i] = ',';
		}
	}

	/* Now start extracting recipients... */

	while (!IsEmptyStr(recipients)) {
		for (i=0; i<=strlen(recipients); ++i) {
			if (recipients[i] == '\"') in_quotes = 1 - in_quotes;
			if ( ( (recipients[i] == ',') && (!in_quotes) ) || (recipients[i] == 0) ) {
				safestrncpy(this_recp, recipients, i+1);
				this_recp[i] = 0;
				if (recipients[i] == ',') {
					strcpy(recipients, &recipients[i+1]);
				}
				else {
					strcpy(recipients, "");
				}
				break;
			}
		}

		striplt(this_recp);
		if (IsEmptyStr(this_recp))
			break;
		MSG_syslog(LOG_DEBUG, "Evaluating recipient #%d: %s\n", num_recps, this_recp);
		++num_recps;

		strcpy(org_recp, this_recp);
		alias(this_recp);
		alias(this_recp);
		mailtype = alias(this_recp);

		for (j = 0; !IsEmptyStr(&this_recp[j]); ++j) {
			if (this_recp[j]=='_') {
				this_recp_cooked[j] = ' ';
			}
			else {
				this_recp_cooked[j] = this_recp[j];
			}
		}
		this_recp_cooked[j] = '\0';
		invalid = 0;
		errmsg[0] = 0;
		switch(mailtype) {
		case MES_LOCAL:
			if (!strcasecmp(this_recp, "sysop")) {
				++ret->num_room;
				strcpy(this_recp, CtdlGetConfigStr("c_aideroom"));
				if (!IsEmptyStr(ret->recp_room)) {
					strcat(ret->recp_room, "|");
				}
				strcat(ret->recp_room, this_recp);
			}
			else if ( (!strncasecmp(this_recp, "room_", 5))
				  && (!CtdlGetRoom(&tempQR, &this_recp_cooked[5])) ) {

				/* Save room so we can restore it later */
				tempQR2 = CCC->room;
				CCC->room = tempQR;
					
				/* Check permissions to send mail to this room */
				err = CtdlDoIHavePermissionToPostInThisRoom(
					errmsg, 
					sizeof errmsg, 
					RemoteIdentifier,
					Flags,
					0			/* 0 = not a reply */
					);
				if (err)
				{
					++ret->num_error;
					invalid = 1;
				} 
				else {
					++ret->num_room;
					if (!IsEmptyStr(ret->recp_room)) {
						strcat(ret->recp_room, "|");
					}
					strcat(ret->recp_room, &this_recp_cooked[5]);

					if (!IsEmptyStr(ret->recp_orgroom)) {
						strcat(ret->recp_orgroom, "|");
					}
					strcat(ret->recp_orgroom, org_recp);

				}
					
				/* Restore room in case something needs it */
				CCC->room = tempQR2;

			}
			else if (CtdlGetUser(&tempUS, this_recp) == 0) {
				++ret->num_local;
				strcpy(this_recp, tempUS.fullname);
				if (!IsEmptyStr(ret->recp_local)) {
					strcat(ret->recp_local, "|");
				}
				strcat(ret->recp_local, this_recp);
			}
			else if (CtdlGetUser(&tempUS, this_recp_cooked) == 0) {
				++ret->num_local;
				strcpy(this_recp, tempUS.fullname);
				if (!IsEmptyStr(ret->recp_local)) {
					strcat(ret->recp_local, "|");
				}
				strcat(ret->recp_local, this_recp);
			}
			else {
				++ret->num_error;
				invalid = 1;
			}
			break;
		case MES_INTERNET:
			/* Yes, you're reading this correctly: if the target
			 * domain points back to the local system or an attached
			 * Citadel directory, the address is invalid.  That's
			 * because if the address were valid, we would have
			 * already translated it to a local address by now.
			 */
			if (IsDirectory(this_recp, 0)) {
				++ret->num_error;
				invalid = 1;
			}
			else {
				++ret->num_internet;
				if (!IsEmptyStr(ret->recp_internet)) {
					strcat(ret->recp_internet, "|");
				}
				strcat(ret->recp_internet, this_recp);
			}
			break;
		case MES_IGNET:
			++ret->num_ignet;
			if (!IsEmptyStr(ret->recp_ignet)) {
				strcat(ret->recp_ignet, "|");
			}
			strcat(ret->recp_ignet, this_recp);
			break;
		case MES_ERROR:
			++ret->num_error;
			invalid = 1;
			break;
		}
		if (invalid) {
			if (IsEmptyStr(errmsg)) {
				snprintf(append, sizeof append, "Invalid recipient: %s", this_recp);
			}
			else {
				snprintf(append, sizeof append, "%s", errmsg);
			}
			if ( (strlen(ret->errormsg) + strlen(append) + 3) < SIZ) {
				if (!IsEmptyStr(ret->errormsg)) {
					strcat(ret->errormsg, "; ");
				}
				strcat(ret->errormsg, append);
			}
		}
		else {
			if (IsEmptyStr(ret->display_recp)) {
				strcpy(append, this_recp);
			}
			else {
				snprintf(append, sizeof append, ", %s", this_recp);
			}
			if ( (strlen(ret->display_recp)+strlen(append)) < SIZ) {
				strcat(ret->display_recp, append);
			}
		}
	}
	free(org_recp);

	if ((ret->num_local + ret->num_internet + ret->num_ignet +
	     ret->num_room + ret->num_error) == 0) {
		ret->num_error = (-1);
		strcpy(ret->errormsg, "No recipients specified.");
	}

	MSGM_syslog(LOG_DEBUG, "validate_recipients()\n");
	MSG_syslog(LOG_DEBUG, " local: %d <%s>\n", ret->num_local, ret->recp_local);
	MSG_syslog(LOG_DEBUG, "  room: %d <%s>\n", ret->num_room, ret->recp_room);
	MSG_syslog(LOG_DEBUG, "  inet: %d <%s>\n", ret->num_internet, ret->recp_internet);
	MSG_syslog(LOG_DEBUG, " ignet: %d <%s>\n", ret->num_ignet, ret->recp_ignet);
	MSG_syslog(LOG_DEBUG, " error: %d <%s>\n", ret->num_error, ret->errormsg);

	free(recipients);
	return(ret);
}