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
usage(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
main(int argc,char ** argv)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
process(void * arg)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
options_callback(int option,char * optarg)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
process_ldif_rec(LDAP * ld,char * rbuf)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