]> git.sur5r.net Git - openldap/blob - clients/tools/ldapmodify.c
82390b7fdd8c2861b19d8639742088bc4afd2555
[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                         prog );
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 #ifdef LIBERAL_CHANGETYPE_MODOP
809                 /* trim trailing spaces (and log warning ...) */
810
811                 int icnt;
812                 for ( icnt = val.bv_len; --icnt > 0; ) {
813                     if ( !isspace( val.bv_val[icnt] ) ) {
814                         break;
815                     }
816                 }
817
818                 if ( ++icnt != val.bv_len ) {
819                     fprintf( stderr, "%s: illegal trailing space after \"%s: %s\" trimmed (line %d of entry \"%s\")\n",
820                             prog, T_CHANGETYPESTR, val.bv_val, linenum, dn );
821                     val.bv_val[icnt] = '\0';
822                 }
823 #endif /* LIBERAL_CHANGETYPE_MODOP */
824
825                 if ( strcasecmp( val.bv_val, T_MODIFYCTSTR ) == 0 ) {
826                         new_entry = 0;
827                         expect_modop = 1;
828                 } else if ( strcasecmp( val.bv_val, T_ADDCTSTR ) == 0 ) {
829                         new_entry = 1;
830                 } else if ( strcasecmp( val.bv_val, T_MODRDNCTSTR ) == 0
831                         || strcasecmp( val.bv_val, T_MODDNCTSTR ) == 0
832                         || strcasecmp( val.bv_val, T_RENAMECTSTR ) == 0)
833                 {
834                     expect_newrdn = 1;
835                 } else if ( strcasecmp( val.bv_val, T_DELETECTSTR ) == 0 ) {
836                     got_all = delete_entry = 1;
837                 } else {
838                     fprintf( stderr,
839                             "%s:  unknown %s \"%s\" (line %d of entry \"%s\")\n",
840                             prog, T_CHANGETYPESTR, val.bv_val, linenum, dn );
841                     rc = LDAP_PARAM_ERROR;
842                 }
843                 goto end_line;
844             } else if ( ldapadd ) {             /*  missing changetype => add */
845                 new_entry = 1;
846                 modop = LDAP_MOD_ADD;
847             } else {
848                 expect_modop = 1;       /* missing changetype => modify */
849             }
850         }
851
852         if ( expect_modop ) {
853 #ifdef LIBERAL_CHANGETYPE_MODOP
854             /* trim trailing spaces (and log warning ...) */
855             
856             int icnt;
857             for ( icnt = val.bv_len; --icnt > 0; ) {
858                 if ( !isspace( val.bv_val[icnt] ) ) {
859                     break;
860                 }
861             }
862             
863             if ( ++icnt != val.bv_len ) {
864                 fprintf( stderr, "%s: illegal trailing space after \"%s: %s\" trimmed (line %d of entry \"%s\")\n",
865                         prog, type, val.bv_val, linenum, dn );
866                 val.bv_val[icnt] = '\0';
867             }
868 #endif /* LIBERAL_CHANGETYPE_MODOP */
869
870             expect_modop = 0;
871             expect_sep = 1;
872             if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) {
873                 modop = LDAP_MOD_ADD;
874                 goto end_line;
875             } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) {
876                 modop = LDAP_MOD_REPLACE;
877                 addmodifyop( &pmods, modop, val.bv_val, NULL );
878                 goto end_line;
879             } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) {
880                 modop = LDAP_MOD_DELETE;
881                 addmodifyop( &pmods, modop, val.bv_val, NULL );
882                 goto end_line;
883             } else {    /* no modify op:  use default */
884                 modop = ldapadd ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
885             }
886         }
887
888         if ( expect_newrdn ) {
889             if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) {
890                         if (( newrdn = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
891                     perror( "strdup" );
892                     exit( EXIT_FAILURE );
893                 }
894                 expect_deleteoldrdn = 1;
895                 expect_newrdn = 0;
896             } else {
897                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
898                         prog, T_NEWRDNSTR, type, linenum, dn );
899                 rc = LDAP_PARAM_ERROR;
900             }
901         } else if ( expect_deleteoldrdn ) {
902             if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) {
903                 deleteoldrdn = ( *val.bv_val == '0' ) ? 0 : 1;
904                 expect_deleteoldrdn = 0;
905                 expect_newsup = 1;
906                 got_all = 1;
907             } else {
908                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
909                         prog, T_DELETEOLDRDNSTR, type, linenum, dn );
910                 rc = LDAP_PARAM_ERROR;
911             }
912         } else if ( expect_newsup ) {
913             if ( strcasecmp( type, T_NEWSUPSTR ) == 0 ) {
914                 if (( newsup = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
915                     perror( "strdup" );
916                     exit( EXIT_FAILURE );
917                 }
918                 expect_newsup = 0;
919             } else {
920                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
921                         prog, T_NEWSUPSTR, type, linenum, dn );
922                 rc = LDAP_PARAM_ERROR;
923             }
924         } else if ( got_all ) {
925             fprintf( stderr,
926                     "%s: extra lines at end (line %d of entry \"%s\")\n",
927                     prog, linenum, dn );
928             rc = LDAP_PARAM_ERROR;
929         } else {
930                 addmodifyop( &pmods, modop, type, val.bv_val == NULL ? NULL : &val );
931         }
932
933 end_line:
934         ber_memfree( type );
935         ber_memfree( val.bv_val );
936     }
937
938         if( linenum == 0 ) {
939                 return 0;
940         }
941
942         if( version && linenum == 1 ) {
943                 return 0;
944         }
945
946     if ( rc == 0 ) {
947         if ( delete_entry ) {
948             rc = dodelete( dn );
949         } else if ( newrdn != NULL ) {
950             rc = dorename( dn, newrdn, newsup, deleteoldrdn );
951         } else {
952             rc = domodify( dn, pmods, new_entry );
953         }
954
955         if ( rc == LDAP_SUCCESS ) {
956             rc = 0;
957         }
958     }
959
960     if ( dn != NULL ) {
961         free( dn );
962     }
963     if ( newrdn != NULL ) {
964         free( newrdn );
965     }
966     if ( pmods != NULL ) {
967         ldap_mods_free( pmods, 1 );
968     }
969
970     return( rc );
971 }
972
973
974 static void
975 addmodifyop(
976         LDAPMod ***pmodsp,
977         int modop,
978         const char *attr,
979         struct berval *val )
980 {
981         LDAPMod         **pmods;
982         int                     i, j;
983
984         pmods = *pmodsp;
985         modop |= LDAP_MOD_BVALUES;
986
987         i = 0;
988         if ( pmods != NULL ) {
989                 for ( ; pmods[ i ] != NULL; ++i ) {
990                         if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
991                                 pmods[ i ]->mod_op == modop )
992                         {
993                                 break;
994                         }
995                 }
996         }
997
998         if ( pmods == NULL || pmods[ i ] == NULL ) {
999                 if (( pmods = (LDAPMod **)ber_memrealloc( pmods, (i + 2) *
1000                         sizeof( LDAPMod * ))) == NULL )
1001                 {
1002                         perror( "realloc" );
1003                         exit( EXIT_FAILURE );
1004                 }
1005
1006                 *pmodsp = pmods;
1007                 pmods[ i + 1 ] = NULL;
1008
1009                 pmods[ i ] = (LDAPMod *)ber_memcalloc( 1, sizeof( LDAPMod ));
1010                 if ( pmods[ i ] == NULL ) {
1011                         perror( "calloc" );
1012                         exit( EXIT_FAILURE );
1013                 }
1014
1015                 pmods[ i ]->mod_op = modop;
1016                 pmods[ i ]->mod_type = ber_strdup( attr );
1017                 if ( pmods[ i ]->mod_type == NULL ) {
1018                         perror( "strdup" );
1019                         exit( EXIT_FAILURE );
1020                 }
1021         }
1022
1023         if ( val != NULL ) {
1024                 j = 0;
1025                 if ( pmods[ i ]->mod_bvalues != NULL ) {
1026                         for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
1027                                 /* Empty */;
1028                         }
1029                 }
1030
1031                 pmods[ i ]->mod_bvalues = (struct berval **) ber_memrealloc(
1032                         pmods[ i ]->mod_bvalues, (j + 2) * sizeof( struct berval * ));
1033                 if ( pmods[ i ]->mod_bvalues == NULL ) {
1034                         perror( "ber_realloc" );
1035                         exit( EXIT_FAILURE );
1036                 }
1037
1038                 pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
1039                 pmods[ i ]->mod_bvalues[ j ] = ber_bvdup( val );
1040                 if ( pmods[ i ]->mod_bvalues[ j ] == NULL ) {
1041                         perror( "ber_bvdup" );
1042                         exit( EXIT_FAILURE );
1043                 }
1044         }
1045 }
1046
1047
1048 static int
1049 domodify(
1050         const char *dn,
1051         LDAPMod **pmods,
1052         int newentry )
1053 {
1054     int                 i, j, k, notascii, op;
1055     struct berval       *bvp;
1056
1057     if ( pmods == NULL ) {
1058         fprintf( stderr, "%s: no attributes to change or add (entry=\"%s\")\n",
1059                 prog, dn );
1060         return( LDAP_PARAM_ERROR );
1061     } 
1062
1063     for ( i = 0; pmods[ i ] != NULL; ++i ) {
1064         op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
1065         if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) {
1066                 fprintf( stderr,
1067                         "%s: attribute \"%s\" has no values (entry=\"%s\")\n",
1068                         prog, pmods[i]->mod_type, dn );
1069                 return LDAP_PARAM_ERROR;
1070         }
1071     }
1072
1073     if ( verbose ) {
1074         for ( i = 0; pmods[ i ] != NULL; ++i ) {
1075             op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
1076             printf( "%s %s:\n", op == LDAP_MOD_REPLACE ?
1077                     "replace" : op == LDAP_MOD_ADD ?
1078                     "add" : "delete", pmods[ i ]->mod_type );
1079             if ( pmods[ i ]->mod_bvalues != NULL ) {
1080                 for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
1081                     bvp = pmods[ i ]->mod_bvalues[ j ];
1082                     notascii = 0;
1083                     for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) {
1084                         if ( !isascii( bvp->bv_val[ k ] )) {
1085                             notascii = 1;
1086                             break;
1087                         }
1088                     }
1089                     if ( notascii ) {
1090                         printf( "\tNOT ASCII (%ld bytes)\n", bvp->bv_len );
1091                     } else {
1092                         printf( "\t%s\n", bvp->bv_val );
1093                     }
1094                 }
1095             }
1096         }
1097     }
1098
1099     if ( newentry ) {
1100         printf( "%sadding new entry \"%s\"\n", not ? "!" : "", dn );
1101     } else {
1102         printf( "%smodifying entry \"%s\"\n", not ? "!" : "", dn );
1103     }
1104
1105     if ( !not ) {
1106         if ( newentry ) {
1107             i = ldap_add_s( ld, dn, pmods );
1108         } else {
1109             i = ldap_modify_s( ld, dn, pmods );
1110         }
1111         if ( i != LDAP_SUCCESS ) {
1112                 /* print error message about failed update including DN */
1113                 fprintf( stderr, "%s: update failed: %s\n", prog, dn );
1114                 ldap_perror( ld, newentry ? "ldap_add" : "ldap_modify" );
1115         } else if ( verbose ) {
1116             printf( "modify complete\n" );
1117         }
1118     } else {
1119         i = LDAP_SUCCESS;
1120     }
1121
1122     putchar( '\n' );
1123
1124     return( i );
1125 }
1126
1127
1128 static int
1129 dodelete(
1130         const char *dn )
1131 {
1132     int rc;
1133
1134     printf( "%sdeleting entry \"%s\"\n", not ? "!" : "", dn );
1135     if ( !not ) {
1136         if (( rc = ldap_delete_s( ld, dn )) != LDAP_SUCCESS ) {
1137                 fprintf( stderr, "%s: delete failed: %s\n", prog, dn );
1138                 ldap_perror( ld, "ldap_delete" );
1139         } else if ( verbose ) {
1140             printf( "delete complete" );
1141         }
1142     } else {
1143         rc = LDAP_SUCCESS;
1144     }
1145
1146     putchar( '\n' );
1147
1148     return( rc );
1149 }
1150
1151
1152 static int
1153 dorename(
1154         const char *dn,
1155         const char *newrdn,
1156         const char* newsup,
1157         int deleteoldrdn )
1158 {
1159     int rc;
1160
1161
1162     printf( "%smodifying rdn of entry \"%s\"\n", not ? "!" : "", dn );
1163     if ( verbose ) {
1164         printf( "\tnew RDN: \"%s\" (%skeep existing values)\n",
1165                 newrdn, deleteoldrdn ? "do not " : "" );
1166     }
1167     if ( !not ) {
1168         if (( rc = ldap_rename2_s( ld, dn, newrdn, newsup, deleteoldrdn ))
1169                 != LDAP_SUCCESS )
1170         {
1171                 fprintf( stderr, "%s: rename failed: %s\n", prog, dn );
1172                 ldap_perror( ld, "ldap_modrdn" );
1173         } else {
1174             printf( "modrdn completed\n" );
1175         }
1176     } else {
1177         rc = LDAP_SUCCESS;
1178     }
1179
1180     putchar( '\n' );
1181
1182     return( rc );
1183 }
1184
1185
1186 static char *
1187 read_one_record( FILE *fp )
1188 {
1189     char        *buf, line[ LDAPMOD_MAXLINE ];
1190     int         lcur, lmax;
1191
1192     lcur = lmax = 0;
1193     buf = NULL;
1194
1195     while ( fgets( line, sizeof(line), fp ) != NULL ) {
1196         int len = strlen( line );
1197
1198                 if( len < 2 || ( len == 3 && *line == '\r' )) {
1199                         if( buf == NULL ) {
1200                                 continue;
1201                         } else {
1202                                 break;
1203                         }
1204                 }
1205
1206                 if ( lcur + len + 1 > lmax ) {
1207                         lmax = LDAPMOD_MAXLINE
1208                                 * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
1209
1210                         if (( buf = (char *)realloc( buf, lmax )) == NULL ) {
1211                                 perror( "realloc" );
1212                                 exit( EXIT_FAILURE );
1213                         }
1214                 }
1215
1216                 strcpy( buf + lcur, line );
1217                 lcur += len;
1218     }
1219
1220     return( buf );
1221 }