static mcon_collection *mongo_filter_candidates_by_replicaset_name(mongo_con_manager *manager, mcon_collection *candidates, mongo_servers *servers)
{
	int              i;
	mcon_collection *filtered;
	char            *candidate_hash;
	char            *candidate_replsetname;

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting to servers with same replicaset name");
	filtered = mcon_init_collection(sizeof(mongo_connection*));

	for (i = 0; i < candidates->count; i++) {
		candidate_hash = ((mongo_connection *) candidates->data[i])->hash;
		mongo_server_split_hash(candidate_hash, NULL, NULL, (char**) &candidate_replsetname, NULL, NULL, NULL, NULL);

		/* Filter out all servers that don't have the replicaset name the same
		 * as what we have in the server definition struct. But only when the
		 * repl_set_name in the server definition struct is actually *set*. If
		 * not, we allow all connections. This make sure we can sort of handle
		 * [ replicaset => true ], although it would not support one PHP worker
		 * process connecting to multiple replicasets correctly. */
		if (candidate_replsetname) {
			if (!servers->repl_set_name || strcmp(candidate_replsetname, servers->repl_set_name) == 0) {
				mongo_print_connection_info(manager, (mongo_connection *) candidates->data[i], MLOG_FINE);
				mcon_collection_add(filtered, (mongo_connection *) candidates->data[i]);
			}
			free(candidate_replsetname);
		}
	}

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting to servers with same replicaset name: done");

	return filtered;
}
/* Collecting the correct servers */
static mcon_collection *filter_connections(mongo_con_manager *manager, int types, mongo_read_preference *rp)
{
	mcon_collection *col;
	mongo_con_manager_item *ptr = manager->connections;
	int current_pid, connection_pid;

	current_pid = getpid();
	col = mcon_init_collection(sizeof(mongo_connection*));

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: adding connections:");
	while (ptr) {
		connection_pid = mongo_server_hash_to_pid(ptr->connection->hash);

		if (connection_pid != current_pid) {
			mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: skipping %s as it doesn't match the current pid (%d)", ptr->connection->hash, current_pid);
		} else if (ptr->connection->connection_type & types) {
			mongo_print_connection_info(manager, ptr->connection, MLOG_FINE);
			mcon_collection_add(col, ptr->connection);
		}
		ptr = ptr->next;
	}
	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: done");

	return col;
}
static mcon_collection* mongo_filter_candidates_by_tagset(mongo_con_manager *manager, mcon_collection *candidates, mongo_read_preference_tagset *tagset)
{
	int              i;
	mcon_collection *tmp;

	tmp = mcon_init_collection(sizeof(mongo_connection*));
	for (i = 0; i < candidates->count; i++) {
		if (candidate_matches_tags(manager, (mongo_connection *) candidates->data[i], tagset)) {
			mcon_collection_add(tmp, candidates->data[i]);
		}
	}
	return tmp;
}
static mcon_collection *mongo_filter_candidates_by_credentials(mongo_con_manager *manager, mcon_collection *candidates, mongo_servers *servers)
{
	int              i;
	char            *db, *username, *auth_hash, *hashed = NULL;
	mcon_collection *filtered;

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by credentials");
	filtered = mcon_init_collection(sizeof(mongo_connection*));

	for (i = 0; i < candidates->count; i++) {
		db = username = auth_hash = hashed = NULL;
		mongo_server_split_hash(((mongo_connection *) candidates->data[i])->hash, NULL, NULL, NULL, &db, &username, &auth_hash, NULL);
		if (servers->server[0]->username && servers->server[0]->password && servers->server[0]->db) {
			if (strcmp(db, servers->server[0]->db) != 0) {
				mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "- skipping '%s', database didn't match ('%s' vs '%s')", ((mongo_connection *) candidates->data[i])->hash, db, servers->server[0]->db);
				goto skip;
			}
			if (strcmp(username, servers->server[0]->username) != 0) {
				mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "- skipping '%s', username didn't match ('%s' vs '%s')", ((mongo_connection *) candidates->data[i])->hash, username, servers->server[0]->username);
				goto skip;
			}
			hashed = mongo_server_create_hashed_password(username, servers->server[0]->password);
			if (strcmp(auth_hash, hashed) != 0) {
				mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "- skipping '%s', authentication hash didn't match ('%s' vs '%s')", ((mongo_connection *) candidates->data[i])->hash, auth_hash, hashed);
				goto skip;
			}
		}

		mcon_collection_add(filtered, (mongo_connection *) candidates->data[i]);
		mongo_print_connection_info(manager, (mongo_connection *) candidates->data[i], MLOG_FINE);
