1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * The contents of this file are subject to the Netscape Public 8 * License Version 1.1 (the "License"); you may not use this file 9 * except in compliance with the License. You may obtain a copy of 10 * the License at http://www.mozilla.org/NPL/ 11 * 12 * Software distributed under the License is distributed on an "AS 13 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 14 * implied. See the License for the specific language governing 15 * rights and limitations under the License. 16 * 17 * The Original Code is Mozilla Communicator client code, released 18 * March 31, 1998. 19 * 20 * The Initial Developer of the Original Code is Netscape 21 * Communications Corporation. Portions created by Netscape are 22 * Copyright (C) 1998-1999 Netscape Communications Corporation. All 23 * Rights Reserved. 24 * 25 * Contributor(s): 26 */ 27 28 /* ldapmodify.c - generic program to modify or add entries using LDAP */ 29 30 #include "ldaptool.h" 31 #include "fileurl.h" 32 33 #ifdef SOLARIS_LDAP_CMD 34 #include <locale.h> 35 #include "ldif.h" 36 #endif /* SOLARIS_LDAP_CMD */ 37 38 #ifndef SOLARIS_LDAP_CMD 39 #define gettext(s) s 40 #endif 41 42 static int newval, contoper, force, valsfromfiles, display_binary_values; 43 static int ldif_version = -1; /* -1 => unknown version */ 44 static char *rejfile = NULL; 45 static char *bulkimport_suffix = NULL; 46 static int ldapmodify_quiet = 0; 47 48 #ifdef SOLARIS_LDAP_CMD 49 static int error = 0, replace, nbthreads = 1; 50 static int thr_create_errors = 0; 51 static pthread_mutex_t read_mutex = {0}; 52 static pthread_mutex_t wait_mutex = {0}; 53 static pthread_cond_t wait_cond = {0}; 54 #else 55 /* 56 * For Solaris, ld is defined local to process() because 57 * multiple threads restricts Solaris from using a global 58 * ld variable. 59 * Solaris uses multiple threads to create multiple 60 * ldap connections if nbthreads > 1 (i.e -l option). 61 */ 62 static LDAP *ld; 63 #endif /* SOLARIS_LDAP_CMD */ 64 65 #define LDAPMOD_MAXLINE 4096 66 67 /* strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */ 68 #define T_REPLICA_STR "replica" 69 #define T_DN_STR "dn" 70 #define T_VERSION_STR "version" 71 #define T_CHANGETYPESTR "changetype" 72 #define T_ADDCTSTR "add" 73 #define T_MODIFYCTSTR "modify" 74 #define T_DELETECTSTR "delete" 75 #define T_RENAMECTSTR "rename" /* non-standard */ 76 #define T_MODDNCTSTR "moddn" 77 #define T_MODRDNCTSTR "modrdn" 78 #define T_MODOPADDSTR "add" 79 #define T_MODOPREPLACESTR "replace" 80 #define T_MODOPDELETESTR "delete" 81 #define T_MODSEPSTR "-" 82 #define T_NEWRDNSTR "newrdn" 83 #define T_NEWSUPERIORSTR "newsuperior" 84 #define T_NEWPARENTSTR "newparent" 85 #define T_DELETEOLDRDNSTR "deleteoldrdn" 86 #define T_NEWSUPERIORSTR "newsuperior" 87 #define T_NEWPARENTSTR "newparent" /* non-standard */ 88 89 /* bulk import */ 90 #define BULKIMPORT_START_OID "2.16.840.1.113730.3.5.7" 91 #define BULKIMPORT_STOP_OID "2.16.840.1.113730.3.5.8" 92 93 static int process( void *arg ); 94 static void options_callback( int option, char *optarg ); 95 static void addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, 96 char *value, int vlen ); 97 static void freepmods( LDAPMod **pmods ); 98 static char *read_one_record( FILE *fp ); 99 static char *strdup_and_trim( char *s ); 100 101 #ifdef SOLARIS_LDAP_CMD 102 static int process_ldapmod_rec( LDAP *ld, char *rbuf ); 103 static int process_ldif_rec( LDAP *ld, char *rbuf ); 104 static int domodify( LDAP *ld, char *dn, LDAPMod **pmods, int newentry ); 105 static int dodelete( LDAP *ld, char *dn ); 106 static int dorename( LDAP *ld, char *dn, char *newrdn, char *newparent, 107 int deleteoldrdn ); 108 #else 109 static int process_ldapmod_rec( char *rbuf ); 110 static int process_ldif_rec( char *rbuf ); 111 static int domodify( char *dn, LDAPMod **pmods, int newentry ); 112 static int dodelete( char *dn ); 113 static int dorename( char *dn, char *newrdn, char *newparent, 114 int deleteoldrdn ); 115 #endif /* SOLARIS_LDAP_CMD */ 116 117 static void 118 usage( void ) 119 { 120 fprintf( stderr, gettext("usage: %s [options]\n"), ldaptool_progname ); 121 fprintf( stderr, gettext("options:\n") ); 122 ldaptool_common_usage( 0 ); 123 fprintf( stderr, gettext(" -c\t\tcontinuous mode (do not stop on errors)\n") ); 124 fprintf( stderr, gettext(" -A\t\tdisplay non-ASCII values in conjunction with -v\n") ); 125 fprintf( stderr, gettext(" -f file\tread modifications from file (default: standard input)\n") ); 126 if ( strcmp( ldaptool_progname, "ldapmodify" ) == 0 ){ 127 fprintf( stderr, gettext(" -a\t\tadd entries\n") ); 128 } 129 fprintf( stderr, gettext(" -b\t\tread values that start with / from files (for bin attrs)\n") ); 130 fprintf( stderr, gettext(" -F\t\tforce application of all changes, regardless of\n") ); 131 fprintf( stderr, gettext(" \t\treplica lines\n") ); 132 fprintf( stderr, gettext(" -e rejfile\tsave rejected entries in \"rejfile\"\n") ); 133 fprintf( stderr, gettext(" -B suffix\tbulk import to \"suffix\"\n")); 134 fprintf( stderr, gettext(" -q\t\tbe quiet when adding/modifying entries\n") ); 135 #ifdef SOLARIS_LDAP_CMD 136 fprintf( stderr, gettext(" -r\t\treplace values\n")); 137 fprintf( stderr, gettext(" -l nb-connections\tnumber of LDAP connections\n")); 138 #endif /* SOLARIS_LDAP_CMD */ 139 exit( LDAP_PARAM_ERROR ); 140 } 141 142 143 int 144 main( int argc, char **argv ) 145 { 146 int optind, i; 147 int rc; 148 149 #ifdef SOLARIS_LDAP_CMD 150 char *locale = setlocale(LC_ALL, ""); 151 textdomain(TEXT_DOMAIN); 152 #endif 153 154 #ifdef notdef 155 #ifdef HPUX11 156 #ifndef __LP64__ 157 _main( argc, argv); 158 #endif /* __LP64_ */ 159 #endif /* HPUX11 */ 160 #endif 161 162 valsfromfiles = display_binary_values = 0; 163 164 #ifdef SOLARIS_LDAP_CMD 165 optind = ldaptool_process_args( argc, argv, "aAbcFe:B:qrl:", 0, 166 options_callback ); 167 #else 168 optind = ldaptool_process_args( argc, argv, "aAbcFe:B:q", 0, 169 options_callback ); 170 #endif /* SOLARIS_LDAP_CMD */ 171 172 173 if ( optind == -1 ) { 174 usage(); 175 } 176 177 if ( !newval && strcmp( ldaptool_progname, "ldapadd" ) == 0 ) { 178 newval = 1; 179 } 180 181 if ( ldaptool_fp == NULL ) { 182 ldaptool_fp = stdin; 183 } 184 185 if ( argc - optind != 0 ) { 186 usage(); 187 } 188 189 #ifdef SOLARIS_LDAP_CMD 190 /* trivial case */ 191 if ( nbthreads == 1 ) { 192 rc = process(NULL); 193 /* check for and report output error */ 194 fflush( stdout ); 195 rc = ldaptool_check_ferror( stdout, rc, 196 gettext("output error (output might be incomplete)") ); 197 return( rc ); 198 } 199 200 for ( i=0; i<nbthreads; ++i ) { 201 if ( thr_create(NULL, 0, process, NULL, NULL, NULL) != 0 ) 202 ++thr_create_errors; 203 } 204 205 if ( thr_create_errors < nbthreads ) 206 while ( thr_join(0, NULL, NULL) == 0 ); 207 else 208 error = -1; 209 rc = error; 210 /* check for and report output error */ 211 fflush( stdout ); 212 rc = ldaptool_check_ferror( stdout, rc, 213 gettext("output error (output might be incomplete)") ); 214 return( rc ); 215 #else 216 rc = process(NULL); 217 /* check for and report output error */ 218 fflush( stdout ); 219 rc = ldaptool_check_ferror( stdout, rc, 220 gettext("output error (output might be incomplete)") ); 221 return( rc ); 222 #endif /* SOLARIS_LDAP_CMD */ 223 } 224 225 #ifdef SOLARIS_LDAP_CMD 226 #define exit(a) \ 227 if (nbthreads > 1) { \ 228 mutex_lock(&read_mutex); \ 229 error |= a; \ 230 mutex_unlock(&read_mutex); \ 231 thr_exit(&error); \ 232 } else { \ 233 exit(a); \ 234 } 235 #endif /* SOLARIS_LDAP_CMD */ 236 237 static int 238 process( void *arg ) 239 { 240 char *rbuf, *saved_rbuf, *start, *p, *q; 241 FILE *rfp = NULL; 242 int rc, use_ldif, deref; 243 LDAPControl *ldctrl; 244 245 #ifdef SOLARIS_LDAP_CMD 246 LDAP *ld; 247 #endif /* SOLARIS_LDAP_CMD */ 248 249 ld = ldaptool_ldap_init( 0 ); 250 251 if ( !ldaptool_not ) { 252 deref = LDAP_DEREF_NEVER; /* this seems prudent */ 253 ldap_set_option( ld, LDAP_OPT_DEREF, &deref ); 254 } 255 256 ldaptool_bind( ld ); 257 258 if (( ldctrl = ldaptool_create_manage_dsait_control()) != NULL ) { 259 ldaptool_add_control_to_array( ldctrl, ldaptool_request_ctrls); 260 } 261 262 if ((ldctrl = ldaptool_create_proxyauth_control(ld)) !=NULL) { 263 ldaptool_add_control_to_array( ldctrl, ldaptool_request_ctrls); 264 } 265 266 rc = 0; 267 268 /* turn on bulk import?*/ 269 if (bulkimport_suffix) { 270 struct berval bv, *retdata; 271 char *retoid; 272 273 bv.bv_val = bulkimport_suffix; 274 bv.bv_len = strlen(bulkimport_suffix); 275 if ((rc = ldap_extended_operation_s(ld, 276 BULKIMPORT_START_OID, &bv, NULL, 277 NULL, &retoid, &retdata)) != 0) { 278 fprintf(stderr, gettext("Error: unable to service " 279 "extended operation request\n\t'%s' for " 280 "bulk import\n\t(error:%d:'%s')\n"), 281 BULKIMPORT_START_OID, rc, ldap_err2string(rc)); 282 return (rc); 283 } 284 if (retoid) 285 ldap_memfree(retoid); 286 if (retdata) 287 ber_bvfree(retdata); 288 } 289 290 while (( rc == 0 || contoper ) && 291 ( rbuf = read_one_record( ldaptool_fp )) != NULL ) { 292 /* 293 * we assume record is ldif/slapd.replog if the first line 294 * has a colon that appears to the left of any equal signs, OR 295 * if the first line consists entirely of digits (an entry id) 296 */ 297 use_ldif = ( p = strchr( rbuf, ':' )) != NULL && 298 ( q = strchr( rbuf, '\n' )) != NULL && p < q && 299 (( q = strchr( rbuf, '=' )) == NULL || p < q ); 300 301 start = rbuf; 302 saved_rbuf = strdup( rbuf ); 303 304 if ( !use_ldif && ( q = strchr( rbuf, '\n' )) != NULL ) { 305 for ( p = rbuf; p < q; ++p ) { 306 if ( !isdigit( *p )) { 307 break; 308 } 309 } 310 if ( p >= q ) { 311 use_ldif = 1; 312 start = q + 1; 313 } 314 } 315 316 #ifdef SOLARIS_LDAP_CMD 317 if ( use_ldif ) { 318 rc = process_ldif_rec( ld, start ); 319 } else { 320 rc = process_ldapmod_rec( ld, start ); 321 } 322 #else 323 if ( use_ldif ) { 324 rc = process_ldif_rec( start ); 325 } else { 326 rc = process_ldapmod_rec( start ); 327 } 328 #endif /* SOLARIS_LDAP_CMD */ 329 330 if ( rc != LDAP_SUCCESS && rejfile != NULL ) { 331 /* Write this record to the reject file */ 332 int newfile = 0; 333 struct stat stbuf; 334 if ( stat( rejfile, &stbuf ) < 0 ) { 335 if ( errno == ENOENT ) { 336 newfile = 1; 337 } 338 } 339 if (( rfp = ldaptool_open_file( rejfile, "a" )) == NULL ) { 340 fprintf( stderr, gettext("Cannot open error file \"%s\" - " 341 "erroneous entries will not be saved\n"), rejfile ); 342 rejfile = NULL; 343 } else { 344 if ( newfile == 0 ) { 345 fputs( "\n", rfp ); 346 } 347 fprintf( rfp, gettext("# Error: %s\n"), ldap_err2string( rc )); 348 fputs( saved_rbuf, rfp ); 349 fclose( rfp ); 350 rfp = NULL; 351 } 352 } 353 354 free( rbuf ); 355 free( saved_rbuf ); 356 } 357 ldaptool_reset_control_array( ldaptool_request_ctrls ); 358 359 /* turn off bulk import?*/ 360 if (bulkimport_suffix) { 361 struct berval bv, *retdata; 362 char *retoid; 363 364 bv.bv_val = ""; 365 bv.bv_len = 0; 366 if ((rc = ldap_extended_operation_s(ld, 367 BULKIMPORT_STOP_OID, &bv, NULL, 368 NULL, &retoid, &retdata)) != 0) { 369 370 fprintf(stderr, gettext("Error: unable to service " 371 "extended operation request\n\t '%s' for " 372 "bulk import\n\t(rc:%d:'%s')\n"), 373 BULKIMPORT_STOP_OID, rc, ldap_err2string(rc)); 374 return (rc); 375 } 376 if (retoid) 377 ldap_memfree(retoid); 378 if (retdata) 379 ber_bvfree(retdata); 380 } 381 382 #ifdef SOLARIS_LDAP_CMD 383 mutex_lock(&read_mutex); 384 #endif 385 ldaptool_cleanup( ld ); 386 #ifdef SOLARIS_LDAP_CMD 387 mutex_unlock(&read_mutex); 388 #endif 389 return( rc ); 390 } 391 392 393 static void 394 options_callback( int option, char *optarg ) 395 { 396 switch( option ) { 397 case 'a': /* add */ 398 newval = 1; 399 break; 400 case 'b': /* read values from files (for binary attributes) */ 401 valsfromfiles = 1; 402 break; 403 case 'A': /* display non-ASCII values when -v is used */ 404 display_binary_values = 1; 405 break; 406 case 'c': /* continuous operation */ 407 contoper = 1; 408 break; 409 case 'F': /* force all changes records to be used */ 410 force = 1; 411 break; 412 case 'e': 413 rejfile = strdup( optarg ); 414 break; 415 case 'B': /* bulk import option */ 416 bulkimport_suffix = strdup( optarg ); 417 break; 418 case 'q': /* quiet mode on add/modify operations */ 419 ldapmodify_quiet = 1; 420 break; 421 #ifdef SOLARIS_LDAP_CMD 422 case 'r': /* default is to replace rather than add values */ 423 replace = 1; 424 break; 425 case 'l': 426 nbthreads = atoi(optarg); 427 break; 428 #endif /* SOLARIS_LDAP_CMD */ 429 default: 430 usage(); 431 } 432 } 433 434 435 436 static int 437 #ifdef SOLARIS_LDAP_CMD 438 process_ldif_rec( LDAP *ld, char *rbuf ) 439 #else 440 process_ldif_rec( char *rbuf ) 441 #endif 442 { 443 char *line, *dn, *type, *value, *newrdn, *newparent, *p; 444 char *ctrl_oid=NULL, *ctrl_value=NULL; 445 int ctrl_criticality=1; 446 LDAPControl *ldctrl; 447 int rc, linenum, vlen, modop, replicaport; 448 int expect_modop, expect_sep, expect_chgtype_or_control, expect_newrdn; 449 int expect_deleteoldrdn, expect_newparent, rename, moddn; 450 int deleteoldrdn, saw_replica, use_record, new_entry, delete_entry; 451 int got_all, got_value; 452 LDAPMod **pmods; 453 454 new_entry = newval; 455 456 rc = got_all = saw_replica = delete_entry = expect_modop = 0; 457 expect_deleteoldrdn = expect_newrdn = expect_newparent = expect_sep = 0; 458 expect_chgtype_or_control = linenum = got_value = rename = moddn = 0; 459 deleteoldrdn = 1; 460 use_record = force; 461 pmods = NULL; 462 dn = newrdn = newparent = NULL; 463 464 #ifdef SOLARIS_LDAP_CMD 465 while ( rc == 0 && ( line = str_getline( &rbuf )) != NULL ) { 466 #else 467 while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) { 468 #endif /* SOLARIS_LDAP_CMD */ 469 ++linenum; 470 if ( expect_sep && strcasecmp( line, T_MODSEPSTR ) == 0 ) { 471 expect_sep = 0; 472 expect_modop = 1; 473 474 /*If we see a separator in the input stream, 475 but we didn't get a value from the last modify 476 then we have to fill pmods with an empty value*/ 477 if (modop == LDAP_MOD_REPLACE && !got_value){ 478 addmodifyop( &pmods, modop, value, NULL, 0); 479 } 480 481 got_value = 0; 482 continue; 483 } 484 485 #ifdef SOLARIS_LDAP_CMD 486 if ( str_parse_line( line, &type, &value, &vlen ) < 0 ) { 487 #else 488 if ( ldif_parse_line( line, &type, &value, &vlen ) < 0 ) { 489 #endif /* SOLARIS_LDAP_CMD */ 490 fprintf( stderr, gettext("%s: invalid format (line %d of entry: %s)\n"), 491 ldaptool_progname, linenum, dn == NULL ? "" : dn ); 492 fprintf( stderr, gettext("%s: line contents: (%s)\n"), 493 ldaptool_progname, line ); 494 rc = LDAP_PARAM_ERROR; 495 break; 496 } 497 498 evaluate_line: 499 if ( dn == NULL ) { 500 if ( !use_record && strcasecmp( type, T_REPLICA_STR ) == 0 ) { 501 ++saw_replica; 502 if (( p = strchr( value, ':' )) == NULL ) { 503 replicaport = LDAP_PORT; 504 } else { 505 *p++ = '\0'; 506 replicaport = atoi( p ); 507 } 508 if ( strcasecmp( value, ldaptool_host ) == 0 && 509 replicaport == ldaptool_port ) { 510 use_record = 1; 511 } 512 513 } else if ( strcasecmp( type, T_DN_STR ) == 0 ) { 514 if (( dn = strdup( value )) == NULL ) { 515 perror( "strdup" ); 516 exit( LDAP_NO_MEMORY ); 517 } 518 expect_chgtype_or_control = 1; 519 520 } else if ( strcasecmp( type, T_VERSION_STR ) == 0 ) { 521 ldif_version = atoi( value ); 522 if ( ldif_version != LDIF_VERSION_ONE ) { 523 fprintf( stderr, gettext("%s: LDIF version %d is not supported;" 524 " use version: %d\n"), ldaptool_progname, ldif_version, 525 LDIF_VERSION_ONE ); 526 exit( LDAP_PARAM_ERROR ); 527 } 528 if ( ldaptool_verbose ) { 529 printf( gettext("Processing a version %d LDIF file...\n"), 530 ldif_version ); 531 } 532 533 /* Now check if there's something left to process */ 534 /* and if not, go get the new record, else continue */ 535 if ( *rbuf == '\0' ) { 536 return( 0 ); 537 } 538 539 } else if ( !saw_replica ) { 540 printf( gettext("%s: skipping change record: no dn: line\n"), 541 ldaptool_progname ); 542 return( 0 ); 543 } 544 545 continue; /* skip all lines until we see "dn:" */ 546 } 547 548 if ( expect_chgtype_or_control ) { 549 expect_chgtype_or_control = 0; 550 if ( !use_record && saw_replica ) { 551 printf( gettext("%s: skipping change record for entry: %s\n\t(LDAP host/port does not match replica: lines)\n"), 552 ldaptool_progname, dn ); 553 free( dn ); 554 return( 0 ); 555 } 556 557 #ifndef SOLARIS_LDAP_CMD 558 if ( strcasecmp( type, "control" ) == 0 ) { 559 value = strdup_and_trim( value ); 560 if (ldaptool_parse_ctrl_arg(value, ' ', &ctrl_oid, 561 &ctrl_criticality, &ctrl_value, &vlen)) { 562 usage(); 563 } 564 ldctrl = calloc(1,sizeof(LDAPControl)); 565 if (ctrl_value) { 566 rc = ldaptool_berval_from_ldif_value( ctrl_value, vlen, 567 &(ldctrl->ldctl_value), 568 1 /* recognize file URLs */, 0 /* always try file */, 569 1 /* report errors */ ); 570 if ((rc = ldaptool_fileurlerr2ldaperr( rc )) != LDAP_SUCCESS) { 571 fprintf( stderr, gettext("Unable to parse %s\n"), ctrl_value); 572 usage(); 573 } 574 } 575 ldctrl->ldctl_oid = ctrl_oid; 576 ldctrl->ldctl_iscritical = ctrl_criticality; 577 ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls); 578 expect_chgtype_or_control = 1; 579 continue; 580 } 581 #endif /* SOLARIS_LDAP_CMD */ 582 583 if ( strcasecmp( type, T_CHANGETYPESTR ) == 0 ) { 584 value = strdup_and_trim( value ); 585 if ( strcasecmp( value, T_MODIFYCTSTR ) == 0 ) { 586 new_entry = 0; 587 expect_modop = 1; 588 } else if ( strcasecmp( value, T_ADDCTSTR ) == 0 ) { 589 new_entry = 1; 590 modop = LDAP_MOD_ADD; 591 } else if ( strcasecmp( value, T_MODRDNCTSTR ) == 0 ) { 592 expect_newrdn = 1; 593 moddn = 1; 594 } else if ( strcasecmp( value, T_MODDNCTSTR ) == 0 ) { 595 expect_newrdn = 1; 596 moddn = 1; 597 } else if ( strcasecmp( value, T_RENAMECTSTR ) == 0 ) { 598 expect_newrdn = 1; 599 rename = 1; 600 } else if ( strcasecmp( value, T_DELETECTSTR ) == 0 ) { 601 got_all = delete_entry = 1; 602 } else { 603 fprintf( stderr, 604 gettext("%s: unknown %s \"%s\" (line %d of entry: %s)\n"), 605 ldaptool_progname, T_CHANGETYPESTR, value, 606 linenum, dn ); 607 rc = LDAP_PARAM_ERROR; 608 } 609 free( value ); 610 continue; 611 } else if ( newval ) { /* missing changetype => add */ 612 new_entry = 1; 613 modop = LDAP_MOD_ADD; 614 } else { 615 /*The user MUST put in changetype: blah 616 unless adding a new entry with either -a or ldapadd*/ 617 fprintf(stderr, gettext("%s: Missing changetype operation specification.\n\tThe dn line must be followed by \"changetype: operation\"\n\t(unless ldapmodify is called with -a option)\n\twhere operation is add|delete|modify|modrdn|moddn|rename\n\t\"%s\" is not a valid changetype operation specification\n\t(line %d of entry %s)\n"), 618 ldaptool_progname, type, linenum, dn); 619 rc = LDAP_PARAM_ERROR; 620 /*expect_modop = 1; missing changetype => modify */ 621 } 622 } 623 624 if ( expect_modop ) { 625 expect_modop = 0; 626 expect_sep = 1; 627 if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) { 628 modop = LDAP_MOD_ADD; 629 continue; 630 } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) { 631 modop = LDAP_MOD_REPLACE; 632 continue; 633 } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) { 634 modop = LDAP_MOD_DELETE; 635 addmodifyop( &pmods, modop, value, NULL, 0 ); 636 continue; 637 #ifdef SOLARIS_LDAP_CMD 638 } else { /* no modify op: use default */ 639 modop = replace ? LDAP_MOD_REPLACE : LDAP_MOD_ADD; 640 } 641 #else 642 } else { /*Bug 27479. Remove default add operation*/ 643 fprintf(stderr, gettext("%s: Invalid parameter \"%s\" specified for changetype modify (line %d of entry %s)\n"), 644 ldaptool_progname, type, linenum, dn); 645 rc = LDAP_PARAM_ERROR; 646 } 647 #endif /* SOLARIS_LDAP_CMD */ 648 649 } 650 651 if ( expect_newrdn ) { 652 if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) { 653 if ( *value == '\0' ) { 654 fprintf( stderr, 655 gettext("%s: newrdn value missing (line %d of entry: %s)\n"), 656 ldaptool_progname, linenum, dn == NULL ? "" : dn ); 657 rc = LDAP_PARAM_ERROR; 658 } else if (( newrdn = strdup( value )) == NULL ) { 659 perror( "strdup" ); 660 exit( LDAP_NO_MEMORY ); 661 } else { 662 expect_newrdn = 0; 663 if ( rename ) { 664 expect_newparent = 1; 665 } else { 666 expect_deleteoldrdn = 1; 667 } 668 } 669 } else { 670 fprintf( stderr, gettext("%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n"), 671 ldaptool_progname, T_NEWRDNSTR, type, linenum, dn ); 672 rc = LDAP_PARAM_ERROR; 673 } 674 } else if ( expect_newparent ) { 675 expect_newparent = 0; 676 if ( rename ) { 677 expect_deleteoldrdn = 1; 678 } 679 if ( strcasecmp( type, T_NEWPARENTSTR ) == 0 680 || strcasecmp( type, T_NEWSUPERIORSTR ) == 0 ) { 681 if (( newparent = strdup( value )) == NULL ) { 682 perror( "strdup" ); 683 exit( LDAP_NO_MEMORY ); 684 } 685 } else { 686 /* Since this is an optional argument for rename/moddn, cause 687 * the current line to be re-evaluated if newparent doesn't 688 * follow deleteoldrdn. 689 */ 690 newparent = NULL; 691 goto evaluate_line; 692 } 693 } else if ( expect_deleteoldrdn ) { 694 if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) { 695 if ( *value == '\0' ) { 696 fprintf( stderr, 697 gettext("%s: missing 0 or 1 (line %d of entry: %s)\n"), 698 ldaptool_progname, linenum, dn == NULL ? "" : dn ); 699 rc = LDAP_PARAM_ERROR; 700 } else { 701 deleteoldrdn = ( *value == '0' ) ? 0 : 1; 702 expect_deleteoldrdn = 0; 703 if ( moddn ) { 704 expect_newparent = 1; 705 } 706 } 707 } else { 708 fprintf( stderr, gettext("%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n"), 709 ldaptool_progname, T_DELETEOLDRDNSTR, type, linenum, 710 dn ); 711 rc = LDAP_PARAM_ERROR; 712 } 713 got_all = 1; 714 } else if ( got_all ) { 715 fprintf( stderr, 716 gettext("%s: extra lines at end (line %d of entry %s)\n"), 717 ldaptool_progname, linenum, dn ); 718 rc = LDAP_PARAM_ERROR; 719 got_all = 1; 720 } else { 721 addmodifyop( &pmods, modop, type, value, vlen ); 722 /*There was a value to replace*/ 723 got_value = 1; 724 725 } 726 } 727 728 if ( rc == 0 ) { 729 if ( delete_entry ) { 730 #ifdef SOLARIS_LDAP_CMD 731 rc = dodelete( ld, dn ); 732 #else 733 rc = dodelete( dn ); 734 #endif /* SOLARIS_LDAP_CMD */ 735 } else if ( newrdn != NULL ) { 736 #ifdef SOLARIS_LDAP_CMD 737 rc = dorename( ld, dn, newrdn, newparent, deleteoldrdn ); 738 #else 739 rc = dorename( dn, newrdn, newparent, deleteoldrdn ); 740 #endif /* SOLARIS_LDAP_CMD */ 741 rename = 0; 742 } else { 743 744 /*Patch to fix Bug 22183 745 If pmods is null, then there is no 746 attribute to replace, so we alloc 747 an empty pmods*/ 748 if (modop == LDAP_MOD_REPLACE && !got_value && expect_sep){ 749 addmodifyop( &pmods, modop, value, NULL, 0); 750 }/*End Patch*/ 751 752 753 #ifdef SOLARIS_LDAP_CMD 754 rc = domodify( ld, dn, pmods, new_entry ); 755 #else 756 rc = domodify( dn, pmods, new_entry ); 757 #endif /* SOLARIS_LDAP_CMD */ 758 } 759 760 if ( rc == LDAP_SUCCESS ) { 761 rc = 0; 762 } 763 } 764 765 if ( dn != NULL ) { 766 free( dn ); 767 } 768 if ( newrdn != NULL ) { 769 free( newrdn ); 770 } 771 if ( newparent != NULL ) { 772 free( newparent ); 773 } 774 if ( pmods != NULL ) { 775 freepmods( pmods ); 776 } 777 778 return( rc ); 779 } 780 781 782 static int 783 #ifdef SOLARIS_LDAP_CMD 784 process_ldapmod_rec( LDAP *ld, char *rbuf ) 785 #else 786 process_ldapmod_rec( char *rbuf ) 787 #endif /* SOLARIS_LDAP_CMD */ 788 { 789 char *line, *dn, *p, *q, *attr, *value; 790 int rc, linenum, modop; 791 LDAPMod **pmods; 792 793 pmods = NULL; 794 dn = NULL; 795 linenum = 0; 796 line = rbuf; 797 rc = 0; 798 799 while ( rc == 0 && rbuf != NULL && *rbuf != '\0' ) { 800 ++linenum; 801 if (( p = strchr( rbuf, '\n' )) == NULL ) { 802 rbuf = NULL; 803 } else { 804 if ( *(p-1) == '\\' ) { /* lines ending in '\' are continued */ 805 strcpy( p - 1, p ); 806 rbuf = p; 807 continue; 808 } 809 *p++ = '\0'; 810 rbuf = p; 811 } 812 813 if ( dn == NULL ) { /* first line contains DN */ 814 if (( dn = strdup( line )) == NULL ) { 815 perror( "strdup" ); 816 exit( LDAP_NO_MEMORY ); 817 } 818 } else { 819 if (( p = strchr( line, '=' )) == NULL ) { 820 value = NULL; 821 p = line + strlen( line ); 822 } else { 823 *p++ = '\0'; 824 value = p; 825 } 826 827 for ( attr = line; *attr != '\0' && isspace( *attr ); ++attr ) { 828 ; /* skip attribute leading white space */ 829 } 830 831 for ( q = p - 1; q > attr && isspace( *q ); --q ) { 832 *q = '\0'; /* remove attribute trailing white space */ 833 } 834 835 if ( value != NULL ) { 836 while ( isspace( *value )) { 837 ++value; /* skip value leading white space */ 838 } 839 for ( q = value + strlen( value ) - 1; q > value && 840 isspace( *q ); --q ) { 841 *q = '\0'; /* remove value trailing white space */ 842 } 843 if ( *value == '\0' ) { 844 value = NULL; 845 } 846 847 } 848 849 if ( value == NULL && newval ) { 850 fprintf( stderr, gettext("%s: missing value on line %d (attr is %s)\n"), 851 ldaptool_progname, linenum, attr ); 852 rc = LDAP_PARAM_ERROR; 853 } else { 854 switch ( *attr ) { 855 case '-': 856 modop = LDAP_MOD_DELETE; 857 ++attr; 858 break; 859 case '+': 860 modop = LDAP_MOD_ADD; 861 ++attr; 862 break; 863 default: 864 #ifdef SOLARIS_LDAP_CMD 865 modop = replace ? LDAP_MOD_REPLACE : LDAP_MOD_ADD; 866 #else 867 /*Bug 27479. Remove the add default*/ 868 fprintf(stderr, gettext("%s: Invalid parameter specified for changetype modify (line %d of entry %s)\n"), 869 ldaptool_progname, linenum, dn); 870 rc = LDAP_PARAM_ERROR; 871 #endif /* SOLARIS_LDAP_CMD */ 872 } 873 874 addmodifyop( &pmods, modop, attr, value, 875 ( value == NULL ) ? 0 : strlen( value )); 876 } 877 } 878 879 line = rbuf; 880 } 881 882 if ( rc == 0 ) { 883 if ( dn == NULL ) { 884 rc = LDAP_PARAM_ERROR; 885 #ifdef SOLARIS_LDAP_CMD 886 } else if (( rc = domodify( ld, dn, pmods, newval )) == LDAP_SUCCESS ){ 887 #else 888 } else if (( rc = domodify( dn, pmods, newval )) == LDAP_SUCCESS ){ 889 #endif /* SOLARIS_LDAP_CMD */ 890 rc = 0; 891 } 892 } 893 894 if ( pmods != NULL ) { 895 freepmods( pmods ); 896 } 897 if ( dn != NULL ) { 898 free( dn ); 899 } 900 901 return( rc ); 902 } 903 904 905 static void 906 addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen ) 907 { 908 LDAPMod **pmods; 909 int i, j, rc; 910 struct berval *bvp; 911 912 pmods = *pmodsp; 913 modop |= LDAP_MOD_BVALUES; 914 915 i = 0; 916 if ( pmods != NULL ) { 917 for ( ; pmods[ i ] != NULL; ++i ) { 918 if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 && 919 pmods[ i ]->mod_op == modop ) { 920 break; 921 } 922 } 923 } 924 925 if ( pmods == NULL || pmods[ i ] == NULL ) { 926 if (( pmods = (LDAPMod **)LDAPTOOL_SAFEREALLOC( pmods, (i + 2) * 927 sizeof( LDAPMod * ))) == NULL ) { 928 perror( "realloc" ); 929 exit( LDAP_NO_MEMORY ); 930 } 931 *pmodsp = pmods; 932 pmods[ i + 1 ] = NULL; 933 if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod ))) 934 == NULL ) { 935 perror( "calloc" ); 936 exit( LDAP_NO_MEMORY ); 937 } 938 pmods[ i ]->mod_op = modop; 939 if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) { 940 perror( "strdup" ); 941 exit( LDAP_NO_MEMORY ); 942 } 943 } 944 945 if ( value != NULL ) { 946 j = 0; 947 if ( pmods[ i ]->mod_bvalues != NULL ) { 948 for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) { 949 ; 950 } 951 } 952 if (( pmods[ i ]->mod_bvalues = (struct berval **) 953 LDAPTOOL_SAFEREALLOC( pmods[ i ]->mod_bvalues, 954 (j + 2) * sizeof( struct berval * ))) == NULL ) { 955 perror( "realloc" ); 956 exit( LDAP_NO_MEMORY ); 957 } 958 pmods[ i ]->mod_bvalues[ j + 1 ] = NULL; 959 if (( bvp = (struct berval *)malloc( sizeof( struct berval ))) 960 == NULL ) { 961 perror( "malloc" ); 962 exit( LDAP_NO_MEMORY ); 963 } 964 pmods[ i ]->mod_bvalues[ j ] = bvp; 965 966 #ifdef notdef 967 if (ldaptool_verbose) { 968 printf(gettext("%s: value: %s vlen: %d\n"), "ldapmodify", value, vlen); 969 } 970 #endif 971 rc = ldaptool_berval_from_ldif_value( value, vlen, bvp, 972 ( ldif_version >= LDIF_VERSION_ONE ), valsfromfiles, 973 1 /* report errors */ ); 974 if ( rc != LDAPTOOL_FILEURL_SUCCESS ) { 975 exit( ldaptool_fileurlerr2ldaperr( rc )); 976 } 977 } 978 } 979 980 981 static int 982 #ifdef SOLARIS_LDAP_CMD 983 domodify( LDAP *ld, char *dn, LDAPMod **pmods, int newentry ) 984 #else 985 domodify( char *dn, LDAPMod **pmods, int newentry ) 986 #endif /* SOLARIS_LDAP_CMD */ 987 { 988 int i, j, notascii, op; 989 struct berval *bvp; 990 991 if ( pmods == NULL ) { 992 fprintf( stderr, gettext("%s: no attributes to change or add (entry %s)\n"), 993 ldaptool_progname, dn ); 994 return( LDAP_PARAM_ERROR ); 995 } 996 997 if ( ldaptool_verbose ) { 998 for ( i = 0; pmods[ i ] != NULL; ++i ) { 999 op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES; 1000 printf( gettext("%s %s:\n"), op == LDAP_MOD_REPLACE ? 1001 gettext("replace") : op == LDAP_MOD_ADD ? 1002 gettext("add") : gettext("delete"), pmods[ i ]->mod_type ); 1003 if ( pmods[ i ]->mod_bvalues != NULL ) { 1004 for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) { 1005 bvp = pmods[ i ]->mod_bvalues[ j ]; 1006 notascii = 0; 1007 if ( !display_binary_values ) { 1008 notascii = !ldaptool_berval_is_ascii( bvp ); 1009 } 1010 if ( notascii ) { 1011 printf( gettext("\tNOT ASCII (%ld bytes)\n"), bvp->bv_len ); 1012 } else { 1013 printf( "\t%s\n", bvp->bv_val ); 1014 } 1015 } 1016 } 1017 } 1018 } 1019 1020 if ( !ldapmodify_quiet) { 1021 if ( newentry ) { 1022 printf( gettext("%sadding new entry %s\n"), 1023 ldaptool_not ? "!" : "", dn ); 1024 } else { 1025 printf( gettext("%smodifying entry %s\n"), 1026 ldaptool_not ? "!" : "", dn ); 1027 } 1028 } 1029 1030 if ( !ldaptool_not ) { 1031 if ( newentry ) { 1032 unsigned int sleep_interval = 2; /* seconds */ 1033 1034 #ifdef SOLARIS_LDAP_CMD 1035 /* Backward compatibility with old Solaris command */ 1036 unsigned int nb = 0; 1037 timestruc_t to; 1038 while ((i = ldaptool_add_ext_s( ld, dn, pmods, 1039 ldaptool_request_ctrls, NULL, "ldap_add" )) 1040 != LDAP_SUCCESS) { 1041 if (i == LDAP_BUSY) { 1042 if ( sleep_interval > 3600 ) { 1043 printf(gettext("ldap_add: Unable to complete " 1044 "request. Server is too " 1045 "busy servicing other " 1046 "requests\n")); 1047 break; 1048 } 1049 if ( !ldapmodify_quiet ) { 1050 printf(gettext("ldap_add: LDAP_BUSY returned " 1051 "by server. Will retry " 1052 "operation in %d seconds\n"), 1053 sleep_interval); 1054 } 1055 sleep( sleep_interval ); 1056 sleep_interval *= 2; 1057 } else if (i == LDAP_NO_SUCH_OBJECT) { 1058 /* 1059 * Wait for the parent entry to be created by 1060 * another thread. Do not retry more than the 1061 * number of threads. 1062 */ 1063 ++nb; 1064 if (nb >= nbthreads) 1065 break; 1066 mutex_lock(&wait_mutex); 1067 to.tv_sec = 5; 1068 to.tv_nsec = 0; 1069 if (cond_reltimedwait(&wait_cond, &wait_mutex, &to) 1070 == ETIME) { 1071 nb = nbthreads; /* last chance */ 1072 } 1073 mutex_unlock(&wait_mutex); 1074 } else { 1075 break; 1076 } 1077 } 1078 cond_broadcast(&wait_cond); 1079 #else 1080 while ((i = ldaptool_add_ext_s( ld, dn, pmods, 1081 ldaptool_request_ctrls, NULL, "ldap_add" )) 1082 == LDAP_BUSY) { 1083 if ( sleep_interval > 3600 ) { 1084 printf("ldap_add: Unable to complete request. "); 1085 printf("Server is too "); 1086 printf("busy servicing other requests\n"); 1087 break; 1088 } 1089 if ( !ldapmodify_quiet ) { 1090 printf("ldap_add: LDAP_BUSY returned by server. "); 1091 printf("Will retry operation "); 1092 printf("in %d seconds\n", sleep_interval); 1093 } 1094 sleep( sleep_interval ); 1095 sleep_interval *= 2; 1096 } 1097 #endif /* SOLARIS_LDAP_CMD */ 1098 } else { 1099 i = ldaptool_modify_ext_s( ld, dn, pmods, ldaptool_request_ctrls, 1100 NULL, "ldap_modify" ); 1101 } 1102 if ( i == LDAP_SUCCESS && ldaptool_verbose ) { 1103 printf( gettext("modify complete\n") ); 1104 } 1105 } else { 1106 i = LDAP_SUCCESS; 1107 } 1108 1109 if ( !ldapmodify_quiet) { 1110 putchar( '\n' ); 1111 } 1112 1113 return( i ); 1114 } 1115 1116 1117 static int 1118 #ifdef SOLARIS_LDAP_CMD 1119 dodelete( LDAP *ld, char *dn ) 1120 #else 1121 dodelete( char *dn ) 1122 #endif /* SOLARIS_LDAP_CMD */ 1123 { 1124 int rc; 1125 1126 printf( gettext("%sdeleting entry %s\n"), ldaptool_not ? "!" : "", dn ); 1127 if ( !ldaptool_not ) { 1128 if (( rc = ldaptool_delete_ext_s( ld, dn, ldaptool_request_ctrls, 1129 NULL, "ldap_delete" )) == LDAP_SUCCESS && ldaptool_verbose ) { 1130 printf( gettext("delete complete") ); 1131 } 1132 } else { 1133 rc = LDAP_SUCCESS; 1134 } 1135 1136 putchar( '\n' ); 1137 1138 return( rc ); 1139 } 1140 1141 1142 static int 1143 #ifdef SOLARIS_LDAP_CMD 1144 dorename( LDAP *ld, char *dn, char *newrdn, char *newparent, int deleteoldrdn ) 1145 #else 1146 dorename( char *dn, char *newrdn, char *newparent, int deleteoldrdn ) 1147 #endif /* SOLARIS_LDAP_CMD */ 1148 { 1149 int rc; 1150 1151 if ( ldaptool_verbose ) { 1152 if ( newparent == NULL ) { 1153 printf(deleteoldrdn ? 1154 gettext("new RDN: %s (do not keep existing values)\n"): 1155 gettext("new RDN: %s (keep existing values)\n")); 1156 } else { 1157 printf(deleteoldrdn ? 1158 gettext("new RDN: %s, new parent %s ( do not keep existing values)\n"): 1159 gettext("new RDN: %s, new parent %s ( keep existing values)\n")); 1160 } 1161 } 1162 1163 printf( gettext("%smodifying RDN of entry %s%s\n"), 1164 ldaptool_not ? "!" : "", dn, ( newparent == NULL ) ? "" : 1165 gettext(" and/or moving it beneath a new parent\n") ); 1166 1167 if ( !ldaptool_not ) { 1168 if (( rc = ldaptool_rename_s( ld, dn, newrdn, newparent, deleteoldrdn, 1169 ldaptool_request_ctrls, NULL, "ldap_rename" )) == LDAP_SUCCESS 1170 && ldaptool_verbose ) { 1171 printf( gettext("rename completed\n") ); 1172 } 1173 } else { 1174 rc = LDAP_SUCCESS; 1175 } 1176 1177 putchar( '\n' ); 1178 1179 return( rc ); 1180 } 1181 1182 1183 static void 1184 freepmods( LDAPMod **pmods ) 1185 { 1186 int i; 1187 1188 for ( i = 0; pmods[ i ] != NULL; ++i ) { 1189 if ( pmods[ i ]->mod_bvalues != NULL ) { 1190 ber_bvecfree( pmods[ i ]->mod_bvalues ); 1191 } 1192 if ( pmods[ i ]->mod_type != NULL ) { 1193 free( pmods[ i ]->mod_type ); 1194 } 1195 free( pmods[ i ] ); 1196 } 1197 free( pmods ); 1198 } 1199 1200 1201 static char * 1202 read_one_record( FILE *fp ) 1203 { 1204 int len, gotnothing; 1205 char *buf, line[ LDAPMOD_MAXLINE ]; 1206 int lcur, lmax; 1207 1208 lcur = lmax = 0; 1209 buf = NULL; 1210 gotnothing = 1; 1211 1212 #ifdef SOLARIS_LDAP_CMD 1213 mutex_lock(&read_mutex); 1214 1215 if (fp == NULL) { 1216 mutex_unlock(&read_mutex); 1217 return(NULL); 1218 } 1219 #endif 1220 1221 while ( fgets( line, sizeof(line), fp ) != NULL ) { 1222 if ( (len = strlen( line )) < 2 ) { 1223 if ( gotnothing ) { 1224 continue; 1225 } else { 1226 break; 1227 } 1228 } 1229 1230 /* Check if the blank line starts with '\r' (CR) */ 1231 if ( ((len = strlen( line )) == 2) && (line[0] == '\r') ) { 1232 if ( gotnothing ) { 1233 continue; 1234 } else { 1235 break; 1236 } 1237 } 1238 1239 if ( *line == '#' ) { 1240 continue; /* skip comment lines */ 1241 } 1242 1243 gotnothing = 0; 1244 if ( lcur + len + 1 > lmax ) { 1245 lmax = LDAPMOD_MAXLINE 1246 * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 ); 1247 if (( buf = (char *)LDAPTOOL_SAFEREALLOC( buf, lmax )) == NULL ) { 1248 perror( "realloc" ); 1249 #ifdef SOLARIS_LDAP_CMD 1250 mutex_unlock(&read_mutex); 1251 #endif 1252 exit( LDAP_NO_MEMORY ); 1253 } 1254 } 1255 strcpy( buf + lcur, line ); 1256 lcur += len; 1257 } 1258 1259 #ifdef SOLARIS_LDAP_CMD 1260 mutex_unlock(&read_mutex); 1261 #endif 1262 1263 return( buf ); 1264 } 1265 1266 1267 /* 1268 * strdup and trim trailing blanks 1269 */ 1270 static char * 1271 strdup_and_trim( char *s ) 1272 { 1273 char *p; 1274 1275 if (( s = strdup( s )) == NULL ) { 1276 perror( "strdup" ); 1277 exit( LDAP_NO_MEMORY ); 1278 } 1279 1280 p = s + strlen( s ) - 1; 1281 while ( p >= s && isspace( *p )) { 1282 --p; 1283 } 1284 *++p = '\0'; 1285 1286 return( s ); 1287 } 1288