4 Copyright (C) Simo Sorce 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 * Name: ldb_controls.c
27 * Component: ldb controls utility functions
29 * Description: helper functions for control modules
34 #include "ldb_private.h"
36 /* check if a control with the specified "oid" exist and return it */
37 /* returns NULL if not found */
38 struct ldb_control
*ldb_request_get_control(struct ldb_request
*req
, const char *oid
)
42 if (req
->controls
!= NULL
) {
43 for (i
= 0; req
->controls
[i
]; i
++) {
44 if (strcmp(oid
, req
->controls
[i
]->oid
) == 0) {
49 return req
->controls
[i
];
55 /* check if a control with the specified "oid" exist and return it */
56 /* returns NULL if not found */
57 struct ldb_control
*ldb_reply_get_control(struct ldb_reply
*rep
, const char *oid
)
61 if (rep
->controls
!= NULL
) {
62 for (i
= 0; rep
->controls
[i
]; i
++) {
63 if (strcmp(oid
, rep
->controls
[i
]->oid
) == 0) {
68 return rep
->controls
[i
];
74 /* saves the current controls list into the "saver" and replace the one in req with a new one excluding
75 the "exclude" control */
76 /* returns 0 on error */
77 int save_controls(struct ldb_control
*exclude
, struct ldb_request
*req
, struct ldb_control
***saver
)
79 struct ldb_control
**lcs
;
82 *saver
= req
->controls
;
83 for (i
= 0; req
->controls
[i
]; i
++);
89 lcs
= talloc_array(req
, struct ldb_control
*, i
);
94 for (i
= 0, j
= 0; (*saver
)[i
]; i
++) {
95 if (exclude
== (*saver
)[i
]) continue;
105 /* Returns a list of controls, except the one specified. Included
106 * controls become a child of returned list if they were children of
108 struct ldb_control
**controls_except_specified(struct ldb_control
**controls_in
,
110 struct ldb_control
*exclude
)
112 struct ldb_control
**lcs
= NULL
;
115 for (i
= 0; controls_in
&& controls_in
[i
]; i
++);
121 for (i
= 0, j
= 0; controls_in
&& controls_in
[i
]; i
++) {
122 if (exclude
== controls_in
[i
]) continue;
125 /* Allocate here so if we remove the only
126 * control, or there were no controls, we
127 * don't allocate at all, and just return
129 lcs
= talloc_array(mem_ctx
, struct ldb_control
*, i
);
135 lcs
[j
] = controls_in
[i
];
136 talloc_reparent(controls_in
, lcs
, lcs
[j
]);
146 /* check if there's any control marked as critical in the list */
147 /* return True if any, False if none */
148 int check_critical_controls(struct ldb_control
**controls
)
152 if (controls
== NULL
) {
156 for (i
= 0; controls
[i
]; i
++) {
157 if (controls
[i
]->critical
) {
165 int ldb_request_add_control(struct ldb_request
*req
, const char *oid
, bool critical
, void *data
)
168 struct ldb_control
**ctrls
;
169 struct ldb_control
*ctrl
;
171 for (n
=0; req
->controls
&& req
->controls
[n
];n
++) {
172 /* having two controls of the same OID makes no sense */
173 if (strcmp(oid
, req
->controls
[n
]->oid
) == 0) {
174 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
178 ctrls
= talloc_array(req
,
179 struct ldb_control
*,
181 if (!ctrls
) return LDB_ERR_OPERATIONS_ERROR
;
183 for (i
=0; i
<n
; i
++) {
184 ctrls
[i
] = req
->controls
[i
];
187 req
->controls
= ctrls
;
191 ctrl
= talloc(ctrls
, struct ldb_control
);
192 if (!ctrl
) return LDB_ERR_OPERATIONS_ERROR
;
194 ctrl
->oid
= talloc_strdup(ctrl
, oid
);
195 if (!ctrl
->oid
) return LDB_ERR_OPERATIONS_ERROR
;
196 ctrl
->critical
= critical
;
203 int ldb_reply_add_control(struct ldb_reply
*ares
, const char *oid
, bool critical
, void *data
)
206 struct ldb_control
**ctrls
;
207 struct ldb_control
*ctrl
;
209 for (n
=0; ares
->controls
&& ares
->controls
[n
];) {
210 /* having two controls of the same OID makes no sense */
211 if (strcmp(oid
, ares
->controls
[n
]->oid
) == 0) {
212 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS
;
217 ctrls
= talloc_realloc(ares
, ares
->controls
,
218 struct ldb_control
*,
220 if (!ctrls
) return LDB_ERR_OPERATIONS_ERROR
;
221 ares
->controls
= ctrls
;
225 ctrl
= talloc(ctrls
, struct ldb_control
);
226 if (!ctrl
) return LDB_ERR_OPERATIONS_ERROR
;
228 ctrl
->oid
= talloc_strdup(ctrl
, oid
);
229 if (!ctrl
->oid
) return LDB_ERR_OPERATIONS_ERROR
;
230 ctrl
->critical
= critical
;
237 /* Parse controls from the format used on the command line and in ejs */
239 struct ldb_control
**ldb_parse_control_strings(struct ldb_context
*ldb
, void *mem_ctx
, const char **control_strings
)
242 struct ldb_control
**ctrl
;
244 char *error_string
= NULL
;
246 if (control_strings
== NULL
|| control_strings
[0] == NULL
)
249 for (i
= 0; control_strings
[i
]; i
++);
251 ctrl
= talloc_array(mem_ctx
, struct ldb_control
*, i
+ 1);
253 for (i
= 0; control_strings
[i
]; i
++) {
254 if (strncmp(control_strings
[i
], "vlv:", 4) == 0) {
255 struct ldb_vlv_req_control
*control
;
259 int crit
, bc
, ac
, os
, cc
, ret
;
263 p
= &(control_strings
[i
][4]);
264 ret
= sscanf(p
, "%d:%d:%d:%d:%d:%1023[^$]", &crit
, &bc
, &ac
, &os
, &cc
, ctxid
);
266 ret
= sscanf(p
, "%d:%d:%d:%1023[^:]:%1023[^$]", &crit
, &bc
, &ac
, attr
, ctxid
);
269 if ((ret
< 4) || (crit
< 0) || (crit
> 1)) {
270 error_string
= talloc_asprintf(mem_ctx
, "invalid server_sort control syntax\n");
271 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b):bc(n):ac(n):<os(n):cc(n)|attr(s)>[:ctxid(o)]\n");
272 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean, n = number, s = string, o = b64 binary blob");
273 ldb_set_errstring(ldb
, error_string
);
274 talloc_free(error_string
);
277 if (!(ctrl
[i
] = talloc(ctrl
, struct ldb_control
))) {
281 ctrl
[i
]->oid
= LDB_CONTROL_VLV_REQ_OID
;
282 ctrl
[i
]->critical
= crit
;
283 if (!(control
= talloc(ctrl
[i
],
284 struct ldb_vlv_req_control
))) {
288 control
->beforeCount
= bc
;
289 control
->afterCount
= ac
;
292 control
->match
.gtOrEq
.value
= talloc_strdup(control
, attr
);
293 control
->match
.gtOrEq
.value_len
= strlen(attr
);
296 control
->match
.byOffset
.offset
= os
;
297 control
->match
.byOffset
.contentCount
= cc
;
300 control
->ctxid_len
= ldb_base64_decode(ctxid
);
301 control
->contextId
= (char *)talloc_memdup(control
, ctxid
, control
->ctxid_len
);
303 control
->ctxid_len
= 0;
304 control
->contextId
= NULL
;
306 ctrl
[i
]->data
= control
;
311 if (strncmp(control_strings
[i
], "dirsync:", 8) == 0) {
312 struct ldb_dirsync_control
*control
;
315 int crit
, flags
, max_attrs
, ret
;
318 p
= &(control_strings
[i
][8]);
319 ret
= sscanf(p
, "%d:%d:%d:%1023[^$]", &crit
, &flags
, &max_attrs
, cookie
);
321 if ((ret
< 3) || (crit
< 0) || (crit
> 1) || (flags
< 0) || (max_attrs
< 0)) {
322 error_string
= talloc_asprintf(mem_ctx
, "invalid dirsync control syntax\n");
323 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b):flags(n):max_attrs(n)[:cookie(o)]\n");
324 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean, n = number, o = b64 binary blob");
325 ldb_set_errstring(ldb
, error_string
);
326 talloc_free(error_string
);
330 /* w2k3 seems to ignore the parameter,
331 * but w2k sends a wrong cookie when this value is to small
332 * this would cause looping forever, while getting
333 * the same data and same cookie forever
335 if (max_attrs
== 0) max_attrs
= 0x0FFFFFFF;
337 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
338 ctrl
[i
]->oid
= LDB_CONTROL_DIRSYNC_OID
;
339 ctrl
[i
]->critical
= crit
;
340 control
= talloc(ctrl
[i
], struct ldb_dirsync_control
);
341 control
->flags
= flags
;
342 control
->max_attributes
= max_attrs
;
344 control
->cookie_len
= ldb_base64_decode(cookie
);
345 control
->cookie
= (char *)talloc_memdup(control
, cookie
, control
->cookie_len
);
347 control
->cookie
= NULL
;
348 control
->cookie_len
= 0;
350 ctrl
[i
]->data
= control
;
355 if (strncmp(control_strings
[i
], "asq:", 4) == 0) {
356 struct ldb_asq_control
*control
;
362 p
= &(control_strings
[i
][4]);
363 ret
= sscanf(p
, "%d:%255[^$]", &crit
, attr
);
364 if ((ret
!= 2) || (crit
< 0) || (crit
> 1) || (attr
[0] == '\0')) {
365 error_string
= talloc_asprintf(mem_ctx
, "invalid asq control syntax\n");
366 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b):attr(s)\n");
367 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean, s = string");
368 ldb_set_errstring(ldb
, error_string
);
369 talloc_free(error_string
);
373 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
378 ctrl
[i
]->oid
= LDB_CONTROL_ASQ_OID
;
379 ctrl
[i
]->critical
= crit
;
380 control
= talloc(ctrl
[i
], struct ldb_asq_control
);
381 control
->request
= 1;
382 control
->source_attribute
= talloc_strdup(control
, attr
);
383 control
->src_attr_len
= strlen(attr
);
384 ctrl
[i
]->data
= control
;
389 if (strncmp(control_strings
[i
], "extended_dn:", 12) == 0) {
390 struct ldb_extended_dn_control
*control
;
394 p
= &(control_strings
[i
][12]);
395 ret
= sscanf(p
, "%d:%d", &crit
, &type
);
396 if ((ret
!= 2) || (crit
< 0) || (crit
> 1) || (type
< 0) || (type
> 1)) {
397 ret
= sscanf(p
, "%d", &crit
);
398 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
399 error_string
= talloc_asprintf(mem_ctx
, "invalid extended_dn control syntax\n");
400 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)[:type(i)]\n");
401 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean\n");
402 error_string
= talloc_asprintf_append(error_string
, " i = integer\n");
403 error_string
= talloc_asprintf_append(error_string
, " valid values are: 0 - hexadecimal representation\n");
404 error_string
= talloc_asprintf_append(error_string
, " 1 - normal string representation");
405 ldb_set_errstring(ldb
, error_string
);
406 talloc_free(error_string
);
411 control
= talloc(ctrl
, struct ldb_extended_dn_control
);
412 control
->type
= type
;
415 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
420 ctrl
[i
]->oid
= LDB_CONTROL_EXTENDED_DN_OID
;
421 ctrl
[i
]->critical
= crit
;
422 ctrl
[i
]->data
= talloc_steal(ctrl
[i
], control
);
427 if (strncmp(control_strings
[i
], "sd_flags:", 9) == 0) {
428 struct ldb_sd_flags_control
*control
;
431 unsigned secinfo_flags
;
433 p
= &(control_strings
[i
][9]);
434 ret
= sscanf(p
, "%d:%u", &crit
, &secinfo_flags
);
435 if ((ret
!= 2) || (crit
< 0) || (crit
> 1) || (secinfo_flags
< 0) || (secinfo_flags
> 0xF)) {
436 error_string
= talloc_asprintf(mem_ctx
, "invalid sd_flags control syntax\n");
437 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b):secinfo_flags(n)\n");
438 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean, n = number");
439 ldb_set_errstring(ldb
, error_string
);
440 talloc_free(error_string
);
444 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
449 ctrl
[i
]->oid
= LDB_CONTROL_SD_FLAGS_OID
;
450 ctrl
[i
]->critical
= crit
;
451 control
= talloc(ctrl
[i
], struct ldb_sd_flags_control
);
452 control
->secinfo_flags
= secinfo_flags
;
453 ctrl
[i
]->data
= control
;
458 if (strncmp(control_strings
[i
], "search_options:", 15) == 0) {
459 struct ldb_search_options_control
*control
;
462 unsigned search_options
;
464 p
= &(control_strings
[i
][15]);
465 ret
= sscanf(p
, "%d:%u", &crit
, &search_options
);
466 if ((ret
!= 2) || (crit
< 0) || (crit
> 1) || (search_options
< 0) || (search_options
> 0xF)) {
467 error_string
= talloc_asprintf(mem_ctx
, "invalid search_options control syntax\n");
468 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b):search_options(n)\n");
469 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean, n = number");
470 ldb_set_errstring(ldb
, error_string
);
471 talloc_free(error_string
);
475 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
480 ctrl
[i
]->oid
= LDB_CONTROL_SEARCH_OPTIONS_OID
;
481 ctrl
[i
]->critical
= crit
;
482 control
= talloc(ctrl
[i
], struct ldb_search_options_control
);
483 control
->search_options
= search_options
;
484 ctrl
[i
]->data
= control
;
489 if (strncmp(control_strings
[i
], "relax:", 6) == 0) {
493 p
= &(control_strings
[i
][6]);
494 ret
= sscanf(p
, "%d", &crit
);
495 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
496 error_string
= talloc_asprintf(mem_ctx
, "invalid relax control syntax\n");
497 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)\n");
498 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean");
499 ldb_set_errstring(ldb
, error_string
);
500 talloc_free(error_string
);
504 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
509 ctrl
[i
]->oid
= LDB_CONTROL_RELAX_OID
;
510 ctrl
[i
]->critical
= crit
;
511 ctrl
[i
]->data
= NULL
;
516 if (strncmp(control_strings
[i
], "domain_scope:", 13) == 0) {
520 p
= &(control_strings
[i
][13]);
521 ret
= sscanf(p
, "%d", &crit
);
522 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
523 error_string
= talloc_asprintf(mem_ctx
, "invalid domain_scope control syntax\n");
524 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)\n");
525 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean");
526 ldb_set_errstring(ldb
, error_string
);
527 talloc_free(error_string
);
531 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
536 ctrl
[i
]->oid
= LDB_CONTROL_DOMAIN_SCOPE_OID
;
537 ctrl
[i
]->critical
= crit
;
538 ctrl
[i
]->data
= NULL
;
543 if (strncmp(control_strings
[i
], "paged_results:", 14) == 0) {
544 struct ldb_paged_control
*control
;
548 p
= &(control_strings
[i
][14]);
549 ret
= sscanf(p
, "%d:%d", &crit
, &size
);
551 if ((ret
!= 2) || (crit
< 0) || (crit
> 1) || (size
< 0)) {
552 error_string
= talloc_asprintf(mem_ctx
, "invalid paged_results control syntax\n");
553 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b):size(n)\n");
554 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean, n = number");
555 ldb_set_errstring(ldb
, error_string
);
556 talloc_free(error_string
);
560 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
565 ctrl
[i
]->oid
= LDB_CONTROL_PAGED_RESULTS_OID
;
566 ctrl
[i
]->critical
= crit
;
567 control
= talloc(ctrl
[i
], struct ldb_paged_control
);
568 control
->size
= size
;
569 control
->cookie
= NULL
;
570 control
->cookie_len
= 0;
571 ctrl
[i
]->data
= control
;
576 if (strncmp(control_strings
[i
], "server_sort:", 12) == 0) {
577 struct ldb_server_sort_control
**control
;
585 p
= &(control_strings
[i
][12]);
586 ret
= sscanf(p
, "%d:%d:%255[^:]:%127[^:]", &crit
, &rev
, attr
, rule
);
587 if ((ret
< 3) || (crit
< 0) || (crit
> 1) || (rev
< 0 ) || (rev
> 1) ||attr
[0] == '\0') {
588 error_string
= talloc_asprintf(mem_ctx
, "invalid server_sort control syntax\n");
589 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b):rev(b):attr(s)[:rule(s)]\n");
590 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean, s = string");
591 ldb_set_errstring(ldb
, error_string
);
592 talloc_free(error_string
);
595 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
600 ctrl
[i
]->oid
= LDB_CONTROL_SERVER_SORT_OID
;
601 ctrl
[i
]->critical
= crit
;
602 control
= talloc_array(ctrl
[i
], struct ldb_server_sort_control
*, 2);
603 control
[0] = talloc(control
, struct ldb_server_sort_control
);
604 control
[0]->attributeName
= talloc_strdup(control
, attr
);
606 control
[0]->orderingRule
= talloc_strdup(control
, rule
);
608 control
[0]->orderingRule
= NULL
;
609 control
[0]->reverse
= rev
;
611 ctrl
[i
]->data
= control
;
616 if (strncmp(control_strings
[i
], "notification:", 13) == 0) {
620 p
= &(control_strings
[i
][13]);
621 ret
= sscanf(p
, "%d", &crit
);
622 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
623 error_string
= talloc_asprintf(mem_ctx
, "invalid notification control syntax\n");
624 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)\n");
625 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean");
626 ldb_set_errstring(ldb
, error_string
);
627 talloc_free(error_string
);
631 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
636 ctrl
[i
]->oid
= LDB_CONTROL_NOTIFICATION_OID
;
637 ctrl
[i
]->critical
= crit
;
638 ctrl
[i
]->data
= NULL
;
643 if (strncmp(control_strings
[i
], "show_deleted:", 13) == 0) {
647 p
= &(control_strings
[i
][13]);
648 ret
= sscanf(p
, "%d", &crit
);
649 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
650 error_string
= talloc_asprintf(mem_ctx
, "invalid show_deleted control syntax\n");
651 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)\n");
652 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean");
653 ldb_set_errstring(ldb
, error_string
);
654 talloc_free(error_string
);
658 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
663 ctrl
[i
]->oid
= LDB_CONTROL_SHOW_DELETED_OID
;
664 ctrl
[i
]->critical
= crit
;
665 ctrl
[i
]->data
= NULL
;
670 if (strncmp(control_strings
[i
], "show_deactivated_link:", 22) == 0) {
674 p
= &(control_strings
[i
][22]);
675 ret
= sscanf(p
, "%d", &crit
);
676 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
677 error_string
= talloc_asprintf(mem_ctx
, "invalid show_deactivated_link control syntax\n");
678 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)\n");
679 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean");
680 ldb_set_errstring(ldb
, error_string
);
681 talloc_free(error_string
);
685 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
690 ctrl
[i
]->oid
= LDB_CONTROL_SHOW_DEACTIVATED_LINK_OID
;
691 ctrl
[i
]->critical
= crit
;
692 ctrl
[i
]->data
= NULL
;
697 if (strncmp(control_strings
[i
], "show_recycled:", 14) == 0) {
701 p
= &(control_strings
[i
][14]);
702 ret
= sscanf(p
, "%d", &crit
);
703 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
704 error_string
= talloc_asprintf(mem_ctx
, "invalid show_recycled control syntax\n");
705 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)\n");
706 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean");
707 ldb_set_errstring(ldb
, error_string
);
708 talloc_free(error_string
);
712 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
717 ctrl
[i
]->oid
= LDB_CONTROL_SHOW_RECYCLED_OID
;
718 ctrl
[i
]->critical
= crit
;
719 ctrl
[i
]->data
= NULL
;
724 if (strncmp(control_strings
[i
], "permissive_modify:", 18) == 0) {
728 p
= &(control_strings
[i
][18]);
729 ret
= sscanf(p
, "%d", &crit
);
730 if ((ret
!= 1) || (crit
< 0) || (crit
> 1)) {
731 error_string
= talloc_asprintf(mem_ctx
, "invalid permissive_modify control syntax\n");
732 error_string
= talloc_asprintf_append(error_string
, " syntax: crit(b)\n");
733 error_string
= talloc_asprintf_append(error_string
, " note: b = boolean");
734 ldb_set_errstring(ldb
, error_string
);
735 talloc_free(error_string
);
739 ctrl
[i
] = talloc(ctrl
, struct ldb_control
);
744 ctrl
[i
]->oid
= LDB_CONTROL_PERMISSIVE_MODIFY_OID
;
745 ctrl
[i
]->critical
= crit
;
746 ctrl
[i
]->data
= NULL
;
751 /* no controls matched, throw an error */
752 ldb_asprintf_errstring(ldb
, "Invalid control name: '%s'", control_strings
[i
]);