]> git.sur5r.net Git - openldap/blob - clients/tools/ldapmodify.c
bb0ee7ffb5f7d7fc8693a9735814bcada1225acf
[openldap] / clients / tools / ldapmodify.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2002 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 #include <ac/ctype.h>
14 #include <ac/string.h>
15 #include <ac/unistd.h>
16
17 #ifdef HAVE_SYS_STAT_H
18 #include <sys/stat.h>
19 #endif
20
21 #ifdef HAVE_SYS_FILE_H
22 #include <sys/file.h>
23 #endif
24 #ifdef HAVE_FCNTL_H
25 #include <fcntl.h>
26 #endif
27
28 #include <ldap.h>
29
30 #include "lutil.h"
31 #include "lutil_ldap.h"
32 #include "ldif.h"
33 #include "ldap_defaults.h"
34 #include "ldap_log.h"
35 #include "ldap_pvt.h"
36
37 #include "common.h"
38
39
40 static int      ldapadd, force = 0;
41 static char *rejfile = NULL;
42 static LDAP     *ld = NULL;
43
44 #define LDAPMOD_MAXLINE         4096
45
46 /* strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */
47 #define T_VERSION_STR           "version"
48 #define T_REPLICA_STR           "replica"
49 #define T_DN_STR                "dn"
50 #define T_CONTROL_STR           "control"
51 #define T_CHANGETYPESTR         "changetype"
52 #define T_ADDCTSTR              "add"
53 #define T_MODIFYCTSTR           "modify"
54 #define T_DELETECTSTR           "delete"
55 #define T_MODRDNCTSTR           "modrdn"
56 #define T_MODDNCTSTR            "moddn"
57 #define T_RENAMECTSTR           "rename"
58 #define T_MODOPADDSTR           "add"
59 #define T_MODOPREPLACESTR       "replace"
60 #define T_MODOPDELETESTR        "delete"
61 #define T_MODSEPSTR             "-"
62 #define T_NEWRDNSTR             "newrdn"
63 #define T_DELETEOLDRDNSTR       "deleteoldrdn"
64 #define T_NEWSUPSTR             "newsuperior"
65
66
67 static int process_ldif_rec LDAP_P(( char *rbuf, int count ));
68 static int parse_ldif_control LDAP_P(( char *line, LDAPControl ***pctrls ));
69 static void addmodifyop LDAP_P((
70         LDAPMod ***pmodsp, int modop,
71         const char *attr,
72         struct berval *value ));
73 static int domodify LDAP_P((
74         const char *dn,
75         LDAPMod **pmods,
76     LDAPControl **pctrls,
77         int newentry ));
78 static int dodelete LDAP_P((
79         const char *dn,
80     LDAPControl **pctrls ));
81 static int dorename LDAP_P((
82         const char *dn,
83         const char *newrdn,
84         const char *newsup,
85         int deleteoldrdn,
86     LDAPControl **pctrls ));
87 static char *read_one_record LDAP_P(( FILE *fp ));
88
89 void
90 usage( void )
91 {
92     fprintf( stderr,
93 "Add or modify entries from an LDAP server\n\n"
94 "usage: %s [options]\n"
95 "       The list of desired operations are read from stdin or from the file\n"
96 "       specified by \"-f file\".\n"
97 "Add or modify options:\n"
98 "  -a         add values (default%s)\n"
99 "  -F         force all changes records to be used\n"
100 "  -S file    write skipped modifications to `file'\n"
101                  , prog, (ldapadd ? "" : " is to replace") );
102         tool_common_usage();
103     exit( EXIT_FAILURE );
104 }
105
106
107 const char options[] = "aFrS:" "cCd:D:e:f:h:H:IkKMnO:p:P:QR:U:vw:WxX:y:Y:Z";
108
109 int
110 handle_private_option( int i )
111 {
112         switch ( i ) {
113 #if 0
114                 char    *control, *cvalue;
115                 int             crit;
116         case 'E': /* modify controls */
117                 if( version == LDAP_VERSION2 ) {
118                         fprintf( stderr, "%s: -E incompatible with LDAPv%d\n",
119                                 prog, version );
120                         exit( EXIT_FAILURE );
121                 }
122
123                 /* should be extended to support comma separated list of
124                  *      [!]key[=value] parameters, e.g.  -E !foo,bar=567
125                  */
126
127                 crit = 0;
128                 cvalue = NULL;
129                 if( optarg[0] == '!' ) {
130                         crit = 1;
131                         optarg++;
132                 }
133
134                 control = ber_strdup( optarg );
135                 if ( (cvalue = strchr( control, '=' )) != NULL ) {
136                         *cvalue++ = '\0';
137                 }
138                 fprintf( stderr, "Invalid modify control name: %s\n", control );
139                 usage();
140 #endif
141
142         case 'a':       /* add */
143             ldapadd = 1;
144             break;
145
146         case 'F':       /* force all changes records to be used */
147             force = 1;
148             break;
149
150         case 'r':       /* replace (obsolete) */
151                 break;
152
153         case 'S':       /* skipped modifications to file */
154                 if( rejfile != NULL ) {
155                         fprintf( stderr, "%s: -S previously specified\n", prog );
156                         exit( EXIT_FAILURE );
157                 }
158                 rejfile = ber_strdup( optarg );
159                 break;
160
161         default:
162                 return 0;
163         }
164         return 1;
165 }
166
167
168 int
169 main( int argc, char **argv )
170 {
171     char                *rbuf, *start, *rejbuf = NULL;
172     FILE                *fp, *rejfp;
173         char            *matched_msg = NULL, *error_msg = NULL;
174         int             rc, retval;
175         int count, len;
176
177     prog = lutil_progname( "ldapmodify", argc, argv );
178
179         /* strncmp instead of strcmp since NT binaries carry .exe extension */
180     ldapadd = ( strncasecmp( prog, "ldapadd", sizeof("ldapadd")-1 ) == 0 );
181
182     /* Print usage when no parameters */
183     if( argc < 2 ) usage();
184
185         tool_args( argc, argv );
186
187         if ( argc != optind )
188         usage();
189
190     if ( rejfile != NULL ) {
191         if (( rejfp = fopen( rejfile, "w" )) == NULL ) {
192             perror( rejfile );
193             return( EXIT_FAILURE );
194         }
195     } else {
196         rejfp = NULL;
197     }
198
199     if ( infile != NULL ) {
200         if (( fp = fopen( infile, "r" )) == NULL ) {
201             perror( infile );
202             return( EXIT_FAILURE );
203         }
204     } else {
205         fp = stdin;
206     }
207
208         if ( debug )
209                 ldif_debug = debug;
210
211         ld = tool_conn_setup( not, 0 );
212
213     if ( !not ) {
214         if ( pw_file || want_bindpw ) {
215                 if ( pw_file ) {
216                         rc = lutil_get_filed_password( pw_file, &passwd );
217                         if( rc ) return EXIT_FAILURE;
218                 } else {
219                         passwd.bv_val = getpassphrase( "Enter LDAP Password: " );
220                         passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
221                 }
222         }
223
224         tool_bind( ld );
225     }
226
227     rc = 0;
228
229         if ( authzid || manageDSAit || noop )
230                 tool_server_controls( ld, NULL, 0 );
231
232         count = 0;
233         retval = 0;
234     while (( rc == 0 || contoper ) &&
235                 ( rbuf = read_one_record( fp )) != NULL ) {
236         count++;
237
238         start = rbuf;
239
240         if ( rejfp ) {
241                 len = strlen( rbuf );
242                 if (( rejbuf = (char *)ber_memalloc( len+1 )) == NULL ) {
243                         perror( "malloc" );
244                         exit( EXIT_FAILURE );
245                 }
246                 memcpy( rejbuf, rbuf, len+1 );
247         }
248
249     rc = process_ldif_rec( start, count );
250
251         if ( rc )
252                 retval = rc;
253         if ( rc && rejfp ) {
254                 fprintf(rejfp, "# Error: %s (%d)", ldap_err2string(rc), rc);
255
256                 ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &matched_msg);
257                 if ( matched_msg != NULL && *matched_msg != '\0' ) {
258                         fprintf( rejfp, ", matched DN: %s", matched_msg );
259                 }
260
261                 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &error_msg);
262                 if ( error_msg != NULL && *error_msg != '\0' ) {
263                         fprintf( rejfp, ", additional info: %s", error_msg );
264                 }
265                 fprintf( rejfp, "\n%s\n", rejbuf );
266         }
267                 if (rejfp) 
268                         free( rejbuf );
269                 free( rbuf );
270     }
271
272     if ( !not ) {
273                 ldap_unbind( ld );
274     }
275
276     if ( rejfp != NULL ) {
277             fclose( rejfp );
278     }
279
280     return( retval );
281 }
282
283
284 static int
285 process_ldif_rec( char *rbuf, int count )
286 {
287     char        *line, *dn, *type, *newrdn, *newsup, *p;
288     int         rc, linenum, modop, replicaport;
289     int         expect_modop, expect_sep, expect_ct, expect_newrdn, expect_newsup;
290     int         expect_deleteoldrdn, deleteoldrdn;
291     int         saw_replica, use_record, new_entry, delete_entry, got_all;
292     LDAPMod     **pmods;
293         int version;
294         struct berval val;
295     LDAPControl **pctrls;
296
297     new_entry = ldapadd;
298
299     rc = got_all = saw_replica = delete_entry = modop = expect_modop = 0;
300     expect_deleteoldrdn = expect_newrdn = expect_newsup = 0;
301         expect_sep = expect_ct = 0;
302     linenum = 0;
303         version = 0;
304     deleteoldrdn = 1;
305     use_record = force;
306     pmods = NULL;
307     pctrls = NULL;
308     dn = newrdn = newsup = NULL;
309
310     while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
311         ++linenum;
312
313         if ( expect_sep && strcasecmp( line, T_MODSEPSTR ) == 0 ) {
314             expect_sep = 0;
315             expect_ct = 1;
316             continue;
317         }
318         
319         if ( ldif_parse_line( line, &type, &val.bv_val, &val.bv_len ) < 0 ) {
320             fprintf( stderr, "%s: invalid format (line %d) entry: \"%s\"\n",
321                     prog, linenum, dn == NULL ? "" : dn );
322             rc = LDAP_PARAM_ERROR;
323             break;
324         }
325
326         if ( dn == NULL ) {
327             if ( !use_record && strcasecmp( type, T_REPLICA_STR ) == 0 ) {
328                 ++saw_replica;
329                 if (( p = strchr( val.bv_val, ':' )) == NULL ) {
330                     replicaport = 0;
331                 } else {
332                     *p++ = '\0';
333                     replicaport = atoi( p );
334                 }
335                 if ( ldaphost != NULL && strcasecmp( val.bv_val, ldaphost ) == 0 &&
336                         replicaport == ldapport ) {
337                     use_record = 1;
338                 }
339             } else if ( count == 1 && linenum == 1 && 
340                         strcasecmp( type, T_VERSION_STR ) == 0 )
341                 {
342                         if( val.bv_len == 0 || atoi(val.bv_val) != 1 ) {
343                         fprintf( stderr, "%s: invalid version %s, line %d (ignored)\n",
344                                 prog, val.bv_val, linenum );
345                         }
346                         version++;
347
348             } else if ( strcasecmp( type, T_DN_STR ) == 0 ) {
349                 if (( dn = ber_strdup( val.bv_val )) == NULL ) {
350                     perror( "strdup" );
351                     exit( EXIT_FAILURE );
352                 }
353                 expect_ct = 1;
354             }
355             goto end_line;      /* skip all lines until we see "dn:" */
356         }
357
358         if ( expect_ct ) {
359         
360         /* Check for "control" tag after dn and before changetype. */
361         if (strcasecmp(type, T_CONTROL_STR) == 0) {
362             /* Parse and add it to the list of controls */
363             rc = parse_ldif_control( line, &pctrls );
364             if (rc != 0) {
365                         fprintf( stderr, "%s: Error processing %s line, line %d: %s\n",
366                                 prog, T_CONTROL_STR, linenum, ldap_err2string(rc) );
367             }
368             goto end_line;
369         }
370         
371             expect_ct = 0;
372             if ( !use_record && saw_replica ) {
373                 printf( "%s: skipping change record for entry: %s\n"
374                         "\t(LDAP host/port does not match replica: lines)\n",
375                         prog, dn );
376                 free( dn );
377                 ber_memfree( type );
378                 ber_memfree( val.bv_val );
379                 return( 0 );
380             }
381
382             if ( strcasecmp( type, T_CHANGETYPESTR ) == 0 ) {
383 #ifdef LIBERAL_CHANGETYPE_MODOP
384                 /* trim trailing spaces (and log warning ...) */
385
386                 int icnt;
387                 for ( icnt = val.bv_len; --icnt > 0; ) {
388                     if ( !isspace( (unsigned char) val.bv_val[icnt] ) ) {
389                         break;
390                     }
391                 }
392
393                 if ( ++icnt != val.bv_len ) {
394                     fprintf( stderr, "%s: illegal trailing space after \"%s: %s\" trimmed (line %d of entry \"%s\")\n",
395                             prog, T_CHANGETYPESTR, val.bv_val, linenum, dn );
396                     val.bv_val[icnt] = '\0';
397                 }
398 #endif /* LIBERAL_CHANGETYPE_MODOP */
399
400                 if ( strcasecmp( val.bv_val, T_MODIFYCTSTR ) == 0 ) {
401                         new_entry = 0;
402                         expect_modop = 1;
403                 } else if ( strcasecmp( val.bv_val, T_ADDCTSTR ) == 0 ) {
404                         new_entry = 1;
405                 } else if ( strcasecmp( val.bv_val, T_MODRDNCTSTR ) == 0
406                         || strcasecmp( val.bv_val, T_MODDNCTSTR ) == 0
407                         || strcasecmp( val.bv_val, T_RENAMECTSTR ) == 0)
408                 {
409                     expect_newrdn = 1;
410                 } else if ( strcasecmp( val.bv_val, T_DELETECTSTR ) == 0 ) {
411                     got_all = delete_entry = 1;
412                 } else {
413                     fprintf( stderr,
414                             "%s:  unknown %s \"%s\" (line %d of entry \"%s\")\n",
415                             prog, T_CHANGETYPESTR, val.bv_val, linenum, dn );
416                     rc = LDAP_PARAM_ERROR;
417                 }
418                 goto end_line;
419             } else if ( ldapadd ) {             /*  missing changetype => add */
420                 new_entry = 1;
421                 modop = LDAP_MOD_ADD;
422             } else {
423                 expect_modop = 1;       /* missing changetype => modify */
424             }
425         }
426
427         if ( expect_modop ) {
428 #ifdef LIBERAL_CHANGETYPE_MODOP
429             /* trim trailing spaces (and log warning ...) */
430             
431             int icnt;
432             for ( icnt = val.bv_len; --icnt > 0; ) {
433                 if ( !isspace( (unsigned char) val.bv_val[icnt] ) ) {
434                     break;
435                 }
436             }
437             
438             if ( ++icnt != val.bv_len ) {
439                 fprintf( stderr, "%s: illegal trailing space after \"%s: %s\" trimmed (line %d of entry \"%s\")\n",
440                         prog, type, val.bv_val, linenum, dn );
441                 val.bv_val[icnt] = '\0';
442             }
443 #endif /* LIBERAL_CHANGETYPE_MODOP */
444
445             expect_modop = 0;
446             expect_sep = 1;
447             if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) {
448                 modop = LDAP_MOD_ADD;
449                 goto end_line;
450             } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) {
451                 modop = LDAP_MOD_REPLACE;
452                 addmodifyop( &pmods, modop, val.bv_val, NULL );
453                 goto end_line;
454             } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) {
455                 modop = LDAP_MOD_DELETE;
456                 addmodifyop( &pmods, modop, val.bv_val, NULL );
457                 goto end_line;
458             } else {    /* no modify op:  use default */
459                 modop = ldapadd ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
460             }
461         }
462
463         if ( expect_newrdn ) {
464             if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) {
465                         if (( newrdn = ber_strdup( val.bv_val )) == NULL ) {
466                     perror( "strdup" );
467                     exit( EXIT_FAILURE );
468                 }
469                 expect_deleteoldrdn = 1;
470                 expect_newrdn = 0;
471             } else {
472                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
473                         prog, T_NEWRDNSTR, type, linenum, dn );
474                 rc = LDAP_PARAM_ERROR;
475             }
476         } else if ( expect_deleteoldrdn ) {
477             if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) {
478                 deleteoldrdn = ( *val.bv_val == '0' ) ? 0 : 1;
479                 expect_deleteoldrdn = 0;
480                 expect_newsup = 1;
481                 got_all = 1;
482             } else {
483                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
484                         prog, T_DELETEOLDRDNSTR, type, linenum, dn );
485                 rc = LDAP_PARAM_ERROR;
486             }
487         } else if ( expect_newsup ) {
488             if ( strcasecmp( type, T_NEWSUPSTR ) == 0 ) {
489                 if (( newsup = ber_strdup( val.bv_val )) == NULL ) {
490                     perror( "strdup" );
491                     exit( EXIT_FAILURE );
492                 }
493                 expect_newsup = 0;
494             } else {
495                 fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
496                         prog, T_NEWSUPSTR, type, linenum, dn );
497                 rc = LDAP_PARAM_ERROR;
498             }
499         } else if ( got_all ) {
500             fprintf( stderr,
501                     "%s: extra lines at end (line %d of entry \"%s\")\n",
502                     prog, linenum, dn );
503             rc = LDAP_PARAM_ERROR;
504         } else {
505                 addmodifyop( &pmods, modop, type, &val );
506         }
507
508 end_line:
509         ber_memfree( type );
510         ber_memfree( val.bv_val );
511     }
512
513         if( linenum == 0 ) {
514                 return 0;
515         }
516
517         if( version && linenum == 1 ) {
518                 return 0;
519         }
520
521     /* If default controls are set (as with -M option) and controls are
522        specified in the LDIF file, we must add the default controls to
523        the list of controls sent with the ldap operation.
524     */
525     if ( rc == 0 ) {
526         if (pctrls) {
527             LDAPControl **defctrls = NULL;   /* Default server controls */
528             LDAPControl **newctrls = NULL;
529             ldap_get_option(ld, LDAP_OPT_SERVER_CONTROLS, &defctrls);
530             if (defctrls) {
531                 int npc=0;                  /* Number of LDIF controls */
532                 int ndefc=0;                /* Number of default controls */
533                 while (pctrls[npc])         /* Count LDIF controls */
534                     npc++; 
535                 while (defctrls[ndefc])     /* Count default controls */
536                     ndefc++;
537                 newctrls = ber_memrealloc(pctrls, (npc+ndefc+1)*sizeof(LDAPControl*));
538                 if (newctrls == NULL)
539                     rc = LDAP_NO_MEMORY;
540                 else {
541                     int i;
542                     pctrls = newctrls;
543                     for (i=npc; i<npc+ndefc; i++) {
544                         pctrls[i] = ldap_control_dup(defctrls[i-npc]);
545                         if (pctrls[i] == NULL) {
546                             rc = LDAP_NO_MEMORY;
547                             break;
548                         }
549                     }
550                     pctrls[npc+ndefc] = NULL;
551                     ldap_controls_free(defctrls);  /* Must be freed by library */
552                 }
553             }
554         }
555     }
556
557
558     if ( rc == 0 ) {
559         if ( delete_entry ) {
560             rc = dodelete( dn, pctrls );
561         } else if ( newrdn != NULL ) {
562             rc = dorename( dn, newrdn, newsup, deleteoldrdn, pctrls );
563         } else {
564             rc = domodify( dn, pmods, pctrls, new_entry );
565         }
566
567         if ( rc == LDAP_SUCCESS ) {
568             rc = 0;
569         }
570     }
571
572     if ( dn != NULL ) {
573         free( dn );
574     }
575     if ( newrdn != NULL ) {
576         free( newrdn );
577     }
578     if ( pmods != NULL ) {
579         ldap_mods_free( pmods, 1 );
580     }
581
582     if (pctrls != NULL) {
583         ldap_controls_free( pctrls );
584     }
585
586     return( rc );
587 }
588
589 /* Parse an LDIF control line of the form
590       control:  oid  [true/false]  [: value]              or
591       control:  oid  [true/false]  [:: base64-value]      or
592       control:  oid  [true/false]  [:< url]
593    The control is added to the list of controls in *ppctrls.
594 */      
595 static int
596 parse_ldif_control( char *line, 
597                     LDAPControl ***ppctrls )
598 {
599     char *oid = NULL;
600     int criticality = 0;   /* Default is false if not present */
601     char *type=NULL;
602     char *val = NULL;
603     ber_len_t value_len = 0;
604     int i, rc=0;
605     char *s, *oidStart, *pcolon;
606     LDAPControl *newctrl = NULL;
607     LDAPControl **pctrls = NULL;
608
609     if (ppctrls) {
610         pctrls = *ppctrls;
611     }
612     s = line + strlen(T_CONTROL_STR);  /* Skip over "control" */
613     pcolon = s;                        /* Save this position for later */
614     if (*s++ != ':')                   /* Make sure colon follows */
615         return ( LDAP_PARAM_ERROR );
616     while (*s && isspace(*s))  s++;    /* Skip white space before OID */
617
618     /* OID should come next. Validate and extract it. */
619     if (*s == 0)
620         return ( LDAP_PARAM_ERROR );
621     oidStart = s;
622     while (isdigit(*s) || *s == '.')  s++;    /* OID should be digits or . */
623     if (s == oidStart) 
624         return ( LDAP_PARAM_ERROR );   /* OID was not present */
625     if (*s) {                          /* End of OID should be space or NULL */
626         if (!isspace(*s))
627             return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */
628         *s++ = 0;                    /* Replace space with null to terminate */
629     }
630
631     
632     oid = ber_strdup(oidStart);
633     if (oid == NULL)
634         return ( LDAP_NO_MEMORY );
635
636     /* Optional Criticality field is next. */
637     while (*s && isspace(*s))  s++;   /* Skip white space before criticality */
638     if (strncasecmp(s, "true", 4) == 0) {
639         criticality = 1;
640         s += 4;
641     } 
642     else if (strncasecmp(s, "false", 5) == 0) {
643         criticality = 0;
644         s += 5;
645     }
646
647     /* Optional value field is next */
648     while (*s && isspace(*s))  s++;    /* Skip white space before value */
649     if (*s) {
650         if (*s != ':') {           /* If value is present, must start with : */
651             rc = LDAP_PARAM_ERROR;
652             goto cleanup;
653         }
654
655         /* Shift value down over OID and criticality so it's in the form
656              control: value
657              control:: base64-value
658              control:< url
659            Then we can use ldif_parse_line to extract and decode the value
660         */
661         while ( (*pcolon++ = *s++) != 0)     /* Shift value */
662             ;
663         rc = ldif_parse_line(line, &type, &val, &value_len);
664         if (type)  ber_memfree(type);   /* Don't need this field*/
665         if (rc < 0) {
666             rc = LDAP_PARAM_ERROR;
667             goto cleanup;
668         }
669     }
670
671     /* Create a new LDAPControl structure. */
672     newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl));
673     if ( newctrl == NULL ) {
674         rc = LDAP_NO_MEMORY;
675         goto cleanup;
676     }
677     newctrl->ldctl_oid = oid;
678     oid = NULL;
679     newctrl->ldctl_iscritical = criticality;
680     newctrl->ldctl_value.bv_len = value_len;
681     newctrl->ldctl_value.bv_val = val;
682     val = NULL;
683
684     /* Add the new control to the passed-in list of controls. */
685     i = 0;
686     if (pctrls) {
687         while ( pctrls[i] )      /* Count the # of controls passed in */
688             i++;
689     }
690     /* Allocate 1 more slot for the new control and 1 for the NULL. */
691     pctrls = (LDAPControl **)ber_memrealloc(pctrls, (i+2)*(sizeof(LDAPControl *)));
692     if (pctrls == NULL) {
693         rc = LDAP_NO_MEMORY;
694         goto cleanup;
695     }
696     pctrls[i] = newctrl;
697     newctrl = NULL;
698     pctrls[i+1] = NULL;
699     *ppctrls = pctrls;
700
701 cleanup:
702     if (newctrl) {
703         if (newctrl->ldctl_oid)
704             ber_memfree(newctrl->ldctl_oid);
705         if (newctrl->ldctl_value.bv_val)
706             ber_memfree(newctrl->ldctl_value.bv_val);
707         ber_memfree(newctrl);
708     }
709     if (val)
710         ber_memfree(val);
711     if (oid)
712         ber_memfree(oid);
713
714     return( rc );
715 }
716
717
718 static void
719 addmodifyop(
720         LDAPMod ***pmodsp,
721         int modop,
722         const char *attr,
723         struct berval *val )
724 {
725         LDAPMod         **pmods;
726         int                     i, j;
727
728         pmods = *pmodsp;
729         modop |= LDAP_MOD_BVALUES;
730
731         i = 0;
732         if ( pmods != NULL ) {
733                 for ( ; pmods[ i ] != NULL; ++i ) {
734                         if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
735                                 pmods[ i ]->mod_op == modop )
736                         {
737                                 break;
738                         }
739                 }
740         }
741
742         if ( pmods == NULL || pmods[ i ] == NULL ) {
743                 if (( pmods = (LDAPMod **)ber_memrealloc( pmods, (i + 2) *
744                         sizeof( LDAPMod * ))) == NULL )
745                 {
746                         perror( "realloc" );
747                         exit( EXIT_FAILURE );
748                 }
749
750                 *pmodsp = pmods;
751                 pmods[ i + 1 ] = NULL;
752
753                 pmods[ i ] = (LDAPMod *)ber_memcalloc( 1, sizeof( LDAPMod ));
754                 if ( pmods[ i ] == NULL ) {
755                         perror( "calloc" );
756                         exit( EXIT_FAILURE );
757                 }
758
759                 pmods[ i ]->mod_op = modop;
760                 pmods[ i ]->mod_type = ber_strdup( attr );
761                 if ( pmods[ i ]->mod_type == NULL ) {
762                         perror( "strdup" );
763                         exit( EXIT_FAILURE );
764                 }
765         }
766
767         if ( val != NULL ) {
768                 j = 0;
769                 if ( pmods[ i ]->mod_bvalues != NULL ) {
770                         for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
771                                 /* Empty */;
772                         }
773                 }
774
775                 pmods[ i ]->mod_bvalues = (struct berval **) ber_memrealloc(
776                         pmods[ i ]->mod_bvalues, (j + 2) * sizeof( struct berval * ));
777                 if ( pmods[ i ]->mod_bvalues == NULL ) {
778                         perror( "ber_realloc" );
779                         exit( EXIT_FAILURE );
780                 }
781
782                 pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
783                 pmods[ i ]->mod_bvalues[ j ] = ber_bvdup( val );
784                 if ( pmods[ i ]->mod_bvalues[ j ] == NULL ) {
785                         perror( "ber_bvdup" );
786                         exit( EXIT_FAILURE );
787                 }
788         }
789 }
790
791
792 static int
793 domodify(
794         const char *dn,
795         LDAPMod **pmods,
796     LDAPControl **pctrls,
797         int newentry )
798 {
799     int                 i, j, k, notascii, op;
800     struct berval       *bvp;
801
802     if ( pmods == NULL ) {
803         fprintf( stderr, "%s: no attributes to change or add (entry=\"%s\")\n",
804                 prog, dn );
805         return( LDAP_PARAM_ERROR );
806     } 
807
808     for ( i = 0; pmods[ i ] != NULL; ++i ) {
809         op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
810         if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) {
811                 fprintf( stderr,
812                         "%s: attribute \"%s\" has no values (entry=\"%s\")\n",
813                         prog, pmods[i]->mod_type, dn );
814                 return LDAP_PARAM_ERROR;
815         }
816     }
817
818     if ( verbose ) {
819         for ( i = 0; pmods[ i ] != NULL; ++i ) {
820             op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
821             printf( "%s %s:\n", op == LDAP_MOD_REPLACE ?
822                     "replace" : op == LDAP_MOD_ADD ?
823                     "add" : "delete", pmods[ i ]->mod_type );
824             if ( pmods[ i ]->mod_bvalues != NULL ) {
825                 for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
826                     bvp = pmods[ i ]->mod_bvalues[ j ];
827                     notascii = 0;
828                     for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) {
829                         if ( !isascii( bvp->bv_val[ k ] )) {
830                             notascii = 1;
831                             break;
832                         }
833                     }
834                     if ( notascii ) {
835                         printf( "\tNOT ASCII (%ld bytes)\n", bvp->bv_len );
836                     } else {
837                         printf( "\t%s\n", bvp->bv_val );
838                     }
839                 }
840             }
841         }
842     }
843
844     if ( newentry ) {
845         printf( "%sadding new entry \"%s\"\n", not ? "!" : "", dn );
846     } else {
847         printf( "%smodifying entry \"%s\"\n", not ? "!" : "", dn );
848     }
849
850     if ( !not ) {
851         if ( newentry ) {
852             i = ldap_add_ext_s( ld, dn, pmods, pctrls, NULL );
853         } else {
854             i = ldap_modify_ext_s( ld, dn, pmods, pctrls, NULL );
855         }
856         if ( i != LDAP_SUCCESS ) {
857                 /* print error message about failed update including DN */
858                 fprintf( stderr, "%s: update failed: %s\n", prog, dn );
859                 ldap_perror( ld, newentry ? "ldap_add" : "ldap_modify" );
860         } else if ( verbose ) {
861             printf( "modify complete\n" );
862         }
863     } else {
864         i = LDAP_SUCCESS;
865     }
866
867     putchar( '\n' );
868
869     return( i );
870 }
871
872
873 static int
874 dodelete(
875         const char *dn,
876     LDAPControl **pctrls )
877 {
878     int rc;
879
880     printf( "%sdeleting entry \"%s\"\n", not ? "!" : "", dn );
881     if ( !not ) {
882         if (( rc = ldap_delete_ext_s( ld, dn, pctrls, NULL )) != LDAP_SUCCESS ) {
883                 fprintf( stderr, "%s: delete failed: %s\n", prog, dn );
884                 ldap_perror( ld, "ldap_delete" );
885         } else if ( verbose ) {
886             printf( "delete complete" );
887         }
888     } else {
889         rc = LDAP_SUCCESS;
890     }
891
892     putchar( '\n' );
893
894     return( rc );
895 }
896
897
898 static int
899 dorename(
900         const char *dn,
901         const char *newrdn,
902         const char* newsup,
903         int deleteoldrdn,
904     LDAPControl **pctrls )
905 {
906     int rc;
907
908
909     printf( "%smodifying rdn of entry \"%s\"\n", not ? "!" : "", dn );
910     if ( verbose ) {
911         printf( "\tnew RDN: \"%s\" (%skeep existing values)\n",
912                 newrdn, deleteoldrdn ? "do not " : "" );
913     }
914     if ( !not ) {
915         if (( rc = ldap_rename_s( ld, dn, newrdn, newsup, deleteoldrdn, pctrls, NULL ))
916                 != LDAP_SUCCESS ) {
917                 fprintf( stderr, "%s: rename failed: %s\n", prog, dn );
918                 ldap_perror( ld, "ldap_modrdn" );
919         } else {
920             printf( "modrdn completed\n" );
921         }
922     } else {
923         rc = LDAP_SUCCESS;
924     }
925
926     putchar( '\n' );
927
928     return( rc );
929 }
930
931
932 static char *
933 read_one_record( FILE *fp )
934 {
935     char        *buf, line[ LDAPMOD_MAXLINE ];
936     int         lcur, lmax;
937
938     lcur = lmax = 0;
939     buf = NULL;
940
941     while ( fgets( line, sizeof(line), fp ) != NULL ) {
942         int len = strlen( line );
943
944                 if( len < 2 || ( len == 2 && *line == '\r' )) {
945                         if( buf == NULL ) {
946                                 continue;
947                         } else {
948                                 break;
949                         }
950                 }
951
952                 if ( lcur + len + 1 > lmax ) {
953                         lmax = LDAPMOD_MAXLINE
954                                 * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
955
956                         if (( buf = (char *)ber_memrealloc( buf, lmax )) == NULL ) {
957                                 perror( "realloc" );
958                                 exit( EXIT_FAILURE );
959                         }
960                 }
961
962                 strcpy( buf + lcur, line );
963                 lcur += len;
964     }
965
966     return( buf );
967 }
968
969