]> git.sur5r.net Git - openldap/blob - clients/tools/ldapmodify.c
d75f0627dc88ee3e6bfbcc11f66261ff421ed357
[openldap] / clients / tools / ldapmodify.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /* ldapmodify.c - generic program to modify or add entries using LDAP */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/stdlib.h>
13
14 #include <ac/ctype.h>
15 #include <ac/signal.h>
16 #include <ac/string.h>
17 #include <ac/unistd.h>
18
19 #ifdef HAVE_SYS_STAT_H
20 #include <sys/stat.h>
21 #endif
22
23 #ifdef HAVE_SYS_FILE_H
24 #include <sys/file.h>
25 #endif
26 #ifdef HAVE_FCNTL_H
27 #include <fcntl.h>
28 #endif
29
30 #include <ldap.h>
31
32 #include "lutil_ldap.h"
33 #include "ldif.h"
34 #include "ldap_defaults.h"
35
36 static char     *prog;
37 static char     *binddn = NULL;
38 static struct berval passwd = { 0, NULL };
39 static char *ldapuri = NULL;
40 static char     *ldaphost = NULL;
41 static int      ldapport = 0;
42 #ifdef HAVE_CYRUS_SASL
43 static unsigned sasl_flags = LDAP_SASL_AUTOMATIC;
44 static char *sasl_realm = NULL;
45 static char     *sasl_authc_id = NULL;
46 static char     *sasl_authz_id = NULL;
47 static char     *sasl_mech = NULL;
48 static char     *sasl_secprops = NULL;
49 #endif
50 static int      use_tls = 0;
51 static int      ldapadd, not, verbose, contoper, force;
52 static LDAP     *ld = NULL;
53
54 #define LDAPMOD_MAXLINE         4096
55
56 /* strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */
57 #define T_VERSION_STR           "version"
58 #define T_REPLICA_STR           "replica"
59 #define T_DN_STR                "dn"
60 #define T_CHANGETYPESTR         "changetype"
61 #define T_ADDCTSTR              "add"
62 #define T_MODIFYCTSTR           "modify"
63 #define T_DELETECTSTR           "delete"
64 #define T_MODRDNCTSTR           "modrdn"
65 #define T_MODDNCTSTR            "moddn"
66 #define T_RENAMECTSTR           "rename"
67 #define T_MODOPADDSTR           "add"
68 #define T_MODOPREPLACESTR       "replace"
69 #define T_MODOPDELETESTR        "delete"
70 #define T_MODSEPSTR             "-"
71 #define T_NEWRDNSTR             "newrdn"
72 #define T_DELETEOLDRDNSTR       "deleteoldrdn"
73 #define T_NEWSUPSTR             "newsuperior"
74
75
76 static void usage LDAP_P(( const char *prog )) LDAP_GCCATTR((noreturn));
77 static int process_ldif_rec LDAP_P(( char *rbuf, int count ));
78 static void addmodifyop LDAP_P((
79         LDAPMod ***pmodsp, int modop,
80         const char *attr,
81         struct berval *value ));
82 static int domodify LDAP_P((
83         const char *dn,
84         LDAPMod **pmods,
85         int newentry ));
86 static int dodelete LDAP_P((
87         const char *dn ));
88 static int dorename LDAP_P((
89         const char *dn,
90         const char *newrdn,
91         const char *newsup,
92         int deleteoldrdn ));
93 static char *read_one_record LDAP_P(( FILE *fp ));
94
95 static void
96 usage( const char *prog )
97 {
98     fprintf( stderr,
99 "Add or modify entries from an LDAP server\n\n"
100 "usage: %s [options]\n"
101 "       The list of desired operations are read from stdin or from the file\n"
102 "       specified by \"-f file\".\n"
103 "Add or modify options:\n"
104 "  -a         add values (default%s)\n"
105 "  -F         force all changes records to be used\n"
106
107 "Common options:\n"
108 "  -d level   set LDAP debugging level to `level'\n"
109 "  -D binddn  bind DN\n"
110 "  -f file    read operations from `file'\n"
111 "  -h host    LDAP server\n"
112 "  -H URI     LDAP Uniform Resource Indentifier(s)\n"
113 "  -I         use SASL Interactive mode\n"
114 "  -k         use Kerberos authentication\n"
115 "  -K         like -k, but do only step 1 of the Kerberos bind\n"
116 "  -M         enable Manage DSA IT control (-MM to make critical)\n"
117 "  -n         show what would be done but don't actually search\n"
118 "  -O props   SASL security properties\n"
119 "  -p port    port on LDAP server\n"
120 "  -P version procotol version (default: 3)\n"
121 "  -Q         use SASL Quiet mode\n"
122 "  -R realm   SASL realm\n"
123 "  -U authcid SASL authentication identity\n"
124 "  -v         run in verbose mode (diagnostics to standard output)\n"
125 "  -w passwd  bind passwd (for simple authentication)\n"
126 "  -W         prompt for bind passwd\n"
127 "  -x         Simple authentication\n"
128 "  -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"
129 "  -Y mech    SASL mechanism\n"
130 "  -Z         Start TLS request (-ZZ to require successful response)\n"
131              , prog, (strcmp( prog, "ldapadd" ) ? " is to replace" : "") );
132
133     exit( EXIT_FAILURE );
134 }
135
136
137 int
138 main( int argc, char **argv )
139 {
140     char                *infile, *rbuf, *start;
141     FILE                *fp;
142         int             rc, i, authmethod, version, want_bindpw, debug, manageDSAit, referrals;
143         int count;
144
145     if (( prog = strrchr( argv[ 0 ], *LDAP_DIRSEP )) == NULL ) {
146         prog = argv[ 0 ];
147     } else {
148         ++prog;
149     }
150
151     /* Print usage when no parameters */
152     if( argc < 2 ) usage( prog );
153
154     ldapadd = ( strcmp( prog, "ldapadd" ) == 0 );
155
156     infile = NULL;
157     not = verbose = want_bindpw = debug = manageDSAit = referrals = 0;
158     authmethod = -1;
159         version = -1;
160
161     while (( i = getopt( argc, argv, "acrf:F"
162                 "Cd:D:h:H:IkKMnO:p:P:QR:U:vw:WxX:Y:Z" )) != EOF )
163         {
164         switch( i ) {
165         /* Modify Options */
166         case 'a':       /* add */
167             ldapadd = 1;
168             break;
169         case 'c':       /* continuous operation */
170             contoper = 1;
171             break;
172         case 'f':       /* read from file */
173                 if( infile != NULL ) {
174                         fprintf( stderr, "%s: -f previously specified\n", prog );
175                         return EXIT_FAILURE;
176                 }
177             infile = strdup( optarg );
178             break;
179         case 'F':       /* force all changes records to be used */
180             force = 1;
181             break;
182
183         /* Common Options */
184         case 'C':
185                 referrals++;
186                 break;
187         case 'd':
188             debug |= atoi( optarg );
189             break;
190         case 'D':       /* bind DN */
191                 if( binddn != NULL ) {
192                         fprintf( stderr, "%s: -D previously specified\n", prog );
193                         return EXIT_FAILURE;
194                 }
195             binddn = strdup( optarg );
196             break;
197         case 'h':       /* ldap host */
198                 if( ldapuri != NULL ) {
199                         fprintf( stderr, "%s: -h incompatible with -H\n", prog );
200                         return EXIT_FAILURE;
201                 }
202                 if( ldaphost != NULL ) {
203                         fprintf( stderr, "%s: -h previously specified\n", prog );
204                         return EXIT_FAILURE;
205                 }
206             ldaphost = strdup( optarg );
207             break;
208         case 'H':       /* ldap URI */
209                 if( ldaphost != NULL ) {
210                         fprintf( stderr, "%s: -H incompatible with -h\n", prog );
211                         return EXIT_FAILURE;
212                 }
213                 if( ldapport ) {
214                         fprintf( stderr, "%s: -H incompatible with -p\n", prog );
215                         return EXIT_FAILURE;
216                 }
217                 if( ldapuri != NULL ) {
218                         fprintf( stderr, "%s: -H previously specified\n", prog );
219                         return EXIT_FAILURE;
220                 }
221             ldapuri = strdup( optarg );
222             break;
223         case 'I':
224 #ifdef HAVE_CYRUS_SASL
225                 if( version == LDAP_VERSION2 ) {
226                         fprintf( stderr, "%s: -I incompatible with version %d\n",
227                                 prog, version );
228                         return EXIT_FAILURE;
229                 }
230                 if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
231                         fprintf( stderr, "%s: incompatible previous "
232                                 "authentication choice\n",
233                                 prog );
234                         return EXIT_FAILURE;
235                 }
236                 authmethod = LDAP_AUTH_SASL;
237                 version = LDAP_VERSION3;
238                 sasl_flags = LDAP_SASL_INTERACTIVE;
239                 break;
240 #else
241                 fprintf( stderr, "%s: was not compiled with SASL support\n",
242                         prog );
243                 return( EXIT_FAILURE );
244 #endif
245         case 'k':       /* kerberos bind */
246 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
247                 if( version > LDAP_VERSION2 ) {
248                         fprintf( stderr, "%s: -k incompatible with LDAPv%d\n",
249                                 prog, version );
250                         return EXIT_FAILURE;
251                 }
252
253                 if( authmethod != -1 ) {
254                         fprintf( stderr, "%s: -k incompatible with previous "
255                                 "authentication choice\n", prog );
256                         return EXIT_FAILURE;
257                 }
258                         
259                 authmethod = LDAP_AUTH_KRBV4;
260 #else
261                 fprintf( stderr, "%s: not compiled with Kerberos support\n", prog );
262                 return EXIT_FAILURE;
263 #endif
264             break;
265         case 'K':       /* kerberos bind, part one only */
266 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
267                 if( version > LDAP_VERSION2 ) {
268                         fprintf( stderr, "%s: -k incompatible with LDAPv%d\n",
269                                 prog, version );
270                         return EXIT_FAILURE;
271                 }
272                 if( authmethod != -1 ) {
273                         fprintf( stderr, "%s: incompatible with previous "
274                                 "authentication choice\n", prog );
275                         return EXIT_FAILURE;
276                 }
277
278                 authmethod = LDAP_AUTH_KRBV41;
279 #else
280                 fprintf( stderr, "%s: not compiled with Kerberos support\n", prog );
281                 return( EXIT_FAILURE );
282 #endif
283             break;
284         case 'M':
285                 /* enable Manage DSA IT */
286                 if( version == LDAP_VERSION2 ) {
287                         fprintf( stderr, "%s: -M incompatible with LDAPv%d\n",
288                                 prog, version );
289                         return EXIT_FAILURE;
290                 }
291                 manageDSAit++;
292                 version = LDAP_VERSION3;
293                 break;
294         case 'n':       /* print deletes, don't actually do them */
295             ++not;
296             break;
297         case 'O':
298 #ifdef HAVE_CYRUS_SASL
299                 if( sasl_secprops != NULL ) {
300                         fprintf( stderr, "%s: -O previously specified\n", prog );
301                         return EXIT_FAILURE;
302                 }
303                 if( version == LDAP_VERSION2 ) {
304                         fprintf( stderr, "%s: -O incompatible with LDAPv%d\n",
305                                 prog, version );
306                         return EXIT_FAILURE;
307                 }
308                 if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
309                         fprintf( stderr, "%s: incompatible previous "
310                                 "authentication choice\n", prog );
311                         return EXIT_FAILURE;
312                 }
313                 authmethod = LDAP_AUTH_SASL;
314                 version = LDAP_VERSION3;
315                 sasl_secprops = strdup( optarg );
316 #else
317                 fprintf( stderr, "%s: not compiled with SASL support\n",
318                         prog );
319                 return( EXIT_FAILURE );
320 #endif
321                 break;
322         case 'p':
323                 if( ldapport ) {
324                         fprintf( stderr, "%s: -p previously specified\n", prog );
325                         return EXIT_FAILURE;
326                 }
327             ldapport = atoi( optarg );
328             break;
329         case 'P':
330                 switch( atoi(optarg) ) {
331                 case 2:
332                         if( version == LDAP_VERSION3 ) {
333                                 fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
334                                         prog, version );
335                                 return EXIT_FAILURE;
336                         }
337                         version = LDAP_VERSION2;
338                         break;
339                 case 3:
340                         if( version == LDAP_VERSION2 ) {
341                                 fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
342                                         prog, version );
343                                 return EXIT_FAILURE;
344                         }
345                         version = LDAP_VERSION3;
346                         break;
347                 default:
348                         fprintf( stderr, "%s: protocol version should be 2 or 3\n",
349                                 prog );
350                         usage( prog );
351                         return( EXIT_FAILURE );
352                 } break;
353         case 'Q':
354 #ifdef HAVE_CYRUS_SASL
355                 if( version == LDAP_VERSION2 ) {
356                         fprintf( stderr, "%s: -Q incompatible with version %d\n",
357                                 prog, version );
358                         return EXIT_FAILURE;
359                 }
360                 if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
361                         fprintf( stderr, "%s: incompatible previous "
362                                 "authentication choice\n",
363                                 prog );
364                         return EXIT_FAILURE;
365                 }
366                 authmethod = LDAP_AUTH_SASL;
367                 version = LDAP_VERSION3;
368                 sasl_flags = LDAP_SASL_QUIET;
369                 break;
370 #else
371                 fprintf( stderr, "%s: not compiled with SASL support\n",
372                         prog );
373                 return( EXIT_FAILURE );
374 #endif
375         case 'r':       /* replace (obsolete) */
376                 break;
377
378         case 'R':
379 #ifdef HAVE_CYRUS_SASL
380                 if( sasl_realm != NULL ) {
381                         fprintf( stderr, "%s: -R previously specified\n", prog );
382                         return EXIT_FAILURE;
383                 }
384                 if( version == LDAP_VERSION2 ) {
385                         fprintf( stderr, "%s: -R incompatible with version %d\n",
386                                 prog, version );
387                         return EXIT_FAILURE;
388                 }
389                 if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
390                         fprintf( stderr, "%s: incompatible previous "
391                                 "authentication choice\n",
392                                 prog );
393                         return EXIT_FAILURE;
394                 }
395                 authmethod = LDAP_AUTH_SASL;
396                 version = LDAP_VERSION3;
397                 sasl_realm = strdup( optarg );
398 #else
399                 fprintf( stderr, "%s: not compiled with SASL support\n",
400                         prog );
401                 return( EXIT_FAILURE );
402 #endif
403                 break;
404         case 'U':
405 #ifdef HAVE_CYRUS_SASL
406                 if( sasl_authc_id != NULL ) {
407                         fprintf( stderr, "%s: -U previously specified\n", prog );
408                         return EXIT_FAILURE;
409                 }
410                 if( version == LDAP_VERSION2 ) {
411                         fprintf( stderr, "%s: -U incompatible with version %d\n",
412                                 prog, version );
413                         return EXIT_FAILURE;
414                 }
415                 if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
416                         fprintf( stderr, "%s: incompatible previous "
417                                 "authentication choice\n",
418                                 prog );
419                         return EXIT_FAILURE;
420                 }
421                 authmethod = LDAP_AUTH_SASL;
422                 version = LDAP_VERSION3;
423                 sasl_authc_id = strdup( optarg );
424 #else
425                 fprintf( stderr, "%s: not compiled with SASL support\n",
426                         prog );
427                 return( EXIT_FAILURE );
428 #endif
429                 break;
430         case 'v':       /* verbose mode */
431             verbose++;
432             break;
433         case 'w':       /* password */
434             passwd.bv_val = strdup( optarg );
435                 {
436                         char* p;
437
438                         for( p = optarg; *p != '\0'; p++ ) {
439                                 *p = '\0';
440                         }
441                 }
442                 passwd.bv_len = strlen( passwd.bv_val );
443             break;
444         case 'W':
445                 want_bindpw++;
446                 break;
447         case 'Y':
448 #ifdef HAVE_CYRUS_SASL
449                 if( sasl_mech != NULL ) {
450                         fprintf( stderr, "%s: -Y previously specified\n", prog );
451                         return EXIT_FAILURE;
452                 }
453                 if( version == LDAP_VERSION2 ) {
454                         fprintf( stderr, "%s: -Y incompatible with version %d\n",
455                                 prog, version );
456                         return EXIT_FAILURE;
457                 }
458                 if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
459                         fprintf( stderr, "%s: incompatible with authentication choice\n", prog );
460                         return EXIT_FAILURE;
461                 }
462                 authmethod = LDAP_AUTH_SASL;
463                 version = LDAP_VERSION3;
464                 sasl_mech = strdup( optarg );
465 #else
466                 fprintf( stderr, "%s: not compiled with SASL support\n",
467                         prog );
468                 return( EXIT_FAILURE );
469 #endif
470                 break;
471         case 'x':
472                 if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
473                         fprintf( stderr, "%s: incompatible with previous "
474                                 "authentication choice\n", prog );
475                         return EXIT_FAILURE;
476                 }
477                 authmethod = LDAP_AUTH_SIMPLE;
478                 break;
479         case 'X':
480 #ifdef HAVE_CYRUS_SASL
481                 if( sasl_authz_id != NULL ) {
482                         fprintf( stderr, "%s: -X previously specified\n", prog );
483                         return EXIT_FAILURE;
484                 }
485                 if( version == LDAP_VERSION2 ) {
486                         fprintf( stderr, "%s: -X incompatible with LDAPv%d\n",
487                                 prog, version );
488                         return EXIT_FAILURE;
489                 }
490                 if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
491                         fprintf( stderr, "%s: -X incompatible with "
492                                 "authentication choice\n", prog );
493                         return EXIT_FAILURE;
494                 }
495                 authmethod = LDAP_AUTH_SASL;
496                 version = LDAP_VERSION3;
497                 sasl_authz_id = strdup( optarg );
498 #else
499                 fprintf( stderr, "%s: not compiled with SASL support\n",
500                         prog );
501                 return( EXIT_FAILURE );
502 #endif
503                 break;
504         case 'Z':
505 #ifdef HAVE_TLS
506                 if( version == LDAP_VERSION2 ) {
507                         fprintf( stderr, "%s: -Z incompatible with version %d\n",
508                                 prog, version );
509                         return EXIT_FAILURE;
510                 }
511                 version = LDAP_VERSION3;
512                 use_tls++;
513 #else
514                 fprintf( stderr, "%s: not compiled with TLS support\n",
515                         prog );
516                 return( EXIT_FAILURE );
517 #endif
518                 break;
519         default:
520                 fprintf( stderr, "%s: unrecognized option -%c\n",
521                         prog, optopt );
522             usage( prog );
523         }
524     }
525
526         if (version == -1) {
527                 version = LDAP_VERSION3;
528         }
529         if (authmethod == -1 && version > LDAP_VERSION2) {
530 #ifdef HAVE_CYRUS_SASL
531                 authmethod = LDAP_AUTH_SASL;
532 #else
533                 authmethod = LDAP_AUTH_SIMPLE;
534 #endif
535         }
536
537         if ( argc != optind )
538         usage( prog );
539
540     if ( infile != NULL ) {
541         if (( fp = fopen( infile, "r" )) == NULL ) {
542             perror( infile );
543             return( EXIT_FAILURE );
544         }
545     } else {
546         fp = stdin;
547     }
548
549         if ( debug ) {
550                 if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) != LBER_OPT_SUCCESS ) {
551                         fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
552                 }
553                 if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) != LDAP_OPT_SUCCESS ) {
554                         fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
555                 }
556                 ldif_debug = debug;
557         }
558
559 #ifdef SIGPIPE
560         (void) SIGNAL( SIGPIPE, SIG_IGN );
561 #endif
562
563 #ifdef NEW_LOGGING
564     lutil_log_initialize( argc, argv );
565 #endif
566
567     if ( !not ) {
568         if( ( ldaphost != NULL || ldapport ) && ( ldapuri == NULL ) ) {
569                 if ( verbose ) {
570                         fprintf( stderr, "ldap_init( %s, %d )\n",
571                                 ldaphost != NULL ? ldaphost : "<DEFAULT>",
572                                 ldapport );
573                 }
574
575                 ld = ldap_init( ldaphost, ldapport );
576                 if( ld == NULL ) {
577                         perror("ldapsearch: ldap_init");
578                         return EXIT_FAILURE;
579                 }
580
581         } else {
582                 if ( verbose ) {
583                         fprintf( stderr, "ldap_initialize( %s )\n",
584                                 ldapuri != NULL ? ldapuri : "<DEFAULT>" );
585                 }
586
587                 rc = ldap_initialize( &ld, ldapuri );
588                 if( rc != LDAP_SUCCESS ) {
589                         fprintf( stderr, "Could not create LDAP session handle (%d): %s\n",
590                                 rc, ldap_err2string(rc) );
591                         return EXIT_FAILURE;
592                 }
593         }
594
595         /* referrals */
596         if( ldap_set_option( ld, LDAP_OPT_REFERRALS,
597                 referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS )
598         {
599                 fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n",
600                         referrals ? "on" : "off" );
601                 return EXIT_FAILURE;
602         }
603
604
605         if (version == -1 ) {
606                 version = LDAP_VERSION3;
607         }
608
609         if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version )
610                 != LDAP_OPT_SUCCESS )
611         {
612                 fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
613                         version );
614                 return EXIT_FAILURE;
615         }
616
617         if ( use_tls && ldap_start_tls_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
618                 ldap_perror( ld, "ldap_start_tls" );
619                 if ( use_tls > 1 ) {
620                         return( EXIT_FAILURE );
621                 }
622         }
623
624         if (want_bindpw) {
625                 passwd.bv_val = getpassphrase("Enter LDAP Password: ");
626                 passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
627         }
628
629         if ( authmethod == LDAP_AUTH_SASL ) {
630 #ifdef HAVE_CYRUS_SASL
631                 void *defaults;
632
633                 if( sasl_secprops != NULL ) {
634                         rc = ldap_set_option( ld, LDAP_OPT_X_SASL_SECPROPS,
635                                 (void *) sasl_secprops );
636                         
637                         if( rc != LDAP_OPT_SUCCESS ) {
638                                 fprintf( stderr,
639                                         "Could not set LDAP_OPT_X_SASL_SECPROPS: %s\n",
640                                         sasl_secprops );
641                                 return( EXIT_FAILURE );
642                         }
643                 }
644                 
645                 defaults = lutil_sasl_defaults( ld,
646                         sasl_mech,
647                         sasl_realm,
648                         sasl_authc_id,
649                         passwd.bv_val,
650                         sasl_authz_id );
651
652                 rc = ldap_sasl_interactive_bind_s( ld, binddn,
653                         sasl_mech, NULL, NULL,
654                         sasl_flags, lutil_sasl_interact, defaults );
655
656                 if( rc != LDAP_SUCCESS ) {
657                         ldap_perror( ld, "ldap_sasl_interactive_bind_s" );
658                         return( EXIT_FAILURE );
659                 }
660 #else
661                 fprintf( stderr, "%s: not compiled with SASL support\n",
662                         argv[0] );
663                 return( EXIT_FAILURE );
664 #endif
665         }
666         else {
667                 if ( ldap_bind_s( ld, binddn, passwd.bv_val, authmethod )
668                                 != LDAP_SUCCESS ) {
669                         ldap_perror( ld, "ldap_bind" );
670                         return( EXIT_FAILURE );
671                 }
672         }
673
674     }
675
676     rc = 0;
677
678         if ( manageDSAit ) {
679                 int err;
680                 LDAPControl c;
681                 LDAPControl *ctrls[2];
682                 ctrls[0] = &c;
683                 ctrls[1] = NULL;
684
685                 c.ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
686                 c.ldctl_value.bv_val = NULL;
687                 c.ldctl_value.bv_len = 0;
688                 c.ldctl_iscritical = manageDSAit > 1;
689
690                 err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
691
692                 if( err != LDAP_OPT_SUCCESS ) {
693                         fprintf( stderr, "Could not set ManageDSAit %scontrol\n",
694                                 c.ldctl_iscritical ? "critical " : "" );
695                         if( c.ldctl_iscritical ) {
696                                 exit( EXIT_FAILURE );
697                         }
698                 }
699         }
700
701         count = 0;
702     while (( rc == 0 || contoper ) &&
703                 ( rbuf = read_one_record( fp )) != NULL ) {
704         count++;
705
706         start = rbuf;
707
708     rc = process_ldif_rec( start, count );
709
710         if( rc )
711                 fprintf( stderr, "ldif_record() = %d\n", rc );
712                 free( rbuf );
713     }
714
715     if ( !not ) {
716                 ldap_unbind( ld );
717     }
718
719         return( rc );
720 }
721
722
723 static int
724 process_ldif_rec( char *rbuf, int count )
725 {
726     char        *line, *dn, *type, *newrdn, *newsup, *p;
727     int         rc, linenum, modop, replicaport;
728     int         expect_modop, expect_sep, expect_ct, expect_newrdn, expect_newsup;
729     int         expect_deleteoldrdn, deleteoldrdn;
730     int         saw_replica, use_record, new_entry, delete_entry, got_all;
731     LDAPMod     **pmods;
732         int version;
733         struct berval val;
734
735     new_entry = ldapadd;
736
737     rc = got_all = saw_replica = delete_entry = modop = expect_modop = 0;
738     expect_deleteoldrdn = expect_newrdn = expect_newsup = 0;
739         expect_sep = expect_ct = 0;
740     linenum = 0;
741         version = 0;
742     deleteoldrdn = 1;
743     use_record = force;
744     pmods = NULL;
745     dn = newrdn = newsup = NULL;
746
747     while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
748         ++linenum;
749
750         if ( expect_sep && strcasecmp( line, T_MODSEPSTR ) == 0 ) {
751             expect_sep = 0;
752             expect_ct = 1;
753             continue;
754         }
755         
756         if ( ldif_parse_line( line, &type, &val.bv_val, &val.bv_len ) < 0 ) {
757             fprintf( stderr, "%s: invalid format (line %d) entry: \"%s\"\n",
758                     prog, linenum, dn == NULL ? "" : dn );
759             rc = LDAP_PARAM_ERROR;
760             break;
761         }
762
763         if ( dn == NULL ) {
764             if ( !use_record && strcasecmp( type, T_REPLICA_STR ) == 0 ) {
765                 ++saw_replica;
766                 if (( p = strchr( val.bv_val, ':' )) == NULL ) {
767                     replicaport = 0;
768                 } else {
769                     *p++ = '\0';
770                     replicaport = atoi( p );
771                 }
772                 if ( ldaphost != NULL && strcasecmp( val.bv_val, ldaphost ) == 0 &&
773                         replicaport == ldapport ) {
774                     use_record = 1;
775                 }
776             } else if ( count == 1 && linenum == 1 && 
777                         strcasecmp( type, T_VERSION_STR ) == 0 )
778                 {
779                         if( val.bv_len == 0 || atoi(val.bv_val) != 1 ) {
780                         fprintf( stderr, "%s: invalid version %s, line %d (ignored)\n",
781                                 prog, val.bv_val == NULL ? "(null)" : val.bv_val, linenum );
782                         }
783                         version++;
784
785             } else if ( strcasecmp( type, T_DN_STR ) == 0 ) {
786                 if (( dn = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
787                     perror( "strdup" );
788                     exit( EXIT_FAILURE );
789                 }
790                 expect_ct = 1;
791             }
792             goto end_line;      /* skip all lines until we see "dn:" */
793         }
794
795         if ( expect_ct ) {
796             expect_ct = 0;
797             if ( !use_record && saw_replica ) {
798                 printf( "%s: skipping change record for entry: %s\n"
799                         "\t(LDAP host/port does not match replica: lines)\n",
800                         prog, dn );
801                 free( dn );
802                 ber_memfree( type );
803                 ber_memfree( val.bv_val );
804                 return( 0 );
805             }
806
807             if ( strcasecmp( type, T_CHANGETYPESTR ) == 0 ) {
808                 if ( strcasecmp( val.bv_val, T_MODIFYCTSTR ) == 0 ) {
809                         new_entry = 0;
810                         expect_modop = 1;
811                 } else if ( strcasecmp( val.bv_val, T_ADDCTSTR ) == 0 ) {
812                         new_entry = 1;
813                 } else if ( strcasecmp( val.bv_val, T_MODRDNCTSTR ) == 0
814                         || strcasecmp( val.bv_val, T_MODDNCTSTR ) == 0
815                         || strcasecmp( val.bv_val, T_RENAMECTSTR ) == 0)
816                 {
817                     expect_newrdn = 1;
818                 } else if ( strcasecmp( val.bv_val, T_DELETECTSTR ) == 0 ) {
819                     got_all = delete_entry = 1;
820                 } else {
821                     fprintf( stderr,
822                             "%s:  unknown %s \"%s\" (line %d of entry \"%s\")\n",
823                             prog, T_CHANGETYPESTR, val.bv_val, linenum, dn );
824                     rc = LDAP_PARAM_ERROR;
825                 }
826                 goto end_line;
827             } else if ( ldapadd ) {             /*  missing changetype => add */
828                 new_entry = 1;
829                 modop = LDAP_MOD_ADD;
830             } else {
831                 expect_modop = 1;       /* missing changetype => modify */
832             }
833         }
834
835         if ( expect_modop ) {
836             expect_modop = 0;
837             expect_sep = 1;
838             if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) {
839                 modop = LDAP_MOD_ADD;
840                 goto end_line;
841             } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) {
842                 modop = LDAP_MOD_REPLACE;
843                 addmodifyop( &pmods, modop, val.bv_val, NULL );
844                 goto end_line;
845             } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) {
846                 modop = LDAP_MOD_DELETE;
847                 addmodifyop( &pmods, modop, val.bv_val, NULL );
848                 goto end_line;
849             } else {    /* no modify op:  use default */
850                 modop = ldapadd ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
851             }
852         }
853
854         if ( expect_newrdn ) {
855             if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) {
856                         if (( newrdn = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
857                     perror( "strdup" );
858                     exit( EXIT_FAILURE );
859                 }
860                 expect_deleteoldrdn = 1;
861                 expect_newrdn = 0;
862             } else {
863                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
864                         prog, T_NEWRDNSTR, type, linenum, dn );
865                 rc = LDAP_PARAM_ERROR;
866             }
867         } else if ( expect_deleteoldrdn ) {
868             if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) {
869                 deleteoldrdn = ( *val.bv_val == '0' ) ? 0 : 1;
870                 expect_deleteoldrdn = 0;
871                 expect_newsup = 1;
872                 got_all = 1;
873             } else {
874                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
875                         prog, T_DELETEOLDRDNSTR, type, linenum, dn );
876                 rc = LDAP_PARAM_ERROR;
877             }
878         } else if ( expect_newsup ) {
879             if ( strcasecmp( type, T_NEWSUPSTR ) == 0 ) {
880                 if (( newsup = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
881                     perror( "strdup" );
882                     exit( EXIT_FAILURE );
883                 }
884                 expect_newsup = 0;
885             } else {
886                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
887                         prog, T_NEWSUPSTR, type, linenum, dn );
888                 rc = LDAP_PARAM_ERROR;
889             }
890         } else if ( got_all ) {
891             fprintf( stderr,
892                     "%s: extra lines at end (line %d of entry \"%s\")\n",
893                     prog, linenum, dn );
894             rc = LDAP_PARAM_ERROR;
895         } else {
896                 addmodifyop( &pmods, modop, type, val.bv_val == NULL ? NULL : &val );
897         }
898
899 end_line:
900         ber_memfree( type );
901         ber_memfree( val.bv_val );
902     }
903
904         if( linenum == 0 ) {
905                 return 0;
906         }
907
908         if( version && linenum == 1 ) {
909                 return 0;
910         }
911
912     if ( rc == 0 ) {
913         if ( delete_entry ) {
914             rc = dodelete( dn );
915         } else if ( newrdn != NULL ) {
916             rc = dorename( dn, newrdn, newsup, deleteoldrdn );
917         } else {
918             rc = domodify( dn, pmods, new_entry );
919         }
920
921         if ( rc == LDAP_SUCCESS ) {
922             rc = 0;
923         }
924     }
925
926     if ( dn != NULL ) {
927         free( dn );
928     }
929     if ( newrdn != NULL ) {
930         free( newrdn );
931     }
932     if ( pmods != NULL ) {
933         ldap_mods_free( pmods, 1 );
934     }
935
936     return( rc );
937 }
938
939
940 static void
941 addmodifyop(
942         LDAPMod ***pmodsp,
943         int modop,
944         const char *attr,
945         struct berval *val )
946 {
947         LDAPMod         **pmods;
948         int                     i, j;
949
950         pmods = *pmodsp;
951         modop |= LDAP_MOD_BVALUES;
952
953         i = 0;
954         if ( pmods != NULL ) {
955                 for ( ; pmods[ i ] != NULL; ++i ) {
956                         if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
957                                 pmods[ i ]->mod_op == modop )
958                         {
959                                 break;
960                         }
961                 }
962         }
963
964         if ( pmods == NULL || pmods[ i ] == NULL ) {
965                 if (( pmods = (LDAPMod **)ber_memrealloc( pmods, (i + 2) *
966                         sizeof( LDAPMod * ))) == NULL )
967                 {
968                         perror( "realloc" );
969                         exit( EXIT_FAILURE );
970                 }
971
972                 *pmodsp = pmods;
973                 pmods[ i + 1 ] = NULL;
974
975                 pmods[ i ] = (LDAPMod *)ber_memcalloc( 1, sizeof( LDAPMod ));
976                 if ( pmods[ i ] == NULL ) {
977                         perror( "calloc" );
978                         exit( EXIT_FAILURE );
979                 }
980
981                 pmods[ i ]->mod_op = modop;
982                 pmods[ i ]->mod_type = ber_strdup( attr );
983                 if ( pmods[ i ]->mod_type == NULL ) {
984                         perror( "strdup" );
985                         exit( EXIT_FAILURE );
986                 }
987         }
988
989         if ( val != NULL ) {
990                 j = 0;
991                 if ( pmods[ i ]->mod_bvalues != NULL ) {
992                         for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
993                                 /* Empty */;
994                         }
995                 }
996
997                 pmods[ i ]->mod_bvalues = (struct berval **) ber_memrealloc(
998                         pmods[ i ]->mod_bvalues, (j + 2) * sizeof( struct berval * ));
999                 if ( pmods[ i ]->mod_bvalues == NULL ) {
1000                         perror( "ber_realloc" );
1001                         exit( EXIT_FAILURE );
1002                 }
1003
1004                 pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
1005                 pmods[ i ]->mod_bvalues[ j ] = ber_bvdup( val );
1006                 if ( pmods[ i ]->mod_bvalues[ j ] == NULL ) {
1007                         perror( "ber_bvdup" );
1008                         exit( EXIT_FAILURE );
1009                 }
1010         }
1011 }
1012
1013
1014 static int
1015 domodify(
1016         const char *dn,
1017         LDAPMod **pmods,
1018         int newentry )
1019 {
1020     int                 i, j, k, notascii, op;
1021     struct berval       *bvp;
1022
1023     if ( pmods == NULL ) {
1024         fprintf( stderr, "%s: no attributes to change or add (entry=\"%s\")\n",
1025                 prog, dn );
1026         return( LDAP_PARAM_ERROR );
1027     } 
1028
1029     for ( i = 0; pmods[ i ] != NULL; ++i ) {
1030         op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
1031         if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) {
1032                 fprintf( stderr,
1033                         "%s: attribute \"%s\" has no values (entry=\"%s\")\n",
1034                         prog, pmods[i]->mod_type, dn );
1035                 return LDAP_PARAM_ERROR;
1036         }
1037     }
1038
1039     if ( verbose ) {
1040         for ( i = 0; pmods[ i ] != NULL; ++i ) {
1041             op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
1042             printf( "%s %s:\n", op == LDAP_MOD_REPLACE ?
1043                     "replace" : op == LDAP_MOD_ADD ?
1044                     "add" : "delete", pmods[ i ]->mod_type );
1045             if ( pmods[ i ]->mod_bvalues != NULL ) {
1046                 for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
1047                     bvp = pmods[ i ]->mod_bvalues[ j ];
1048                     notascii = 0;
1049                     for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) {
1050                         if ( !isascii( bvp->bv_val[ k ] )) {
1051                             notascii = 1;
1052                             break;
1053                         }
1054                     }
1055                     if ( notascii ) {
1056                         printf( "\tNOT ASCII (%ld bytes)\n", bvp->bv_len );
1057                     } else {
1058                         printf( "\t%s\n", bvp->bv_val );
1059                     }
1060                 }
1061             }
1062         }
1063     }
1064
1065     if ( newentry ) {
1066         printf( "%sadding new entry \"%s\"\n", not ? "!" : "", dn );
1067     } else {
1068         printf( "%smodifying entry \"%s\"\n", not ? "!" : "", dn );
1069     }
1070
1071     if ( !not ) {
1072         if ( newentry ) {
1073             i = ldap_add_s( ld, dn, pmods );
1074         } else {
1075             i = ldap_modify_s( ld, dn, pmods );
1076         }
1077         if ( i != LDAP_SUCCESS ) {
1078             ldap_perror( ld, newentry ? "ldap_add" : "ldap_modify" );
1079         } else if ( verbose ) {
1080             printf( "modify complete\n" );
1081         }
1082     } else {
1083         i = LDAP_SUCCESS;
1084     }
1085
1086     putchar( '\n' );
1087
1088     return( i );
1089 }
1090
1091
1092 static int
1093 dodelete(
1094         const char *dn )
1095 {
1096     int rc;
1097
1098     printf( "%sdeleting entry \"%s\"\n", not ? "!" : "", dn );
1099     if ( !not ) {
1100         if (( rc = ldap_delete_s( ld, dn )) != LDAP_SUCCESS ) {
1101             ldap_perror( ld, "ldap_delete" );
1102         } else if ( verbose ) {
1103             printf( "delete complete" );
1104         }
1105     } else {
1106         rc = LDAP_SUCCESS;
1107     }
1108
1109     putchar( '\n' );
1110
1111     return( rc );
1112 }
1113
1114
1115 static int
1116 dorename(
1117         const char *dn,
1118         const char *newrdn,
1119         const char* newsup,
1120         int deleteoldrdn )
1121 {
1122     int rc;
1123
1124
1125     printf( "%smodifying rdn of entry \"%s\"\n", not ? "!" : "", dn );
1126     if ( verbose ) {
1127         printf( "\tnew RDN: \"%s\" (%skeep existing values)\n",
1128                 newrdn, deleteoldrdn ? "do not " : "" );
1129     }
1130     if ( !not ) {
1131         if (( rc = ldap_rename2_s( ld, dn, newrdn, newsup, deleteoldrdn ))
1132                 != LDAP_SUCCESS ) {
1133             ldap_perror( ld, "ldap_modrdn" );
1134         } else {
1135             printf( "modrdn completed\n" );
1136         }
1137     } else {
1138         rc = LDAP_SUCCESS;
1139     }
1140
1141     putchar( '\n' );
1142
1143     return( rc );
1144 }
1145
1146
1147 static char *
1148 read_one_record( FILE *fp )
1149 {
1150     char        *buf, line[ LDAPMOD_MAXLINE ];
1151     int         lcur, lmax;
1152
1153     lcur = lmax = 0;
1154     buf = NULL;
1155
1156     while ( fgets( line, sizeof(line), fp ) != NULL ) {
1157         int len = strlen( line );
1158
1159                 if( len < 2 ) {
1160                         if( buf == NULL ) {
1161                                 continue;
1162                         } else {
1163                                 break;
1164                         }
1165                 }
1166
1167                 if ( lcur + len + 1 > lmax ) {
1168                         lmax = LDAPMOD_MAXLINE
1169                                 * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
1170
1171                         if (( buf = (char *)realloc( buf, lmax )) == NULL ) {
1172                                 perror( "realloc" );
1173                                 exit( EXIT_FAILURE );
1174                         }
1175                 }
1176
1177                 strcpy( buf + lcur, line );
1178                 lcur += len;
1179     }
1180
1181     return( buf );
1182 }