1/* 2 ldb database library 3 4 Copyright (C) Simo Sorce 2005 5 6 ** NOTE! The following LGPL license applies to the ldb 7 ** library. This does NOT imply that all of Samba is released 8 ** under the LGPL 9 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. 14 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. 19 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/>. 22*/ 23 24/* 25 * Name: ldb_controls.c 26 * 27 * Component: ldb controls utility functions 28 * 29 * Description: helper functions for control modules 30 * 31 * Author: Simo Sorce 32 */ 33 34#include "ldb_private.h" 35 36/* check if a control with the specified "oid" exist and return it */ 37/* returns NULL if not found */ 38struct ldb_control *ldb_request_get_control(struct ldb_request *req, const char *oid) 39{ 40 int i; 41 42 if (req->controls != NULL) { 43 for (i = 0; req->controls[i]; i++) { 44 if (strcmp(oid, req->controls[i]->oid) == 0) { 45 break; 46 } 47 } 48 49 return req->controls[i]; 50 } 51 52 return NULL; 53} 54 55/* check if a control with the specified "oid" exist and return it */ 56/* returns NULL if not found */ 57struct ldb_control *ldb_reply_get_control(struct ldb_reply *rep, const char *oid) 58{ 59 int i; 60 61 if (rep->controls != NULL) { 62 for (i = 0; rep->controls[i]; i++) { 63 if (strcmp(oid, rep->controls[i]->oid) == 0) { 64 break; 65 } 66 } 67 68 return rep->controls[i]; 69 } 70 71 return NULL; 72} 73 74/* saves the current controls list into the "saver" and replace the one in req with a new one excluding 75the "exclude" control */ 76/* returns 0 on error */ 77int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver) 78{ 79 struct ldb_control **lcs; 80 int i, j; 81 82 *saver = req->controls; 83 for (i = 0; req->controls[i]; i++); 84 if (i == 1) { 85 req->controls = NULL; 86 return 1; 87 } 88 89 lcs = talloc_array(req, struct ldb_control *, i); 90 if (!lcs) { 91 return 0; 92 } 93 94 for (i = 0, j = 0; (*saver)[i]; i++) { 95 if (exclude == (*saver)[i]) continue; 96 lcs[j] = (*saver)[i]; 97 j++; 98 } 99 lcs[j] = NULL; 100 101 req->controls = lcs; 102 return 1; 103} 104 105/* check if there's any control marked as critical in the list */ 106/* return True if any, False if none */ 107int check_critical_controls(struct ldb_control **controls) 108{ 109 int i; 110 111 if (controls == NULL) { 112 return 0; 113 } 114 115 for (i = 0; controls[i]; i++) { 116 if (controls[i]->critical) { 117 return 1; 118 } 119 } 120 121 return 0; 122} 123 124int ldb_request_add_control(struct ldb_request *req, const char *oid, bool critical, void *data) 125{ 126 unsigned n; 127 struct ldb_control **ctrls; 128 struct ldb_control *ctrl; 129 130 for (n=0; req->controls && req->controls[n];) { 131 /* having two controls of the same OID makes no sense */ 132 if (strcmp(oid, req->controls[n]->oid) == 0) { 133 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; 134 } 135 n++; 136 } 137 138 ctrls = talloc_realloc(req, req->controls, 139 struct ldb_control *, 140 n + 2); 141 if (!ctrls) return LDB_ERR_OPERATIONS_ERROR; 142 req->controls = ctrls; 143 ctrls[n] = NULL; 144 ctrls[n+1] = NULL; 145 146 ctrl = talloc(ctrls, struct ldb_control); 147 if (!ctrl) return LDB_ERR_OPERATIONS_ERROR; 148 149 ctrl->oid = talloc_strdup(ctrl, oid); 150 if (!ctrl->oid) return LDB_ERR_OPERATIONS_ERROR; 151 ctrl->critical = critical; 152 ctrl->data = data; 153 154 ctrls[n] = ctrl; 155 return LDB_SUCCESS; 156} 157 158/* Parse controls from the format used on the command line and in ejs */ 159 160struct ldb_control **ldb_parse_control_strings(struct ldb_context *ldb, void *mem_ctx, const char **control_strings) 161{ 162 int i; 163 struct ldb_control **ctrl; 164 165 char *error_string = NULL; 166 167 if (control_strings == NULL || control_strings[0] == NULL) 168 return NULL; 169 170 for (i = 0; control_strings[i]; i++); 171 172 ctrl = talloc_array(mem_ctx, struct ldb_control *, i + 1); 173 174 for (i = 0; control_strings[i]; i++) { 175 if (strncmp(control_strings[i], "vlv:", 4) == 0) { 176 struct ldb_vlv_req_control *control; 177 const char *p; 178 char attr[1024]; 179 char ctxid[1024]; 180 int crit, bc, ac, os, cc, ret; 181 182 attr[0] = '\0'; 183 ctxid[0] = '\0'; 184 p = &(control_strings[i][4]); 185 ret = sscanf(p, "%d:%d:%d:%d:%d:%1023[^$]", &crit, &bc, &ac, &os, &cc, ctxid); 186 if (ret < 5) { 187 ret = sscanf(p, "%d:%d:%d:%1023[^:]:%1023[^$]", &crit, &bc, &ac, attr, ctxid); 188 } 189 190 if ((ret < 4) || (crit < 0) || (crit > 1)) { 191 error_string = talloc_asprintf(mem_ctx, "invalid server_sort control syntax\n"); 192 error_string = talloc_asprintf_append(error_string, " syntax: crit(b):bc(n):ac(n):<os(n):cc(n)|attr(s)>[:ctxid(o)]\n"); 193 error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, s = string, o = b64 binary blob"); 194 ldb_set_errstring(ldb, error_string); 195 talloc_free(error_string); 196 return NULL; 197 } 198 if (!(ctrl[i] = talloc(ctrl, struct ldb_control))) { 199 ldb_oom(ldb); 200 return NULL; 201 } 202 ctrl[i]->oid = LDB_CONTROL_VLV_REQ_OID; 203 ctrl[i]->critical = crit; 204 if (!(control = talloc(ctrl[i], 205 struct ldb_vlv_req_control))) { 206 ldb_oom(ldb); 207 return NULL; 208 } 209 control->beforeCount = bc; 210 control->afterCount = ac; 211 if (attr[0]) { 212 control->type = 1; 213 control->match.gtOrEq.value = talloc_strdup(control, attr); 214 control->match.gtOrEq.value_len = strlen(attr); 215 } else { 216 control->type = 0; 217 control->match.byOffset.offset = os; 218 control->match.byOffset.contentCount = cc; 219 } 220 if (ctxid[0]) { 221 control->ctxid_len = ldb_base64_decode(ctxid); 222 control->contextId = (char *)talloc_memdup(control, ctxid, control->ctxid_len); 223 } else { 224 control->ctxid_len = 0; 225 control->contextId = NULL; 226 } 227 ctrl[i]->data = control; 228 229 continue; 230 } 231 232 if (strncmp(control_strings[i], "dirsync:", 8) == 0) { 233 struct ldb_dirsync_control *control; 234 const char *p; 235 char cookie[1024]; 236 int crit, flags, max_attrs, ret; 237 238 cookie[0] = '\0'; 239 p = &(control_strings[i][8]); 240 ret = sscanf(p, "%d:%d:%d:%1023[^$]", &crit, &flags, &max_attrs, cookie); 241 242 if ((ret < 3) || (crit < 0) || (crit > 1) || (flags < 0) || (max_attrs < 0)) { 243 error_string = talloc_asprintf(mem_ctx, "invalid dirsync control syntax\n"); 244 error_string = talloc_asprintf_append(error_string, " syntax: crit(b):flags(n):max_attrs(n)[:cookie(o)]\n"); 245 error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number, o = b64 binary blob"); 246 ldb_set_errstring(ldb, error_string); 247 talloc_free(error_string); 248 return NULL; 249 } 250 251 /* w2k3 seems to ignore the parameter, 252 * but w2k sends a wrong cookie when this value is to small 253 * this would cause looping forever, while getting 254 * the same data and same cookie forever 255 */ 256 if (max_attrs == 0) max_attrs = 0x0FFFFFFF; 257 258 ctrl[i] = talloc(ctrl, struct ldb_control); 259 ctrl[i]->oid = LDB_CONTROL_DIRSYNC_OID; 260 ctrl[i]->critical = crit; 261 control = talloc(ctrl[i], struct ldb_dirsync_control); 262 control->flags = flags; 263 control->max_attributes = max_attrs; 264 if (*cookie) { 265 control->cookie_len = ldb_base64_decode(cookie); 266 control->cookie = (char *)talloc_memdup(control, cookie, control->cookie_len); 267 } else { 268 control->cookie = NULL; 269 control->cookie_len = 0; 270 } 271 ctrl[i]->data = control; 272 273 continue; 274 } 275 276 if (strncmp(control_strings[i], "asq:", 4) == 0) { 277 struct ldb_asq_control *control; 278 const char *p; 279 char attr[256]; 280 int crit, ret; 281 282 attr[0] = '\0'; 283 p = &(control_strings[i][4]); 284 ret = sscanf(p, "%d:%255[^$]", &crit, attr); 285 if ((ret != 2) || (crit < 0) || (crit > 1) || (attr[0] == '\0')) { 286 error_string = talloc_asprintf(mem_ctx, "invalid asq control syntax\n"); 287 error_string = talloc_asprintf_append(error_string, " syntax: crit(b):attr(s)\n"); 288 error_string = talloc_asprintf_append(error_string, " note: b = boolean, s = string"); 289 ldb_set_errstring(ldb, error_string); 290 talloc_free(error_string); 291 return NULL; 292 } 293 294 ctrl[i] = talloc(ctrl, struct ldb_control); 295 if (!ctrl[i]) { 296 ldb_oom(ldb); 297 return NULL; 298 } 299 ctrl[i]->oid = LDB_CONTROL_ASQ_OID; 300 ctrl[i]->critical = crit; 301 control = talloc(ctrl[i], struct ldb_asq_control); 302 control->request = 1; 303 control->source_attribute = talloc_strdup(control, attr); 304 control->src_attr_len = strlen(attr); 305 ctrl[i]->data = control; 306 307 continue; 308 } 309 310 if (strncmp(control_strings[i], "extended_dn:", 12) == 0) { 311 struct ldb_extended_dn_control *control; 312 const char *p; 313 int crit, type, ret; 314 315 p = &(control_strings[i][12]); 316 ret = sscanf(p, "%d:%d", &crit, &type); 317 if ((ret != 2) || (crit < 0) || (crit > 1) || (type < 0) || (type > 1)) { 318 ret = sscanf(p, "%d", &crit); 319 if ((ret != 1) || (crit < 0) || (crit > 1)) { 320 error_string = talloc_asprintf(mem_ctx, "invalid extended_dn control syntax\n"); 321 error_string = talloc_asprintf_append(error_string, " syntax: crit(b)[:type(i)]\n"); 322 error_string = talloc_asprintf_append(error_string, " note: b = boolean\n"); 323 error_string = talloc_asprintf_append(error_string, " i = integer\n"); 324 error_string = talloc_asprintf_append(error_string, " valid values are: 0 - hexadecimal representation\n"); 325 error_string = talloc_asprintf_append(error_string, " 1 - normal string representation"); 326 ldb_set_errstring(ldb, error_string); 327 talloc_free(error_string); 328 return NULL; 329 } 330 control = NULL; 331 } else { 332 control = talloc(ctrl, struct ldb_extended_dn_control); 333 control->type = type; 334 } 335 336 ctrl[i] = talloc(ctrl, struct ldb_control); 337 if (!ctrl[i]) { 338 ldb_oom(ldb); 339 return NULL; 340 } 341 ctrl[i]->oid = LDB_CONTROL_EXTENDED_DN_OID; 342 ctrl[i]->critical = crit; 343 ctrl[i]->data = talloc_steal(ctrl[i], control); 344 345 continue; 346 } 347 348 if (strncmp(control_strings[i], "sd_flags:", 9) == 0) { 349 struct ldb_sd_flags_control *control; 350 const char *p; 351 int crit, ret; 352 unsigned secinfo_flags; 353 354 p = &(control_strings[i][9]); 355 ret = sscanf(p, "%d:%u", &crit, &secinfo_flags); 356 if ((ret != 2) || (crit < 0) || (crit > 1) || (secinfo_flags < 0) || (secinfo_flags > 0xF)) { 357 error_string = talloc_asprintf(mem_ctx, "invalid sd_flags control syntax\n"); 358 error_string = talloc_asprintf_append(error_string, " syntax: crit(b):secinfo_flags(n)\n"); 359 error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number"); 360 ldb_set_errstring(ldb, error_string); 361 talloc_free(error_string); 362 return NULL; 363 } 364 365 ctrl[i] = talloc(ctrl, struct ldb_control); 366 if (!ctrl[i]) { 367 ldb_oom(ldb); 368 return NULL; 369 } 370 ctrl[i]->oid = LDB_CONTROL_SD_FLAGS_OID; 371 ctrl[i]->critical = crit; 372 control = talloc(ctrl[i], struct ldb_sd_flags_control); 373 control->secinfo_flags = secinfo_flags; 374 ctrl[i]->data = control; 375 376 continue; 377 } 378 379 if (strncmp(control_strings[i], "search_options:", 15) == 0) { 380 struct ldb_search_options_control *control; 381 const char *p; 382 int crit, ret; 383 unsigned search_options; 384 385 p = &(control_strings[i][15]); 386 ret = sscanf(p, "%d:%u", &crit, &search_options); 387 if ((ret != 2) || (crit < 0) || (crit > 1) || (search_options < 0) || (search_options > 0xF)) { 388 error_string = talloc_asprintf(mem_ctx, "invalid search_options control syntax\n"); 389 error_string = talloc_asprintf_append(error_string, " syntax: crit(b):search_options(n)\n"); 390 error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number"); 391 ldb_set_errstring(ldb, error_string); 392 talloc_free(error_string); 393 return NULL; 394 } 395 396 ctrl[i] = talloc(ctrl, struct ldb_control); 397 if (!ctrl[i]) { 398 ldb_oom(ldb); 399 return NULL; 400 } 401 ctrl[i]->oid = LDB_CONTROL_SEARCH_OPTIONS_OID; 402 ctrl[i]->critical = crit; 403 control = talloc(ctrl[i], struct ldb_search_options_control); 404 control->search_options = search_options; 405 ctrl[i]->data = control; 406 407 continue; 408 } 409 410 if (strncmp(control_strings[i], "domain_scope:", 13) == 0) { 411 const char *p; 412 int crit, ret; 413 414 p = &(control_strings[i][13]); 415 ret = sscanf(p, "%d", &crit); 416 if ((ret != 1) || (crit < 0) || (crit > 1)) { 417 error_string = talloc_asprintf(mem_ctx, "invalid domain_scope control syntax\n"); 418 error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); 419 error_string = talloc_asprintf_append(error_string, " note: b = boolean"); 420 ldb_set_errstring(ldb, error_string); 421 talloc_free(error_string); 422 return NULL; 423 } 424 425 ctrl[i] = talloc(ctrl, struct ldb_control); 426 if (!ctrl[i]) { 427 ldb_oom(ldb); 428 return NULL; 429 } 430 ctrl[i]->oid = LDB_CONTROL_DOMAIN_SCOPE_OID; 431 ctrl[i]->critical = crit; 432 ctrl[i]->data = NULL; 433 434 continue; 435 } 436 437 if (strncmp(control_strings[i], "paged_results:", 14) == 0) { 438 struct ldb_paged_control *control; 439 const char *p; 440 int crit, size, ret; 441 442 p = &(control_strings[i][14]); 443 ret = sscanf(p, "%d:%d", &crit, &size); 444 445 if ((ret != 2) || (crit < 0) || (crit > 1) || (size < 0)) { 446 error_string = talloc_asprintf(mem_ctx, "invalid paged_results control syntax\n"); 447 error_string = talloc_asprintf_append(error_string, " syntax: crit(b):size(n)\n"); 448 error_string = talloc_asprintf_append(error_string, " note: b = boolean, n = number"); 449 ldb_set_errstring(ldb, error_string); 450 talloc_free(error_string); 451 return NULL; 452 } 453 454 ctrl[i] = talloc(ctrl, struct ldb_control); 455 if (!ctrl[i]) { 456 ldb_oom(ldb); 457 return NULL; 458 } 459 ctrl[i]->oid = LDB_CONTROL_PAGED_RESULTS_OID; 460 ctrl[i]->critical = crit; 461 control = talloc(ctrl[i], struct ldb_paged_control); 462 control->size = size; 463 control->cookie = NULL; 464 control->cookie_len = 0; 465 ctrl[i]->data = control; 466 467 continue; 468 } 469 470 if (strncmp(control_strings[i], "server_sort:", 12) == 0) { 471 struct ldb_server_sort_control **control; 472 const char *p; 473 char attr[256]; 474 char rule[128]; 475 int crit, rev, ret; 476 477 attr[0] = '\0'; 478 rule[0] = '\0'; 479 p = &(control_strings[i][12]); 480 ret = sscanf(p, "%d:%d:%255[^:]:%127[^:]", &crit, &rev, attr, rule); 481 if ((ret < 3) || (crit < 0) || (crit > 1) || (rev < 0 ) || (rev > 1) ||attr[0] == '\0') { 482 error_string = talloc_asprintf(mem_ctx, "invalid server_sort control syntax\n"); 483 error_string = talloc_asprintf_append(error_string, " syntax: crit(b):rev(b):attr(s)[:rule(s)]\n"); 484 error_string = talloc_asprintf_append(error_string, " note: b = boolean, s = string"); 485 ldb_set_errstring(ldb, error_string); 486 talloc_free(error_string); 487 return NULL; 488 } 489 ctrl[i] = talloc(ctrl, struct ldb_control); 490 if (!ctrl[i]) { 491 ldb_oom(ldb); 492 return NULL; 493 } 494 ctrl[i]->oid = LDB_CONTROL_SERVER_SORT_OID; 495 ctrl[i]->critical = crit; 496 control = talloc_array(ctrl[i], struct ldb_server_sort_control *, 2); 497 control[0] = talloc(control, struct ldb_server_sort_control); 498 control[0]->attributeName = talloc_strdup(control, attr); 499 if (rule[0]) 500 control[0]->orderingRule = talloc_strdup(control, rule); 501 else 502 control[0]->orderingRule = NULL; 503 control[0]->reverse = rev; 504 control[1] = NULL; 505 ctrl[i]->data = control; 506 507 continue; 508 } 509 510 if (strncmp(control_strings[i], "notification:", 13) == 0) { 511 const char *p; 512 int crit, ret; 513 514 p = &(control_strings[i][13]); 515 ret = sscanf(p, "%d", &crit); 516 if ((ret != 1) || (crit < 0) || (crit > 1)) { 517 error_string = talloc_asprintf(mem_ctx, "invalid notification control syntax\n"); 518 error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); 519 error_string = talloc_asprintf_append(error_string, " note: b = boolean"); 520 ldb_set_errstring(ldb, error_string); 521 talloc_free(error_string); 522 return NULL; 523 } 524 525 ctrl[i] = talloc(ctrl, struct ldb_control); 526 if (!ctrl[i]) { 527 ldb_oom(ldb); 528 return NULL; 529 } 530 ctrl[i]->oid = LDB_CONTROL_NOTIFICATION_OID; 531 ctrl[i]->critical = crit; 532 ctrl[i]->data = NULL; 533 534 continue; 535 } 536 537 if (strncmp(control_strings[i], "show_deleted:", 13) == 0) { 538 const char *p; 539 int crit, ret; 540 541 p = &(control_strings[i][13]); 542 ret = sscanf(p, "%d", &crit); 543 if ((ret != 1) || (crit < 0) || (crit > 1)) { 544 error_string = talloc_asprintf(mem_ctx, "invalid show_deleted control syntax\n"); 545 error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); 546 error_string = talloc_asprintf_append(error_string, " note: b = boolean"); 547 ldb_set_errstring(ldb, error_string); 548 talloc_free(error_string); 549 return NULL; 550 } 551 552 ctrl[i] = talloc(ctrl, struct ldb_control); 553 if (!ctrl[i]) { 554 ldb_oom(ldb); 555 return NULL; 556 } 557 ctrl[i]->oid = LDB_CONTROL_SHOW_DELETED_OID; 558 ctrl[i]->critical = crit; 559 ctrl[i]->data = NULL; 560 561 continue; 562 } 563 564 if (strncmp(control_strings[i], "show_deactivated_link:", 22) == 0) { 565 const char *p; 566 int crit, ret; 567 568 p = &(control_strings[i][22]); 569 ret = sscanf(p, "%d", &crit); 570 if ((ret != 1) || (crit < 0) || (crit > 1)) { 571 error_string = talloc_asprintf(mem_ctx, "invalid show_deactivated_link control syntax\n"); 572 error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); 573 error_string = talloc_asprintf_append(error_string, " note: b = boolean"); 574 ldb_set_errstring(ldb, error_string); 575 talloc_free(error_string); 576 return NULL; 577 } 578 579 ctrl[i] = talloc(ctrl, struct ldb_control); 580 if (!ctrl[i]) { 581 ldb_oom(ldb); 582 return NULL; 583 } 584 ctrl[i]->oid = LDB_CONTROL_SHOW_DEACTIVATED_LINK_OID; 585 ctrl[i]->critical = crit; 586 ctrl[i]->data = NULL; 587 588 continue; 589 } 590 591 if (strncmp(control_strings[i], "show_recycled:", 14) == 0) { 592 const char *p; 593 int crit, ret; 594 595 p = &(control_strings[i][14]); 596 ret = sscanf(p, "%d", &crit); 597 if ((ret != 1) || (crit < 0) || (crit > 1)) { 598 error_string = talloc_asprintf(mem_ctx, "invalid show_recycled control syntax\n"); 599 error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); 600 error_string = talloc_asprintf_append(error_string, " note: b = boolean"); 601 ldb_set_errstring(ldb, error_string); 602 talloc_free(error_string); 603 return NULL; 604 } 605 606 ctrl[i] = talloc(ctrl, struct ldb_control); 607 if (!ctrl[i]) { 608 ldb_oom(ldb); 609 return NULL; 610 } 611 ctrl[i]->oid = LDB_CONTROL_SHOW_RECYCLED_OID; 612 ctrl[i]->critical = crit; 613 ctrl[i]->data = NULL; 614 615 continue; 616 } 617 618 if (strncmp(control_strings[i], "permissive_modify:", 18) == 0) { 619 const char *p; 620 int crit, ret; 621 622 p = &(control_strings[i][18]); 623 ret = sscanf(p, "%d", &crit); 624 if ((ret != 1) || (crit < 0) || (crit > 1)) { 625 error_string = talloc_asprintf(mem_ctx, "invalid permissive_modify control syntax\n"); 626 error_string = talloc_asprintf_append(error_string, " syntax: crit(b)\n"); 627 error_string = talloc_asprintf_append(error_string, " note: b = boolean"); 628 ldb_set_errstring(ldb, error_string); 629 talloc_free(error_string); 630 return NULL; 631 } 632 633 ctrl[i] = talloc(ctrl, struct ldb_control); 634 if (!ctrl[i]) { 635 ldb_oom(ldb); 636 return NULL; 637 } 638 ctrl[i]->oid = LDB_CONTROL_PERMISSIVE_MODIFY_OID; 639 ctrl[i]->critical = crit; 640 ctrl[i]->data = NULL; 641 642 continue; 643 } 644 645 /* no controls matched, throw an error */ 646 ldb_asprintf_errstring(ldb, "Invalid control name: '%s'", control_strings[i]); 647 return NULL; 648 } 649 650 ctrl[i] = NULL; 651 652 return ctrl; 653} 654 655 656