Index: Makefile
===================================================================
--- Makefile	(revision 1448)
+++ Makefile	(working copy)
@@ -640,6 +640,7 @@
 clean:: torture-clean
 
 torture/openchange.$(SHLIBEXT):			\
+	torture/nspi_torture.po			\
 	torture/nspi_profile.po			\
 	torture/nspi_resolvenames.po		\
 	torture/mapi_restrictions.po		\
@@ -683,6 +684,7 @@
 	torture/mapi_fetchattach.c	\
 	torture/mapi_sendmail.c		\
 	torture/mapi_sendmail_html.c	\
+	torture/nspi_torture.c		\
 	torture/nspi_profile.c 		\
 	torture/nspi_resolvenames.c	\
 	torture/mapi_fetchappointment.c	\
Index: torture/openchange.c
===================================================================
--- torture/openchange.c	(revision 1448)
+++ torture/openchange.c	(working copy)
@@ -28,6 +28,7 @@
 
 NTSTATUS samba_init_module(void)
 {
+	struct torture_tcase *tcase;
 	struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "OPENCHANGE");
 
 	ndr_table_init();
@@ -37,7 +38,9 @@
 	/* OpenChange torture tests */
 	/* Address Book name resolution */
 	torture_suite_add_simple_test(suite, "NSPI-PROFILE", torture_rpc_nspi_profile);
-	torture_suite_add_simple_test(suite, "NSPI-RESOLVENAMES", torture_rpc_nspi_resolvenames);
+	tcase = torture_suite_add_tcase(suite, "NSPI-RESOLVENAMES");
+	torture_nspi_resolvenames_tcase_init(tcase);
+
 	/* MAPI mail torture tests */
 	torture_suite_add_simple_test(suite, "MAPI-FETCHMAIL", torture_rpc_mapi_fetchmail);
 	torture_suite_add_simple_test(suite, "MAPI-FETCHATTACH", torture_rpc_mapi_fetchattach);
Index: torture/nspi_resolvenames.c
===================================================================
--- torture/nspi_resolvenames.c	(revision 1448)
+++ torture/nspi_resolvenames.c	(working copy)
@@ -18,122 +18,326 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
+/*
+   Usage information:
+   In order to run following tests, one should set following params:
+     mapi:codepage = 0x000004e4		# CodePage in Nspi STAT
+     mapi:language = 0x00000409		# TemplateLocale in Nspi STAT
+     mapi:method = 0x00000409		# SortLocale in Nspi STAT
+     mapi:unicode = 1			# should we use Unicode version
+     exchange:ok_names = [list of known good names to be resolved]
+     exchange:ok_names = [list of known bad names to be resolved]
 
+   From command line this may be achived using:
+     /usr/local/samba/bin/smbtorture
+        -UAdministrator%{secret}
+        --option=exchange:ok_names=Admin,user
+        --option=exchange:err_names=bad_name
+        --load-module=modules/torture/openchange.so
+        ncacn_ip_tcp:{server_ip_or_name}[print]
+        OPENCHANGE-NSPI-RESOLVENAMES-2
+ */
+
 #include <libmapi/libmapi.h>
 #include <gen_ndr/ndr_exchange.h>
 #include <param.h>
-#include <credentials.h>
 #include <torture/mapi_torture.h>
 #include <torture.h>
 #include <torture/torture_proto.h>
 #include <samba/popt.h>
 
