summaryrefslogtreecommitdiff
path: root/source4/torture
diff options
context:
space:
mode:
Diffstat (limited to 'source4/torture')
-rw-r--r--source4/torture/rpc/spoolss.c455
1 files changed, 328 insertions, 127 deletions
diff --git a/source4/torture/rpc/spoolss.c b/source4/torture/rpc/spoolss.c
index a4a5d08e14..c109a7a4eb 100644
--- a/source4/torture/rpc/spoolss.c
+++ b/source4/torture/rpc/spoolss.c
@@ -38,7 +38,10 @@
#define TORTURE_WELLKNOWN_PRINTER_EX "torture_wkn_printer_ex"
#define TORTURE_PRINTER_EX "torture_printer_ex"
-#define TOP_LEVEL_PRINTER_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers"
+#define TOP_LEVEL_PRINT_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print"
+#define TOP_LEVEL_PRINT_PRINTERS_KEY TOP_LEVEL_PRINT_KEY "\\Printers"
+#define TOP_LEVEL_CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\Print"
+#define TOP_LEVEL_CONTROL_FORMS_KEY TOP_LEVEL_CONTROL_KEY "\\Forms"
struct test_spoolss_context {
/* print server handle */
@@ -2211,11 +2214,12 @@ static bool test_ClosePrinter(struct torture_context *tctx,
return true;
}
-static bool test_GetForm(struct torture_context *tctx,
- struct dcerpc_binding_handle *b,
- struct policy_handle *handle,
- const char *form_name,
- uint32_t level)
+static bool test_GetForm_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level,
+ union spoolss_FormInfo *info_p)
{
NTSTATUS status;
struct spoolss_GetForm r;
@@ -2228,7 +2232,7 @@ static bool test_GetForm(struct torture_context *tctx,
r.in.offered = 0;
r.out.needed = &needed;
- torture_comment(tctx, "Testing GetForm level %d\n", r.in.level);
+ torture_comment(tctx, "Testing GetForm(%s) level %d\n", form_name, r.in.level);
status = dcerpc_spoolss_GetForm_r(b, tctx, &r);
torture_assert_ntstatus_ok(tctx, status, "GetForm failed");
@@ -2250,70 +2254,136 @@ static bool test_GetForm(struct torture_context *tctx,
CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+ if (info_p) {
+ *info_p = *r.out.info;
+ }
+
return true;
}
+static bool test_GetForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level)
+{
+ return test_GetForm_args(tctx, b, handle, form_name, level, NULL);
+}
+
static bool test_EnumForms(struct torture_context *tctx,
struct dcerpc_binding_handle *b,
- struct policy_handle *handle, bool print_server)
+ struct policy_handle *handle,
+ bool print_server,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_FormInfo **info_p)
{
- NTSTATUS status;
struct spoolss_EnumForms r;
- bool ret = true;
uint32_t needed;
uint32_t count;
- uint32_t levels[] = { 1, 2 };
- int i;
+ union spoolss_FormInfo *info;
- for (i=0; i<ARRAY_SIZE(levels); i++) {
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
- union spoolss_FormInfo *info;
+ torture_comment(tctx, "Testing EnumForms level %d\n", r.in.level);
- r.in.handle = handle;
- r.in.level = levels[i];
- r.in.buffer = NULL;
- r.in.offered = 0;
- r.out.needed = &needed;
- r.out.count = &count;
- r.out.info = &info;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+ "EnumForms failed");
- torture_comment(tctx, "Testing EnumForms level %d\n", levels[i]);
+ if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_UNKNOWN_LEVEL))) {
+ torture_skip(tctx, "EnumForms level 2 not supported");
+ }
- status = dcerpc_spoolss_EnumForms_r(b, tctx, &r);
- torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+ if (print_server && W_ERROR_EQUAL(r.out.result, WERR_BADFID)) {
+ torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+ }
- if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_UNKNOWN_LEVEL))) {
- break;
- }
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
- if (print_server && W_ERROR_EQUAL(r.out.result, WERR_BADFID))
- torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+ "EnumForms failed");
- if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
- int j;
- DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
- data_blob_clear(&blob);
- r.in.buffer = &blob;
- r.in.offered = needed;
+ torture_assert(tctx, info, "No forms returned");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+
+ if (info_p) {
+ *info_p = info;
+ }
+ if (count_p) {
+ *count_p = count;
+ }
+
+ return true;
+}
+
+static bool test_EnumForms_all(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server)
+{
+ uint32_t levels[] = { 1, 2 };
+ int i, j;
- status = dcerpc_spoolss_EnumForms_r(b, tctx, &r);
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
- torture_assert(tctx, info, "No forms returned");
+ uint32_t count = 0;
+ union spoolss_FormInfo *info = NULL;
- for (j = 0; j < count; j++) {
- if (!print_server)
- ret &= test_GetForm(tctx, b, handle, info[j].info1.form_name, levels[i]);
+ torture_assert(tctx,
+ test_EnumForms(tctx, b, handle, print_server, levels[i], &count, &info),
+ "failed to enum forms");
+
+ for (j = 0; j < count; j++) {
+ if (!print_server) {
+ torture_assert(tctx,
+ test_GetForm(tctx, b, handle, info[j].info1.form_name, levels[i]),
+ "failed to get form");
}
}
+ }
- torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+ return true;
+}
- torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
+static bool test_EnumForms_find_one(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *form_name)
+{
+ union spoolss_FormInfo *info;
+ uint32_t count;
+ bool found = false;
+ int i;
+
+ torture_assert(tctx,
+ test_EnumForms(tctx, b, handle, print_server, 1, &count, &info),
+ "failed to enumerate forms");
- CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, lp_iconv_convenience(tctx->lp_ctx), needed, 4);
+ for (i=0; i<count; i++) {
+ if (strequal(form_name, info[i].info1.form_name)) {
+ found = true;
+ break;
+ }
}
- return true;
+ return found;
}
static bool test_DeleteForm(struct torture_context *tctx,
@@ -2321,124 +2391,199 @@ static bool test_DeleteForm(struct torture_context *tctx,
struct policy_handle *handle,
const char *form_name)
{
- NTSTATUS status;
struct spoolss_DeleteForm r;
r.in.handle = handle;
r.in.form_name = form_name;
- status = dcerpc_spoolss_DeleteForm_r(b, tctx, &r);
-
- torture_assert_ntstatus_ok(tctx, status, "DeleteForm failed");
+ torture_comment(tctx, "Testing DeleteForm(%s)\n", form_name);
- torture_assert_werr_ok(tctx, r.out.result, "DeleteForm failed");
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteForm_r(b, tctx, &r),
+ "DeleteForm failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DeleteForm failed");
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteForm_r(b, tctx, &r),
+ "2nd DeleteForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_FORM_NAME,
+ "2nd DeleteForm failed");
return true;
}
static bool test_AddForm(struct torture_context *tctx,
struct dcerpc_binding_handle *b,
- struct policy_handle *handle, bool print_server)
+ struct policy_handle *handle,
+ uint32_t level,
+ union spoolss_AddFormInfo *info)
{
struct spoolss_AddForm r;
- struct spoolss_AddFormInfo1 addform;
- const char *form_name = "testform3";
- NTSTATUS status;
- bool ret = true;
+
+ if (level != 1) {
+ torture_skip(tctx, "only level 1 supported");
+ }
r.in.handle = handle;
- r.in.level = 1;
- r.in.info.info1 = &addform;
- addform.flags = SPOOLSS_FORM_USER;
- addform.form_name = form_name;
- addform.size.width = 50;
- addform.size.height = 25;
- addform.area.left = 5;
- addform.area.top = 10;
- addform.area.right = 45;
- addform.area.bottom = 15;
+ r.in.level = level;
+ r.in.info = *info;
- status = dcerpc_spoolss_AddForm_r(b, tctx, &r);
+ torture_comment(tctx, "Testing AddForm(%s) level %d\n",
+ r.in.info.info1->form_name, r.in.level);
- torture_assert_ntstatus_ok(tctx, status, "AddForm failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
+ "AddForm failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_FILE_EXISTS)) {
+ test_DeleteForm(tctx, b, handle, r.in.info.info1->form_name);
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
+ "AddForm failed");
+ }
- torture_assert_werr_ok(tctx, r.out.result, "AddForm failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AddForm failed");
- if (!print_server) ret &= test_GetForm(tctx, b, handle, form_name, 1);
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
+ "2nd AddForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_FILE_EXISTS,
+ "2nd AddForm gave unexpected result");
- {
- struct spoolss_SetForm sf;
- struct spoolss_AddFormInfo1 setform;
+ return true;
+}
- sf.in.handle = handle;
- sf.in.form_name = form_name;
- sf.in.level = 1;
- sf.in.info.info1= &setform;
- setform.flags = addform.flags;
- setform.form_name = addform.form_name;
- setform.size = addform.size;
- setform.area = addform.area;
+static bool test_SetForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level,
+ union spoolss_AddFormInfo *info)
+{
+ struct spoolss_SetForm r;
- setform.size.width = 1234;
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+ r.in.level = level;
+ r.in.info = *info;
- status = dcerpc_spoolss_SetForm_r(b, tctx, &sf);
+ torture_comment(tctx, "Testing SetForm(%s) level %d\n",
+ form_name, r.in.level);
- torture_assert_ntstatus_ok(tctx, status, "SetForm failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetForm_r(b, tctx, &r),
+ "SetForm failed");
- torture_assert_werr_ok(tctx, sf.out.result, "SetForm failed");
- }
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SetForm failed");
- if (!print_server) ret &= test_GetForm(tctx, b, handle, form_name, 1);
+ return true;
+}
- {
- struct spoolss_EnumForms e;
- union spoolss_FormInfo *info;
- uint32_t needed;
- uint32_t count;
- bool found = false;
+static bool test_GetForm_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *form_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data);
- e.in.handle = handle;
- e.in.level = 1;
- e.in.buffer = NULL;
- e.in.offered = 0;
- e.out.needed = &needed;
- e.out.count = &count;
- e.out.info = &info;
+static bool test_Forms(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ union spoolss_FormInfo info;
+ const char *form_name = "testform3";
- torture_comment(tctx, "Testing EnumForms level 1\n");
+ union spoolss_AddFormInfo add_info;
+ struct spoolss_AddFormInfo1 info1;
- status = dcerpc_spoolss_EnumForms_r(b, tctx, &e);
- torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
- if (print_server && W_ERROR_EQUAL(e.out.result, WERR_BADFID))
- torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+ info1.flags = SPOOLSS_FORM_USER;
+ info1.form_name = form_name;
+ info1.size.width = 50;
+ info1.size.height = 25;
+ info1.area.left = 5;
+ info1.area.top = 10;
+ info1.area.right = 45;
+ info1.area.bottom = 15;
- if (W_ERROR_EQUAL(e.out.result, WERR_INSUFFICIENT_BUFFER)) {
- int j;
- DATA_BLOB blob = data_blob_talloc(tctx, NULL, needed);
- data_blob_clear(&blob);
- e.in.buffer = &blob;
- e.in.offered = needed;
+ add_info.info1 = &info1;
- status = dcerpc_spoolss_EnumForms_r(b, tctx, &e);
+ torture_assert(tctx,
+ test_AddForm(tctx, b, handle, 1, &add_info),
+ "failed to add form");
- torture_assert(tctx, info, "No forms returned");
+ if (winreg_handle && hive_handle) {
- for (j = 0; j < count; j++) {
- if (strequal(form_name, info[j].info1.form_name)) {
- found = true;
- break;
- }
- }
+ torture_assert(tctx,
+ test_GetForm_winreg(tctx, winreg_handle, hive_handle, TOP_LEVEL_CONTROL_FORMS_KEY, form_name, &w_type, &w_size, &w_length, &w_data),
+ "failed to get form via winreg");
+
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");
+ torture_assert_int_equal(tctx, w_size, 0x20, "unexpected size");
+ torture_assert_int_equal(tctx, w_length, 0x20, "unexpected length");
+ torture_assert_mem_equal(tctx, &w_data[0], &add_info.info1->size.width, 4, "width mismatch");
+ torture_assert_mem_equal(tctx, &w_data[4], &add_info.info1->size.height, 4, "height mismatch");
+ torture_assert_mem_equal(tctx, &w_data[8], &add_info.info1->area.left, 4, "left mismatch");
+ torture_assert_mem_equal(tctx, &w_data[12], &add_info.info1->area.top, 4, "top mismatch");
+ torture_assert_mem_equal(tctx, &w_data[16], &add_info.info1->area.right, 4, "right mismatch");
+ torture_assert_mem_equal(tctx, &w_data[20], &add_info.info1->area.bottom, 4, "bottom mismatch");
+ /* skip index here */
+ torture_assert_mem_equal(tctx, &w_data[28], &add_info.info1->flags, 4, "flags mismatch");
+ }
+
+ if (!print_server) {
+ torture_assert(tctx,
+ test_GetForm_args(tctx, b, handle, form_name, 1, &info),
+ "failed to get added form");
+
+ torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch");
+ torture_assert_int_equal(tctx, info.info1.size.height, add_info.info1->size.height, "height mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.left, add_info.info1->area.left, "left mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.top, add_info.info1->area.top, "top mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.right, add_info.info1->area.right, "right mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.bottom, add_info.info1->area.bottom, "bottom mismatch");
+ torture_assert_int_equal(tctx, info.info1.flags, add_info.info1->flags, "flags mismatch");
+
+ if (winreg_handle && hive_handle) {
+ torture_assert_mem_equal(tctx, &w_data[0], &info.info1.size.width, 4, "width mismatch");
+ torture_assert_mem_equal(tctx, &w_data[4], &info.info1.size.height, 4, "height mismatch");
+ torture_assert_mem_equal(tctx, &w_data[8], &info.info1.area.left, 4, "left mismatch");
+ torture_assert_mem_equal(tctx, &w_data[12], &info.info1.area.top, 4, "top mismatch");
+ torture_assert_mem_equal(tctx, &w_data[16], &info.info1.area.right, 4, "right mismatch");
+ torture_assert_mem_equal(tctx, &w_data[20], &info.info1.area.bottom, 4, "bottom mismatch");
+ /* skip index here */
+ torture_assert_mem_equal(tctx, &w_data[28], &info.info1.flags, 4, "flags mismatch");
}
- torture_assert(tctx, found, "Newly added form not found in enum call");
- }
- if (!test_DeleteForm(tctx, b, handle, form_name)) {
- ret = false;
+ add_info.info1->size.width = 1234;
+
+ torture_assert(tctx,
+ test_SetForm(tctx, b, handle, form_name, 1, &add_info),
+ "failed to set form");
+ torture_assert(tctx,
+ test_GetForm_args(tctx, b, handle, form_name, 1, &info),
+ "failed to get setted form");
+
+ torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch");
}
- return ret;
+ torture_assert(tctx,
+ test_EnumForms_find_one(tctx, b, handle, print_server, form_name),
+ "Newly added form not found in enum call");
+
+ torture_assert(tctx,
+ test_DeleteForm(tctx, b, handle, form_name),
+ "failed to delete form");
+
+ return true;
}
static bool test_EnumPorts_old(struct torture_context *tctx,
@@ -3576,7 +3721,7 @@ static bool test_winreg_query_printerdata(struct torture_context *tctx,
struct policy_handle key_handle;
printer_key = talloc_asprintf(tctx, "%s\\%s\\%s",
- TOP_LEVEL_PRINTER_KEY, printer_name, key_name);
+ TOP_LEVEL_PRINT_PRINTERS_KEY, printer_name, key_name);
torture_assert(tctx,
test_winreg_OpenKey(tctx, b, handle, printer_key, &key_handle), "");
@@ -3590,6 +3735,30 @@ static bool test_winreg_query_printerdata(struct torture_context *tctx,
return true;
}
+static bool test_GetForm_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *form_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data)
+{
+ struct policy_handle key_handle;
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, b, handle, key_name, &key_handle), "");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, &key_handle, form_name, w_type, w_size, w_length, w_data), "");
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, b, &key_handle), "");
+
+ return true;
+}
+
static bool test_SetPrinterData(struct torture_context *tctx,
struct dcerpc_binding_handle *b,
struct policy_handle *handle,
@@ -3937,6 +4106,33 @@ static bool test_PrinterData_winreg(struct torture_context *tctx,
return ret;
}
+static bool test_Forms_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name)
+{
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_Forms(tctx, b, handle, print_server, printer_name, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
static bool test_GetChangeID_PrinterData(struct torture_context *tctx,
struct dcerpc_binding_handle *b,
struct policy_handle *handle,
@@ -4395,11 +4591,15 @@ static bool test_OpenPrinterEx(struct torture_context *tctx,
ret = false;
}
- if (!test_EnumForms(tctx, b, &handle, false)) {
+ if (!test_EnumForms_all(tctx, b, &handle, false)) {
+ ret = false;
+ }
+
+ if (!test_Forms(tctx, b, &handle, false, name, NULL, NULL)) {
ret = false;
}
- if (!test_AddForm(tctx, b, &handle, false)) {
+ if (!test_Forms_winreg(tctx, b, &handle, false, name)) {
ret = false;
}
@@ -5398,8 +5598,9 @@ bool torture_rpc_spoolss(struct torture_context *torture)
ret &= test_OpenPrinter_server(torture, p, &ctx->server_handle);
ret &= test_GetPrinterData_list(torture, p, &ctx->server_handle, &environment);
- ret &= test_EnumForms(torture, b, &ctx->server_handle, true);
- ret &= test_AddForm(torture, b, &ctx->server_handle, true);
+ ret &= test_EnumForms_all(torture, b, &ctx->server_handle, true);
+ ret &= test_Forms(torture, b, &ctx->server_handle, true, NULL, NULL, NULL);
+ ret &= test_Forms_winreg(torture, b, &ctx->server_handle, true, NULL);
ret &= test_EnumPorts(torture, b, ctx);
ret &= test_GetPrinterDriverDirectory(torture, p, ctx, environment);
ret &= test_GetPrintProcessorDirectory(torture, p, ctx, environment);