skip:
		if (db) {
			free(db);
		}
		if (username) {
			free(username);
		}
		if (auth_hash) {
			free(auth_hash);
		}
		if (hashed) {
			free(hashed);
		}
	}
	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by credentials: done");

	return filtered;
}
static mcon_collection* mongo_filter_candidates_by_tagset(mongo_con_manager *manager, mcon_collection *candidates, mongo_read_preference_tagset *tagset, int rp_type)
{
	int              i;
	mcon_collection *tmp;

	tmp = mcon_init_collection(sizeof(mongo_connection*));
	for (i = 0; i < candidates->count; i++) {
		if (rp_type == MONGO_RP_PRIMARY_PREFERRED && (((mongo_connection *) candidates->data[i])->connection_type == MONGO_NODE_PRIMARY)) {
			mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "candidate_matches_tags: added primary regardless of tags: %s", ((mongo_connection *) candidates->data[i])->hash);
			mcon_collection_add(tmp, candidates->data[i]);
		} else if (candidate_matches_tags(manager, (mongo_connection *) candidates->data[i], tagset)) {
			mcon_collection_add(tmp, candidates->data[i]);
		}
	}
	return tmp;
}
/* Collecting the correct servers */
static mcon_collection *filter_connections(mongo_con_manager *manager, int types, mongo_read_preference *rp)
{
	mcon_collection *col;
	mongo_con_manager_item *ptr = manager->connections;
	col = mcon_init_collection(sizeof(mongo_connection*));

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: adding connections:");
	while (ptr) {
		if (ptr->connection->connection_type & types) {
			mongo_print_connection_info(manager, ptr->connection, MLOG_FINE);
			mcon_collection_add(col, ptr->connection);
		}
		ptr = ptr->next;
	}
	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: done");

	return col;
}
mcon_collection *mongo_select_nearest_servers(mongo_con_manager *manager, mcon_collection *col, mongo_read_preference *rp)
{
	mcon_collection *filtered;
	int              i, nearest_ping;

	filtered = mcon_init_collection(sizeof(mongo_connection*));

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "selecting near servers");

	switch (rp->type) {
		case MONGO_RP_PRIMARY:
		case MONGO_RP_PRIMARY_PREFERRED:
		case MONGO_RP_SECONDARY:
		case MONGO_RP_SECONDARY_PREFERRED:
		case MONGO_RP_NEAREST:
			/* The nearest ping time is in the first element */
			nearest_ping = ((mongo_connection*)col->data[0])->ping_ms;
			mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "selecting near servers: nearest is %dms", nearest_ping);

			/* FIXME: Change to iterator later */
			for (i = 0; i < col->count; i++) {
				if (((mongo_connection*)col->data[i])->ping_ms <= nearest_ping + MONGO_RP_CUTOFF) {
					mcon_collection_add(filtered, col->data[i]);
				}
			}
			break;

		default:
			return NULL;
	}

	/* Clean up the old collection that we no longer need */
	mcon_collection_free(col);

	mcon_collection_iterate(manager, filtered, mongo_print_connection_iterate_wrapper);
	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "selecting near server: done");

	return filtered;
}
static mcon_collection *mongo_filter_candidates_by_seed(mongo_con_manager *manager, mcon_collection *candidates, mongo_servers *servers)
{
	int              i, j;
	mcon_collection *filtered;
	char            *server_hash;

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by seeded/discovered servers");
	filtered = mcon_init_collection(sizeof(mongo_connection*));

	for (j = 0; j < servers->count; j++) {
		server_hash = mongo_server_create_hash(servers->server[j]);
		for (i = 0; i < candidates->count; i++) {
			if (strcmp(((mongo_connection *) candidates->data[i])->hash, server_hash) == 0) {
				mongo_print_connection_info(manager, (mongo_connection *) candidates->data[i], MLOG_FINE);
				mcon_collection_add(filtered, (mongo_connection *) candidates->data[i]);
			}
		}
		free(server_hash);
	}

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by seeded/discovered servers: done");

	return filtered;
}