-bool torture_rpc_nspi_resolvenames(struct torture_context *torture)
+/**
+ * nspi_resolvenames tests context
+ */
+struct nspi_resolvenames_tcase_ctx
 {
+	uint32_t		unicode;  // "mapi:unicode" -> 1
+	const char 		**ok_names; // list of OK names to resolve
+	const char 		**err_names; // list of NOK names to resolve
+	struct nspi_context	*nspi_ctx;
+};
+
+/**
+ * nspi_resolvenames setup function
+ *
+ * TODO: Implement getting of ok_names and err_names using generator function
+ */
+static bool nspi_resolvenames_tcase_setup(struct torture_context *tctx, void **data)
+{
 	NTSTATUS                status;
-	enum MAPISTATUS		retval;
 	struct dcerpc_pipe      *p;
-	TALLOC_CTX              *mem_ctx;
-	struct mapi_session	*session = NULL;
-	bool                    ret = true;
-	struct SPropTagArray    *SPropTagArray;
-	struct SRowSet		*rowset = NULL;
-	struct SPropTagArray   	*flaglist = NULL;
-	const char		*profdb;
-	char			*profname;
-	const char		*username = lp_parm_string(torture->lp_ctx, NULL, "exchange", "resolvename");
-	const char		*password = lp_parm_string(torture->lp_ctx, NULL, "mapi", "password");
-	uint32_t		unicode = lp_parm_int(torture->lp_ctx, NULL, "mapi", "unicode", 0);
-	char *tmp;
-	char **usernames;
-	int j;
+	uint32_t 		codepage;
+	uint32_t 		template_locale;
+	uint32_t 		sort_locale;
+	const char 		*ok_names = lp_parm_string(tctx->lp_ctx, NULL, "exchange", "ok_names");
+	const char 		*err_names = lp_parm_string(tctx->lp_ctx, NULL, "exchange", "err_names");
 
-	mem_ctx = talloc_named(NULL, 0, "torture_rpc_nspi_resolvenames");
+	struct nspi_resolvenames_tcase_ctx *tcase_ctx = talloc(tctx, struct nspi_resolvenames_tcase_ctx);
+	torture_assert(tctx, tcase_ctx != NULL, "Not enough memory");
 
-	if (!username) {
-		DEBUG(0,("Specify the usernames to resolve with exchange:resolvename\n"));
-		talloc_free(mem_ctx);
+	// load parameters
+	if (!tnspi_util_get_lp_param_common(tctx, &codepage, &template_locale,
+						&sort_locale, &tcase_ctx->unicode)) {
 		return false;
 	}
 
-	status = torture_rpc_connection(torture, &p, &ndr_table_exchange_nsp);
-	if (!NT_STATUS_IS_OK(status)) {
-		talloc_free(mem_ctx);
-		return false;
+	// parse names to be resolved
+	tcase_ctx->ok_names = (const char **)str_list_make(tcase_ctx, ok_names, ",");
+	torture_assert(tctx, str_list_length(tcase_ctx->ok_names),
+			"You must supply at least one _valid_ user name to be resolved");
+	tcase_ctx->err_names = (const char **)str_list_make(tcase_ctx, err_names, ",");
+	torture_assert(tctx, str_list_length(tcase_ctx->err_names),
+			"You must supply at least one _invalid_ name to be resolved");
+
+	status = torture_rpc_connection(tctx, &p, &ndr_table_exchange_nsp);
+	torture_assert_ntstatus_ok(tctx, status, "torture_rpc_connection failed");
+
+	tcase_ctx->nspi_ctx = nspi_bind(tcase_ctx, p, cmdline_credentials,
+					codepage,
+					template_locale,
+					sort_locale);
+	torture_assert(tctx, tcase_ctx->nspi_ctx, "nspi_bind filed");
+
+	// return context
+	*data = tcase_ctx;
+
+	return true;
+}
+
+/**
+ * nspi_resolvenames teardown function
+ */
+static bool nspi_resolvenames_tcase_teardown(struct torture_context *tctx, void *data)
+{
+	struct nspi_resolvenames_tcase_ctx *tcase_ctx = (struct nspi_resolvenames_tcase_ctx *)data;
+
+	if (tcase_ctx->nspi_ctx) {
+		nspi_unbind(tcase_ctx->nspi_ctx);
 	}
 
-	/* init mapi */
-	profdb = lp_parm_string(torture->lp_ctx, NULL, "mapi", "profile_store");
-	if (!profdb) {
-		profdb = talloc_asprintf(mem_ctx, DEFAULT_PROFDB_PATH, getenv("HOME"));
-		if (!profdb) {
-			DEBUG(0, ("Specify a valie MAPI profile store\n"));
-			return false;
-		}
+	talloc_free(tcase_ctx);
+	return true;
+}
+
+static struct SPropTagArray* nspi_rn_get_wanted_proptags(TALLOC_CTX *mem_ctx)
+{
+	return set_SPropTagArray(mem_ctx, 0xd,
+				  PR_ENTRYID,
+				  PR_DISPLAY_NAME,
+				  PR_ADDRTYPE,
+				  PR_GIVEN_NAME,
+				  PR_SMTP_ADDRESS,
+				  PR_OBJECT_TYPE,
+				  PR_DISPLAY_TYPE,
+				  PR_EMAIL_ADDRESS,
+				  PR_SEND_INTERNET_ENCODING,
+				  PR_SEND_RICH_INFO,
+				  PR_SEARCH_KEY,
+				  PR_TRANSMITTABLE_DISPLAY_NAME,
+				  PR_7BIT_DISPLAY_NAME);
+}
+
+/**
+ * Test for resolvable names, i.e. names that must be resolved by NSPI server
+ */
+static bool test_nspi_resolvenames_ok_names(struct torture_context *tctx, struct nspi_resolvenames_tcase_ctx *tcase_ctx)
+{
+	uint32_t		i;
+	uint32_t 		rows_count;
+	enum MAPISTATUS		retval;
+	TALLOC_CTX              *mem_ctx;
+	struct SPropTagArray    *query_proptags;
+	struct SRowSet		*pRows = NULL, **ppRows = &pRows;
+	struct SPropTagArray   	*pMIds = NULL, **ppMIds = &pMIds;
+	uint32_t		names_count = str_list_length(tcase_ctx->ok_names);
+
+	torture_comment(tctx, "Starting: %s\n", __FUNCTION__);
+
+	mem_ctx = talloc_named(tcase_ctx, 0, __FUNCTION__);
+
+	query_proptags = nspi_rn_get_wanted_proptags(mem_ctx);
+
+	if (tcase_ctx->unicode) {
+		retval = nspi_ResolveNamesW(tcase_ctx->nspi_ctx, tcase_ctx->ok_names, query_proptags, &ppRows, &ppMIds);
 	}
-	retval = MAPIInitialize(profdb);
-	mapi_errstr("MAPIInitialize", GetLastError());
-	if (retval != MAPI_E_SUCCESS) return false;
+	else {
+		retval = nspi_ResolveNames(tcase_ctx->nspi_ctx, tcase_ctx->ok_names, query_proptags, &ppRows, &ppMIds);
+	}
+	torture_assert(tctx, !MAPI_STATUS_IS_ERR(retval), "nspi_ResolveNames failed");
+	torture_comment(tctx, "nspi_Resolvenames(): %s (0x%x)\n", mapi_get_errstr(retval), retval);
 
-	/* profile name */
-	profname = talloc_strdup(mem_ctx, lp_parm_string(torture->lp_ctx, NULL, "mapi", "profile"));
-	if (!profname) {
-		retval = GetDefaultProfile(&profname);
-		if (retval != MAPI_E_SUCCESS) {
-			DEBUG(0, ("Please specify a valid profile name\n"));
-			return false;
+	// returned Rows and MIds
+	torture_assert(tctx, pRows != NULL, "No rows returned!");
+	torture_assert(tctx, pMIds != NULL, "No MIds returned!");
+	torture_assert(tctx, names_count == pMIds->cValues, "MIds count must be equal to supplied names for resolving");
+	torture_assert(tctx, names_count == pRows->cRows, "Returned rows must be equal to supplied names for resolving");
+
+	// now, check returned rows count
+	{
+		rows_count = 0;
+		for (i = 0; i < pMIds->cValues; i++) {
+			if (MAPI_RESOLVED == pMIds->aulPropTag[i])
+				rows_count++;
 		}
+		torture_comment(tctx, "Expected rows count: %d\n", rows_count);
+		torture_assert(tctx, pRows->cRows == rows_count, "Expected rows count differ from received rows count");
 	}
-	
-	retval = MapiLogonProvider(&session, profname, password, PROVIDER_ID_NSPI);
-	talloc_free(profname);
-	mapi_errstr("MapiLogonProvider", GetLastError());
-	if (retval != MAPI_E_SUCCESS) return false;
 
-	SPropTagArray = set_SPropTagArray(mem_ctx, 0xd,
-					  PR_ENTRYID,
-					  PR_DISPLAY_NAME,
-					  PR_ADDRTYPE,
-					  PR_GIVEN_NAME,
-					  PR_SMTP_ADDRESS,
-					  PR_OBJECT_TYPE,
-					  PR_DISPLAY_TYPE,
-					  PR_EMAIL_ADDRESS,
-					  PR_SEND_INTERNET_ENCODING,
-					  PR_SEND_RICH_INFO,
-					  PR_SEARCH_KEY,
-					  PR_TRANSMITTABLE_DISPLAY_NAME,
-					  PR_7BIT_DISPLAY_NAME);
+	if (DEBUGLVL(1))
+		mapidump_Recipients(tcase_ctx->ok_names, pRows, pMIds);
 
-	if ((tmp = strtok((char *)username, ",")) == NULL){
-		DEBUG(2, ("Invalid usernames string format\n"));
-		exit (1);
+	if (pRows) {
+		retval = MAPIFreeBuffer(pRows);
+		if (!MAPI_STATUS_IS_OK(retval))
+			torture_comment(tctx, "MAPIFreeBuffer(pRows) - %s\n", mapi_get_errstr(retval));
 	}
 
-	usernames = talloc_array(mem_ctx, char *, 2);
-	usernames[0] = strdup(tmp);
+	if (pMIds) {
+		retval = MAPIFreeBuffer(pMIds);
+		if (!MAPI_STATUS_IS_OK(retval))
+			torture_comment(tctx, "MAPIFreeBuffer(pMIds) - %s\n", mapi_get_errstr(retval));
+	}
 
-	for (j = 1; (tmp = strtok(NULL, ",")) != NULL; j++) {
-		     usernames = talloc_realloc(mem_ctx, usernames, char *, j+2);
-		     usernames[j] = strdup(tmp);
+	talloc_free(mem_ctx);
+	return true;
+}
+
+/**
+ * Test for _not_ resolvable names, i.e. names that doesn't exists
+ */
+static bool test_nspi_resolvenames_err_names(struct torture_context *tctx, struct nspi_resolvenames_tcase_ctx *tcase_ctx)
+{
+	uint32_t		i;
+	enum MAPISTATUS		retval;
+	TALLOC_CTX              *mem_ctx;
+	struct SPropTagArray    *query_proptags;
+	struct SRowSet		*pRows = NULL, **ppRows = &pRows;
+	struct SPropTagArray   	*pMIds = NULL, **ppMIds = &pMIds;
+	uint32_t		names_count = str_list_length(tcase_ctx->err_names);
+
+	torture_comment(tctx, "Starting: %s\n", __FUNCTION__);
+
+	mem_ctx = talloc_named(tcase_ctx, 0, __FUNCTION__);
+
+	query_proptags = nspi_rn_get_wanted_proptags(mem_ctx);
+
+	if (tcase_ctx->unicode) {
+		retval = nspi_ResolveNamesW(tcase_ctx->nspi_ctx, tcase_ctx->err_names, query_proptags, &ppRows, &ppMIds);
 	}
-	usernames[j] = 0;
+	else {
+		retval = nspi_ResolveNames(tcase_ctx->nspi_ctx, tcase_ctx->err_names, query_proptags, &ppRows, &ppMIds);
+	}
+	torture_assert(tctx, !MAPI_STATUS_IS_ERR(retval), "nspi_ResolveNames failed");
+	torture_comment(tctx, "nspi_Resolvenames(): %s (0x%x)\n", mapi_get_errstr(retval), retval);
 
-	retval = ResolveNames(session, (const char **)usernames, SPropTagArray, &rowset, &flaglist, unicode?MAPI_UNICODE:0);
-	mapi_errstr("ResolveNames", GetLastError());
-	if (retval != MAPI_E_SUCCESS) return false;
+	// returned Rows and MIds
+	torture_assert(tctx, pRows == NULL, "No rows should be returned!");
+	torture_assert(tctx, pMIds != NULL, "A list of MIds is expected!");
+	torture_assert(tctx, names_count == pMIds->cValues, "MIds count must be equal to supplied names for resolving");
 
-	mapidump_Recipients((const char **)usernames, rowset, flaglist);
+	// check returned MIds
+	for (i = 0; i < names_count; i++) {
+		enum MAPITAGS proptag = pMIds->aulPropTag[i];
+		torture_assert(tctx, (proptag == MAPI_UNRESOLVED), "Expected _unresolved_ value for MId");
+	}
 
-	retval = MAPIFreeBuffer(rowset);
-	mapi_errstr("MAPIFreeBuffer: rowset", GetLastError());
-	
-	retval = MAPIFreeBuffer(flaglist);
-	mapi_errstr("MAPIFreeBuffer: flaglist", GetLastError());
-	
-	MAPIUninitialize();
+	talloc_free(mem_ctx);
+	return true;
+}
 
+/**
+ * Test with a mixed list of names - both resolvable not valid names
+ */
+static bool test_nspi_resolvenames_mixed_names(struct torture_context *tctx, struct nspi_resolvenames_tcase_ctx *tcase_ctx)
+{
+	uint32_t		i;
+	enum MAPISTATUS		retval;
+	TALLOC_CTX              *mem_ctx;
+	struct SPropTagArray    *query_proptags;
+	struct SRowSet		*pRows = NULL, **ppRows = &pRows;
+	struct SPropTagArray   	*pMIds = NULL, **ppMIds = &pMIds;
+	const char		**names_list;
+	uint32_t		names_count;
+
+	torture_comment(tctx, "Starting: %s\n", __FUNCTION__);
+
+	mem_ctx = talloc_named(tcase_ctx, 0, __FUNCTION__);
+
+	// make mixed list
+	names_list = str_list_copy_const(mem_ctx, tcase_ctx->ok_names);
+	torture_assert(tctx, names_list, "Not enough memory");
+	names_list = str_list_append_const(names_list, tcase_ctx->err_names);
+	torture_assert(tctx, names_list, "Not enough memory");
+
+	// check names count
+	names_count = str_list_length(names_list);
+	torture_assert(tctx, names_count > 0, "Specify at least one user name to be resolved");
+
+	query_proptags = nspi_rn_get_wanted_proptags(mem_ctx);
+
+	if (tcase_ctx->unicode) {
+		retval = nspi_ResolveNamesW(tcase_ctx->nspi_ctx, names_list, query_proptags, &ppRows, &ppMIds);
+	}
+	else {
+		retval = nspi_ResolveNames(tcase_ctx->nspi_ctx, names_list, query_proptags, &ppRows, &ppMIds);
+	}
+	torture_assert(tctx, !MAPI_STATUS_IS_ERR(retval), "nspi_ResolveNames failed");
+	torture_comment(tctx, "nspi_Resolvenames(): %s (0x%x)\n", mapi_get_errstr(retval), retval);
+
+	// returned Rows and MIds
+	torture_assert(tctx, pRows != NULL, "No rows returned!");
+	torture_assert(tctx, pMIds != NULL, "No MIds returned!");
+	torture_assert(tctx, names_count == pMIds->cValues, "MIds count must be equal to supplied names for resolving");
+	torture_assert(tctx, names_count >= pRows->cRows, "Returned rows must be equal or less to supplied names for resolving");
+
+	// now, check returned rows count
+	{
+		uint32_t rows_count = 0;
+		for (i = 0; i < pMIds->cValues; i++) {
+			if (MAPI_RESOLVED == pMIds->aulPropTag[i])
+				rows_count++;
+		}
+		torture_comment(tctx, "Expected rows count: %d\n", rows_count);
+		torture_assert(tctx, pRows->cRows == rows_count, "Expected rows count differ from received rows count");
+	}
+
+	if (DEBUGLVL(1))
+		mapidump_Recipients(tcase_ctx->ok_names, pRows, pMIds);
+
+	if (pRows) {
+		retval = MAPIFreeBuffer(pRows);
+		if (!MAPI_STATUS_IS_OK(retval))
+			torture_comment(tctx, "MAPIFreeBuffer(pRows) - %s\n", mapi_get_errstr(retval));
+	}
+
+	if (pMIds) {
+		retval = MAPIFreeBuffer(pMIds);
+		if (!MAPI_STATUS_IS_OK(retval))
+			torture_comment(tctx, "MAPIFreeBuffer(pMIds) - %s\n", mapi_get_errstr(retval));
+	}
+
 	talloc_free(mem_ctx);
+	return true;
+}
 
-	return ret;
+/**
+ * Public function to be used to fill up TestCase structure for NSPI-RESOLVENAMES tests
+ */
+_PUBLIC_ bool torture_nspi_resolvenames_tcase_init(struct torture_tcase *tcase)
+{
+	typedef bool (*run_func) (struct torture_context *test, void *tcase_data);
+	struct torture_test *test;
+
+	torture_tcase_set_fixture(tcase, nspi_resolvenames_tcase_setup, nspi_resolvenames_tcase_teardown);
+
+	test = torture_tcase_add_simple_test(tcase, "NSPI-RESOLVE-OK-NAMES", (run_func)test_nspi_resolvenames_ok_names);
+	test->description = talloc_strdup(tcase, "Execute NspiResolveNames with a list of names that should be resolved successfully");
+
+	test = torture_tcase_add_simple_test(tcase, "NSPI-RESOLVE-ERR-NAMES", (run_func)test_nspi_resolvenames_err_names);
+	test->description = talloc_strdup(tcase, "Execute NspiResolveNames with a list of names that should not be resolved");
+
+	test = torture_tcase_add_simple_test(tcase, "NSPI-RESOLVE-MIXED-NAMES", (run_func)test_nspi_resolvenames_mixed_names);
+	test->description = talloc_strdup(tcase, "Execute NspiResolveNames with a mixed list of names - resolvable and not valid");
+
+	return true;
 }
Index: torture/nspi_torture.c
===================================================================
--- torture/nspi_torture.c	(revision 0)
+++ torture/nspi_torture.c	(revision 0)
@@ -0,0 +1,65 @@
+/*
+   OpenChange MAPI torture suite implementation.
+
+   NSPI torture tests utility functions.
+
+   Copyright (C) Kamen Mazdrashki 2009.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libmapi/libmapi.h>
+#include <gen_ndr/ndr_exchange.h>
+#include <param.h>
+#include <credentials.h>
+#include <torture/mapi_torture.h>
+#include <torture.h>
+#include <torture/torture_proto.h>
+#include <samba/popt.h>
+
+/**
+ * Get common params for NSPI torture tests.
+ * Those are:
+ * 	codepage
+ * 	template_locate
+ * 	sort_locale
+ */
+_PUBLIC_ bool tnspi_util_get_lp_param_common(struct torture_context *tctx,
+						uint32_t *codepage,
+						uint32_t *language,
+						uint32_t *method,
+						uint32_t *unicode)
+{
+	if (codepage) {
+		*codepage = lp_parm_int(tctx->lp_ctx, NULL, "mapi", "codepage", 0x000004e4);
+		torture_assert(tctx, *codepage != 0, "Please enter valid CodePage value");
+	}
+
+	if (language) {
+		*language = lp_parm_int(tctx->lp_ctx, NULL, "mapi", "language", 0x00000409);
+		torture_assert(tctx, *language != 0, "Please supply valid TemplateLocale code");
+	}
+
+	if (method) {
+		*method = lp_parm_int(tctx->lp_ctx, NULL, "mapi", "method", 0x00000409);
+		torture_assert(tctx, *method != 0, "Please supply valid SortLocale value");
+	}
+
+	if (unicode) {
+		*unicode = lp_parm_int(tctx->lp_ctx, NULL, "mapi", "unicode", 1);
+	}
+
+	return true;
+}
+
