Back to index

lightning-sunbird  0.9+nobinonly
ldapmodify.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /* ldapmodify.c - generic program to modify or add entries using LDAP */
00039 
00040 #include "ldaptool.h"
00041 #include "fileurl.h"
00042 
00043 static int           newval, contoper, force, valsfromfiles, display_binary_values;
00044 static int           ldif_version = -1;   /* -1 => unknown version */
00045 static LDAP          *ld;
00046 static char          *rejfile = NULL;
00047 static char          *bulkimport_suffix = NULL;
00048 static int           ldapmodify_quiet = 0;
00049 
00050 #define LDAPMOD_MAXLINE            4096
00051 
00052 /* strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */
00053 #define T_REPLICA_STR              "replica"
00054 #define T_DN_STR            "dn"
00055 #define T_VERSION_STR              "version"
00056 #define T_CHANGETYPESTR         "changetype"
00057 #define T_ADDCTSTR          "add"
00058 #define T_MODIFYCTSTR              "modify"
00059 #define T_DELETECTSTR              "delete"
00060 #define T_RENAMECTSTR              "rename"      /* non-standard */
00061 #define T_MODDNCTSTR        "moddn"
00062 #define T_MODRDNCTSTR              "modrdn"
00063 #define T_MODOPADDSTR              "add"
00064 #define T_MODOPREPLACESTR   "replace"
00065 #define T_MODOPDELETESTR    "delete"
00066 #define T_MODSEPSTR         "-"
00067 #define T_NEWRDNSTR         "newrdn"
00068 #define       T_NEWSUPERIORSTR     "newsuperior"
00069 #define       T_NEWPARENTSTR              "newparent"
00070 #define T_DELETEOLDRDNSTR   "deleteoldrdn"
00071 #define T_NEWSUPERIORSTR        "newsuperior"
00072 #define T_NEWPARENTSTR          "newparent"      /* non-standard */
00073 
00074 /* bulk import */
00075 #define       BULKIMPORT_START_OID "2.16.840.1.113730.3.5.7"
00076 #define       BULKIMPORT_STOP_OID  "2.16.840.1.113730.3.5.8"
00077 
00078 static void options_callback( int option, char *optarg );
00079 static int process_ldapmod_rec( char *rbuf );
00080 static int process_ldif_rec( char *rbuf );
00081 static void addmodifyop( LDAPMod ***pmodsp, int modop, char *attr,
00082        char *value, int vlen );
00083 static int domodify( char *dn, LDAPMod **pmods, int newentry );
00084 static int dodelete( char *dn );
00085 static int dorename( char *dn, char *newrdn, char *newparent,
00086        int deleteoldrdn );
00087 static void freepmods( LDAPMod **pmods );
00088 static char *read_one_record( FILE *fp );
00089 static char *strdup_and_trim( char *s );
00090 
00091 static void
00092 usage( void )
00093 {
00094     fprintf( stderr, "usage: %s [options]\n", ldaptool_progname );
00095     fprintf( stderr, "options:\n" );
00096     ldaptool_common_usage( 0 );
00097     fprintf( stderr, "    -c\t\tcontinuous mode (do not stop on errors)\n" );
00098     fprintf( stderr, "    -A\t\tdisplay non-ASCII values in conjunction with -v\n" );
00099     fprintf( stderr, "    -f file\tread modifications from file (default: standard input)\n" );
00100     if ( strcmp( ldaptool_progname, "ldapmodify" ) == 0 ){
00101        fprintf( stderr, "    -a\t\tadd entries\n" );
00102     }
00103     fprintf( stderr, "    -b\t\tread values that start with / from files (for bin attrs)\n" );
00104     fprintf( stderr, "    -F\t\tforce application of all changes, regardless of\n" );
00105     fprintf( stderr, "      \t\treplica lines\n" );
00106     fprintf( stderr, "    -e rejfile\tsave rejected entries in \"rejfile\"\n" );
00107     fprintf( stderr, "    -B suffix\tbulk import to \"suffix\"\n");
00108     fprintf( stderr, "    -q\t\tbe quiet when adding/modifying entries\n" );
00109     exit( LDAP_PARAM_ERROR );
00110 }
00111 
00112 
00113 int
00114 main( int argc, char **argv )
00115 {
00116     char      *rbuf, *saved_rbuf, *start, *p, *q;
00117     FILE      *rfp = NULL;
00118     int              rc, use_ldif, deref, optind;
00119     LDAPControl      *ldctrl;
00120 
00121 
00122 #ifdef notdef
00123 #ifdef HPUX11
00124 #ifndef __LP64__
00125        _main( argc, argv);
00126 #endif /* __LP64_ */
00127 #endif /* HPUX11 */
00128 #endif
00129 
00130     valsfromfiles = display_binary_values = 0;
00131 
00132     optind = ldaptool_process_args( argc, argv, "aAbcFe:B:q", 0,
00133            options_callback );
00134 
00135 
00136     if ( optind == -1 ) {
00137        usage();
00138     }
00139 
00140     if ( !newval && strcmp( ldaptool_progname, "ldapadd" ) == 0 ) {
00141        newval = 1;
00142     }
00143 
00144     if ( ldaptool_fp == NULL ) {
00145        ldaptool_fp = stdin;
00146     }
00147 
00148     if ( argc - optind != 0 ) {
00149        usage();
00150     }
00151 
00152     ld = ldaptool_ldap_init( 0 );
00153 
00154     if ( !ldaptool_not ) {
00155        deref = LDAP_DEREF_NEVER;   /* this seems prudent */
00156        ldap_set_option( ld, LDAP_OPT_DEREF, &deref );
00157     }
00158 
00159     ldaptool_bind( ld );
00160 
00161     if (( ldctrl = ldaptool_create_manage_dsait_control()) != NULL ) {
00162        ldaptool_add_control_to_array( ldctrl, ldaptool_request_ctrls);
00163     } 
00164 
00165     if ((ldctrl = ldaptool_create_proxyauth_control(ld)) !=NULL) {
00166        ldaptool_add_control_to_array( ldctrl, ldaptool_request_ctrls);
00167     }
00168 
00169     rc = 0;
00170 
00171     /* turn on bulk import?*/
00172     if (bulkimport_suffix) {
00173        struct berval bv, *retdata;
00174        char          *retoid;
00175 
00176        bv.bv_val = bulkimport_suffix;
00177        bv.bv_len = strlen(bulkimport_suffix);
00178        if ((rc = ldap_extended_operation_s(ld,
00179            BULKIMPORT_START_OID, &bv, NULL,
00180            NULL, &retoid, &retdata)) != 0) {
00181               fprintf(stderr, "Error: unable to service "
00182                   "extended operation request\n\t'%s' for "
00183                   "bulk import\n\t(error:%d:'%s')\n",
00184                   BULKIMPORT_START_OID, rc, ldap_err2string(rc));
00185               return (rc);
00186        }
00187        if (retoid)
00188               ldap_memfree(retoid);
00189        if (retdata)
00190               ber_bvfree(retdata);
00191     }
00192 
00193     while (( rc == 0 || contoper ) &&
00194               ( rbuf = read_one_record( ldaptool_fp )) != NULL ) {
00195        /*
00196         * we assume record is ldif/slapd.replog if the first line
00197         * has a colon that appears to the left of any equal signs, OR
00198         * if the first line consists entirely of digits (an entry id)
00199         */
00200        use_ldif = ( p = strchr( rbuf, ':' )) != NULL &&
00201               ( q = strchr( rbuf, '\n' )) != NULL && p < q &&
00202               (( q = strchr( rbuf, '=' )) == NULL || p < q );
00203 
00204        start = rbuf;
00205        saved_rbuf = strdup( rbuf );
00206 
00207        if ( !use_ldif && ( q = strchr( rbuf, '\n' )) != NULL ) {
00208            for ( p = rbuf; p < q; ++p ) {
00209               if ( !isdigit( *p )) {
00210                   break;
00211               }
00212            }
00213            if ( p >= q ) {
00214               use_ldif = 1;
00215               start = q + 1;
00216            }
00217        }
00218 
00219        if ( use_ldif ) {
00220            rc = process_ldif_rec( start );
00221        } else {
00222            rc = process_ldapmod_rec( start );
00223        }
00224        if ( rc != LDAP_SUCCESS && rejfile != NULL ) {
00225            /* Write this record to the reject file */
00226            int newfile = 0;
00227            struct stat stbuf;
00228            if ( stat( rejfile, &stbuf ) < 0 ) {
00229               if ( errno == ENOENT ) {
00230                   newfile = 1;
00231               }
00232            }
00233            if (( rfp = fopen( rejfile, "a" )) == NULL ) {
00234               fprintf( stderr, "Cannot open error file \"%s\" - "
00235                      "erroneous entries will not be saved\n", rejfile );
00236               rejfile = NULL;
00237            } else {
00238               if ( newfile == 0 ) {
00239                   fputs( "\n", rfp );
00240               }
00241               fprintf( rfp, "# Error: %s\n", ldap_err2string( rc ));
00242               fputs( saved_rbuf, rfp );
00243               fclose( rfp );
00244               rfp = NULL;
00245            }
00246        }
00247 
00248        free( rbuf );
00249        free( saved_rbuf );
00250     }
00251     ldaptool_reset_control_array( ldaptool_request_ctrls );
00252 
00253     /* turn off bulk import?*/
00254     if (bulkimport_suffix) {
00255        struct berval bv, *retdata;
00256        char          *retoid;
00257 
00258        bv.bv_val = "";
00259        bv.bv_len = 0;
00260        if ((rc = ldap_extended_operation_s(ld,
00261            BULKIMPORT_STOP_OID, &bv, NULL,
00262            NULL, &retoid, &retdata)) != 0) {
00263 
00264               fprintf(stderr, "Error: unable to service "
00265                   "extended operation request\n\t '%s' for "
00266                   "bulk import\n\t(rc:%d:'%s')\n",
00267                   BULKIMPORT_STOP_OID, rc, ldap_err2string(rc));
00268               return (rc);
00269        }
00270        if (retoid)
00271               ldap_memfree(retoid);
00272        if (retdata)
00273               ber_bvfree(retdata);
00274     }
00275 
00276     ldaptool_cleanup( ld );
00277     return( rc );
00278 }
00279 
00280 
00281 static void
00282 options_callback( int option, char *optarg )
00283 {
00284     switch( option ) {
00285     case 'a': /* add */
00286        newval = 1;
00287        break;
00288     case 'b': /* read values from files (for binary attributes) */
00289        valsfromfiles = 1;
00290        break;
00291     case 'A': /* display non-ASCII values when -v is used */
00292        display_binary_values = 1;
00293        break;
00294     case 'c': /* continuous operation */
00295        contoper = 1;
00296        break;
00297     case 'F': /* force all changes records to be used */
00298        force = 1;
00299        break;
00300     case 'e':
00301        rejfile = strdup( optarg );
00302        break;
00303     case 'B': /* bulk import option */
00304        bulkimport_suffix = strdup( optarg );
00305        break;
00306     case 'q': /* quiet mode on add/modify operations */
00307        ldapmodify_quiet = 1;
00308        break;
00309     default:
00310        usage();
00311     }
00312 }
00313 
00314 
00315 
00316 static int
00317 process_ldif_rec( char *rbuf )
00318 {
00319     char      *line, *dn, *type, *value, *newrdn, *newparent, *p;
00320     char      *ctrl_oid=NULL, *ctrl_value=NULL;
00321     int       ctrl_criticality=1;
00322     LDAPControl *ldctrl;
00323     int              rc, linenum, vlen, modop, replicaport;
00324     int              expect_modop, expect_sep, expect_chgtype_or_control, expect_newrdn;
00325     int              expect_deleteoldrdn, expect_newparent, rename, moddn;
00326     int              deleteoldrdn, saw_replica, use_record, new_entry, delete_entry;
00327     int         got_all, got_value;
00328     LDAPMod   **pmods;
00329 
00330     new_entry = newval;
00331 
00332     rc = got_all = saw_replica = delete_entry = expect_modop = 0;
00333     expect_deleteoldrdn = expect_newrdn = expect_newparent = expect_sep = 0;
00334     expect_chgtype_or_control = linenum = got_value = rename = moddn = 0;
00335     deleteoldrdn = 1;
00336     use_record = force;
00337     pmods = NULL;
00338     dn = newrdn = newparent = NULL;
00339 
00340     while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
00341        ++linenum;
00342        if ( expect_sep && strcasecmp( line, T_MODSEPSTR ) == 0 ) {
00343            expect_sep = 0;
00344            expect_modop = 1;
00345            
00346            /*If we see a separator in the input stream,
00347             but we didn't get a value from the last modify
00348             then we have to fill pmods with an empty value*/
00349            if (modop == LDAP_MOD_REPLACE && !got_value){
00350              addmodifyop( &pmods, modop, value, NULL, 0);
00351            }
00352 
00353            got_value = 0;
00354            continue;
00355        }
00356        
00357        if ( ldif_parse_line( line, &type, &value, &vlen ) < 0 ) {
00358            fprintf( stderr, "%s: invalid format (line %d of entry: %s)\n",
00359                   ldaptool_progname, linenum, dn == NULL ? "" : dn );
00360            fprintf( stderr, "%s: line contents: (%s)\n",
00361                   ldaptool_progname, line );
00362            rc = LDAP_PARAM_ERROR;
00363            break;
00364        }
00365 
00366 evaluate_line:
00367        if ( dn == NULL ) {
00368            if ( !use_record && strcasecmp( type, T_REPLICA_STR ) == 0 ) {
00369               ++saw_replica;
00370               if (( p = strchr( value, ':' )) == NULL ) {
00371                   replicaport = LDAP_PORT;
00372               } else {
00373                   *p++ = '\0';
00374                   replicaport = atoi( p );
00375               }
00376               if ( strcasecmp( value, ldaptool_host ) == 0 &&
00377                      replicaport == ldaptool_port ) {
00378                   use_record = 1;
00379               }
00380 
00381            } else if ( strcasecmp( type, T_DN_STR ) == 0 ) {
00382               if (( dn = strdup( value )) == NULL ) {
00383                   perror( "strdup" );
00384                   exit( LDAP_NO_MEMORY );
00385               }
00386               expect_chgtype_or_control = 1;
00387 
00388            } else if ( strcasecmp( type, T_VERSION_STR ) == 0 ) {
00389               ldif_version = atoi( value );
00390               if ( ldif_version != LDIF_VERSION_ONE ) {
00391                   fprintf( stderr, "%s:  LDIF version %d is not supported;"
00392                      " use version: %d\n", ldaptool_progname, ldif_version,
00393                      LDIF_VERSION_ONE );
00394                   exit( LDAP_PARAM_ERROR );
00395               }
00396               if ( ldaptool_verbose ) {
00397                   printf( "Processing a version %d LDIF file...\n",
00398                          ldif_version );
00399               }
00400               
00401               /* Now check if there's something left to process   */
00402               /* and if not, go get the new record, else continue */
00403               if ( *rbuf == '\0' ) {
00404                      return( 0 );
00405               }
00406 
00407            } else if ( !saw_replica ) {
00408               printf( "%s: skipping change record: no dn: line\n",
00409                      ldaptool_progname );
00410               return( 0 );
00411            }
00412 
00413            continue; /* skip all lines until we see "dn:" */
00414        }
00415 
00416        if ( expect_chgtype_or_control ) {
00417            expect_chgtype_or_control = 0;
00418            if ( !use_record && saw_replica ) {
00419               printf( "%s: skipping change record for entry: %s\n\t(LDAP host/port does not match replica: lines)\n",
00420                      ldaptool_progname, dn );
00421               free( dn );
00422               return( 0 );
00423            }
00424 
00425            if ( strcasecmp( type, "control" ) == 0 ) {
00426               value = strdup_and_trim( value );
00427               if (ldaptool_parse_ctrl_arg(value, ' ', &ctrl_oid, 
00428                      &ctrl_criticality, &ctrl_value, &vlen)) {
00429                          usage();
00430               }
00431               ldctrl = calloc(1,sizeof(LDAPControl));
00432               if (ctrl_value) {
00433                   rc = ldaptool_berval_from_ldif_value( ctrl_value, vlen,
00434                       &(ldctrl->ldctl_value),
00435                       1 /* recognize file URLs */, 0 /* always try file */,
00436                       1 /* report errors */ );
00437                   if ((rc = ldaptool_fileurlerr2ldaperr( rc )) != LDAP_SUCCESS) {
00438                      fprintf( stderr, "Unable to parse %s\n", ctrl_value);
00439                      usage();
00440                   }
00441               }
00442               ldctrl->ldctl_oid = ctrl_oid;
00443               ldctrl->ldctl_iscritical = ctrl_criticality;
00444               ldaptool_add_control_to_array(ldctrl, ldaptool_request_ctrls);
00445               expect_chgtype_or_control = 1;
00446               continue;
00447            }
00448 
00449            if ( strcasecmp( type, T_CHANGETYPESTR ) == 0 ) {
00450               value = strdup_and_trim( value );
00451               if ( strcasecmp( value, T_MODIFYCTSTR ) == 0 ) {
00452                   new_entry = 0;
00453                   expect_modop = 1;
00454               } else if ( strcasecmp( value, T_ADDCTSTR ) == 0 ) {
00455                   new_entry = 1;
00456                   modop = LDAP_MOD_ADD;
00457               } else if ( strcasecmp( value, T_MODRDNCTSTR ) == 0 ) {
00458                   expect_newrdn = 1;
00459                   moddn = 1;
00460               } else if ( strcasecmp( value, T_MODDNCTSTR ) == 0 ) {
00461                   expect_newrdn = 1;
00462                   moddn = 1;
00463               } else if ( strcasecmp( value, T_RENAMECTSTR ) == 0 ) {
00464                   expect_newrdn = 1;
00465                   rename = 1;
00466               } else if ( strcasecmp( value, T_DELETECTSTR ) == 0 ) {
00467                   got_all = delete_entry = 1;
00468               } else {
00469                   fprintf( stderr,
00470                          "%s:  unknown %s \"%s\" (line %d of entry: %s)\n",
00471                          ldaptool_progname, T_CHANGETYPESTR, value,
00472                          linenum, dn );
00473                   rc = LDAP_PARAM_ERROR;
00474               }
00475               free( value );
00476               continue;
00477            } else if ( newval ) {         /*  missing changetype => add */
00478               new_entry = 1;
00479               modop = LDAP_MOD_ADD;
00480            } else {
00481              /*The user MUST put in changetype: blah
00482               unless adding a new entry with either -a or ldapadd*/
00483               fprintf(stderr, "%s: Missing changetype operation specification.\n\tThe dn line must be followed by \"changetype: operation\"\n\t(unless ldapmodify is called with -a option)\n\twhere operation is add|delete|modify|modrdn|moddn|rename\n\t\"%s\" is not a valid changetype operation specification\n\t(line %d of entry %s)\n", 
00484               ldaptool_progname, type, linenum, dn);
00485               rc = LDAP_PARAM_ERROR;
00486               /*expect_modop = 1;   missing changetype => modify */
00487            }
00488        }
00489 
00490        if ( expect_modop ) {
00491            expect_modop = 0;
00492            expect_sep = 1;
00493            if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) {
00494               modop = LDAP_MOD_ADD;
00495               continue;
00496            } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) {
00497               modop = LDAP_MOD_REPLACE;
00498               continue;
00499            } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) {
00500               modop = LDAP_MOD_DELETE;
00501               addmodifyop( &pmods, modop, value, NULL, 0 );
00502               continue;
00503            }  else { /*Bug 27479. Remove default add operation*/ 
00504              fprintf(stderr, "%s: Invalid parameter \"%s\" specified for changetype modify (line %d of entry %s)\n", 
00505                     ldaptool_progname, type, linenum, dn);
00506              rc = LDAP_PARAM_ERROR;
00507            }
00508 
00509          }
00510 
00511        if ( expect_newrdn ) {
00512            if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) {
00513               if ( *value == '\0' ) {
00514                   fprintf( stderr,
00515                          "%s: newrdn value missing (line %d of entry: %s)\n",
00516                          ldaptool_progname, linenum, dn == NULL ? "" : dn );
00517                   rc = LDAP_PARAM_ERROR;
00518               } else if (( newrdn = strdup( value )) == NULL ) {
00519                   perror( "strdup" );
00520                   exit( LDAP_NO_MEMORY );
00521               } else {
00522                   expect_newrdn = 0;
00523                   if ( rename ) {
00524                      expect_newparent = 1;
00525                   } else {
00526                      expect_deleteoldrdn = 1;
00527                   }
00528               }
00529            } else {
00530               fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n",
00531                      ldaptool_progname, T_NEWRDNSTR, type, linenum, dn );
00532               rc = LDAP_PARAM_ERROR;
00533            }
00534        } else if ( expect_newparent ) {
00535            expect_newparent = 0;
00536            if ( rename ) {
00537               expect_deleteoldrdn = 1;
00538            }
00539            if ( strcasecmp( type, T_NEWPARENTSTR ) == 0
00540                   || strcasecmp( type, T_NEWSUPERIORSTR ) == 0 ) {
00541               if (( newparent = strdup( value )) == NULL ) {
00542                   perror( "strdup" );
00543                   exit( LDAP_NO_MEMORY );
00544               }
00545            } else {
00546               /* Since this is an optional argument for rename/moddn, cause
00547                * the current line to be re-evaluated if newparent doesn't
00548                * follow deleteoldrdn.
00549                */
00550               newparent = NULL;  
00551               goto evaluate_line;
00552            }
00553        } else if ( expect_deleteoldrdn ) {
00554            if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) {
00555               if ( *value == '\0' ) {
00556                   fprintf( stderr,
00557                          "%s: missing 0 or 1 (line %d of entry: %s)\n",
00558                          ldaptool_progname, linenum, dn == NULL ? "" : dn );
00559                   rc = LDAP_PARAM_ERROR;
00560               } else {
00561                   deleteoldrdn = ( *value == '0' ) ? 0 : 1;
00562                   expect_deleteoldrdn = 0;
00563                   if ( moddn ) {
00564                      expect_newparent = 1;
00565                   }
00566               }
00567            } else {
00568               fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n",
00569                      ldaptool_progname, T_DELETEOLDRDNSTR, type, linenum,
00570                      dn );
00571               rc = LDAP_PARAM_ERROR;
00572            }
00573            got_all = 1;
00574        } else if ( got_all ) {
00575            fprintf( stderr,
00576                   "%s: extra lines at end (line %d of entry %s)\n",
00577                   ldaptool_progname, linenum, dn );
00578            rc = LDAP_PARAM_ERROR;
00579            got_all = 1;
00580        } else {
00581            addmodifyop( &pmods, modop, type, value, vlen );
00582            /*There was a value to replace*/
00583            got_value = 1;
00584 
00585        }
00586     }
00587 
00588     if ( rc == 0 ) {
00589        if ( delete_entry ) {
00590            rc = dodelete( dn );
00591        } else if ( newrdn != NULL ) {
00592            rc = dorename( dn, newrdn, newparent, deleteoldrdn );
00593            rename = 0;
00594        } else {
00595 
00596          /*Patch to fix Bug 22183
00597            If pmods is null, then there is no
00598            attribute to replace, so we alloc
00599            an empty pmods*/
00600          if (modop == LDAP_MOD_REPLACE && !got_value && expect_sep){
00601            addmodifyop( &pmods, modop, value, NULL, 0);
00602          }/*End Patch*/
00603          
00604          
00605          rc = domodify( dn, pmods, new_entry );
00606        }
00607 
00608        if ( rc == LDAP_SUCCESS ) {
00609            rc = 0;
00610        }
00611     }
00612 
00613     if ( dn != NULL ) {
00614        free( dn );
00615     }
00616     if ( newrdn != NULL ) {
00617        free( newrdn );
00618     }
00619     if ( newparent != NULL ) {
00620        free( newparent );
00621     }
00622     if ( pmods != NULL ) {
00623        freepmods( pmods );
00624     }
00625 
00626     return( rc );
00627 }
00628 
00629 
00630 static int
00631 process_ldapmod_rec( char *rbuf )
00632 {
00633     char      *line, *dn, *p, *q, *attr, *value;
00634     int              rc, linenum, modop;
00635     LDAPMod   **pmods;
00636 
00637     pmods = NULL;
00638     dn = NULL;
00639     linenum = 0;
00640     line = rbuf;
00641     rc = 0;
00642 
00643     while ( rc == 0 && rbuf != NULL && *rbuf != '\0' ) {
00644        ++linenum;
00645        if (( p = strchr( rbuf, '\n' )) == NULL ) {
00646            rbuf = NULL;
00647        } else {
00648            if ( *(p-1) == '\\' ) { /* lines ending in '\' are continued */
00649               strcpy( p - 1, p );
00650               rbuf = p;
00651               continue;
00652            }
00653            *p++ = '\0';
00654            rbuf = p;
00655        }
00656 
00657        if ( dn == NULL ) {  /* first line contains DN */
00658            if (( dn = strdup( line )) == NULL ) {
00659               perror( "strdup" );
00660               exit( LDAP_NO_MEMORY );
00661            }
00662        } else {
00663            if (( p = strchr( line, '=' )) == NULL ) {
00664               value = NULL;
00665               p = line + strlen( line );
00666            } else {
00667               *p++ = '\0';
00668               value = p;
00669            }
00670 
00671            for ( attr = line; *attr != '\0' && isspace( *attr ); ++attr ) {
00672               ;      /* skip attribute leading white space */
00673            }
00674 
00675            for ( q = p - 1; q > attr && isspace( *q ); --q ) {
00676               *q = '\0';    /* remove attribute trailing white space */
00677            }
00678 
00679            if ( value != NULL ) {
00680               while ( isspace( *value )) {
00681                   ++value;         /* skip value leading white space */
00682               }
00683               for ( q = value + strlen( value ) - 1; q > value &&
00684                      isspace( *q ); --q ) {
00685                   *q = '\0';       /* remove value trailing white space */
00686               }
00687               if ( *value == '\0' ) {
00688                   value = NULL;
00689               }
00690 
00691            }
00692 
00693            if ( value == NULL && newval ) {
00694               fprintf( stderr, "%s: missing value on line %d (attr is %s)\n",
00695                      ldaptool_progname, linenum, attr );
00696               rc = LDAP_PARAM_ERROR;
00697            } else {
00698                switch ( *attr ) {
00699               case '-':
00700                   modop = LDAP_MOD_DELETE;
00701                   ++attr;
00702                   break;
00703               case '+':
00704                   modop = LDAP_MOD_ADD;
00705                   ++attr;
00706                   break;
00707               default:
00708                   /*Bug 27479. Remove the add default*/
00709                     fprintf(stderr, "%s: Invalid parameter specified for changetype modify (line %d of entry %s)\n", 
00710                     ldaptool_progname, linenum, dn);
00711                     rc = LDAP_PARAM_ERROR;
00712               }
00713 
00714               addmodifyop( &pmods, modop, attr, value,
00715                      ( value == NULL ) ? 0 : strlen( value ));
00716            }
00717        }
00718 
00719        line = rbuf;
00720     }
00721 
00722     if ( rc == 0 ) {
00723        if ( dn == NULL ) {
00724            rc = LDAP_PARAM_ERROR;
00725        } else if (( rc = domodify( dn, pmods, newval )) == LDAP_SUCCESS ){
00726          rc = 0;
00727        }
00728       }
00729     
00730     if ( pmods != NULL ) {
00731        freepmods( pmods );
00732     }
00733     if ( dn != NULL ) {
00734        free( dn );
00735     }
00736 
00737     return( rc );
00738 }
00739 
00740 
00741 static void
00742 addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen )
00743 {
00744     LDAPMod          **pmods;
00745     int                     i, j, rc;
00746     struct berval    *bvp;
00747 
00748     pmods = *pmodsp;
00749     modop |= LDAP_MOD_BVALUES;
00750 
00751     i = 0;
00752     if ( pmods != NULL ) {
00753        for ( ; pmods[ i ] != NULL; ++i ) {
00754            if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
00755                   pmods[ i ]->mod_op == modop ) {
00756               break;
00757            }
00758        }
00759     }
00760 
00761     if ( pmods == NULL || pmods[ i ] == NULL ) {
00762        if (( pmods = (LDAPMod **)LDAPTOOL_SAFEREALLOC( pmods, (i + 2) *
00763               sizeof( LDAPMod * ))) == NULL ) {
00764            perror( "realloc" );
00765            exit( LDAP_NO_MEMORY );
00766        }
00767        *pmodsp = pmods;
00768        pmods[ i + 1 ] = NULL;
00769        if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod )))
00770               == NULL ) {
00771            perror( "calloc" );
00772            exit( LDAP_NO_MEMORY );
00773        }
00774        pmods[ i ]->mod_op = modop;
00775        if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) {
00776            perror( "strdup" );
00777            exit( LDAP_NO_MEMORY );
00778        }
00779     }
00780 
00781     if ( value != NULL ) {
00782        j = 0;
00783        if ( pmods[ i ]->mod_bvalues != NULL ) {
00784            for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
00785               ;
00786            }
00787        }
00788        if (( pmods[ i ]->mod_bvalues = (struct berval **)
00789               LDAPTOOL_SAFEREALLOC( pmods[ i ]->mod_bvalues,
00790               (j + 2) * sizeof( struct berval * ))) == NULL ) {
00791            perror( "realloc" );
00792            exit( LDAP_NO_MEMORY );
00793        }
00794        pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
00795        if (( bvp = (struct berval *)malloc( sizeof( struct berval )))
00796               == NULL ) {
00797            perror( "malloc" );
00798            exit( LDAP_NO_MEMORY );
00799        }
00800        pmods[ i ]->mod_bvalues[ j ] = bvp;
00801 
00802        rc = ldaptool_berval_from_ldif_value( value, vlen, bvp,
00803                   ( ldif_version >= LDIF_VERSION_ONE ), valsfromfiles,
00804                      1 /* report errors */ );
00805        if ( rc != LDAPTOOL_FILEURL_SUCCESS ) {
00806            exit( ldaptool_fileurlerr2ldaperr( rc ));
00807        }
00808     }
00809 }
00810 
00811 
00812 static int
00813 domodify( char *dn, LDAPMod **pmods, int newentry )
00814 {
00815     int                     i, j, notascii, op;
00816     struct berval    *bvp;
00817 
00818     if ( pmods == NULL ) {
00819        fprintf( stderr, "%s: no attributes to change or add (entry %s)\n",
00820               ldaptool_progname, dn );
00821        return( LDAP_PARAM_ERROR );
00822     }
00823 
00824     if ( ldaptool_verbose ) {
00825        for ( i = 0; pmods[ i ] != NULL; ++i ) {
00826            op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
00827            printf( "%s %s:\n", op == LDAP_MOD_REPLACE ?
00828                   "replace" : op == LDAP_MOD_ADD ?
00829                   "add" : "delete", pmods[ i ]->mod_type );
00830            if ( pmods[ i ]->mod_bvalues != NULL ) {
00831               for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
00832                   bvp = pmods[ i ]->mod_bvalues[ j ];
00833                   notascii = 0;
00834                   if ( !display_binary_values ) {
00835                      notascii = !ldaptool_berval_is_ascii( bvp );
00836                   }
00837                   if ( notascii ) {
00838                      printf( "\tNOT ASCII (%ld bytes)\n", bvp->bv_len );
00839                   } else {
00840                      printf( "\t%s\n", bvp->bv_val );
00841                   }
00842               }
00843            }
00844        }
00845     }
00846 
00847     if ( !ldapmodify_quiet) {
00848        if ( newentry ) {
00849            printf( "%sadding new entry %s\n",
00850               ldaptool_not ? "!" : "", dn );
00851        } else {
00852            printf( "%smodifying entry %s\n",
00853               ldaptool_not ? "!" : "", dn );
00854        }
00855     }
00856 
00857     if ( !ldaptool_not ) {
00858        if ( newentry ) {
00859        unsigned int  sleep_interval = 2; /* seconds */
00860 
00861            while ((i = ldaptool_add_ext_s( ld, dn, pmods,
00862                      ldaptool_request_ctrls, NULL, "ldap_add" ))
00863                      == LDAP_BUSY) {
00864               if ( sleep_interval > 3600 ) {
00865                      printf("ldap_add: Unable to complete request.  ");
00866                      printf("Server is too ");
00867                      printf("busy servicing other requests\n");
00868                      break;
00869               }
00870               if ( !ldapmodify_quiet ) {
00871                      printf("ldap_add: LDAP_BUSY returned by server.  ");
00872                      printf("Will retry operation ");
00873                      printf("in %d seconds\n", sleep_interval); 
00874               }
00875               sleep( sleep_interval );
00876               sleep_interval *= 2;
00877            }
00878        } else {
00879            i = ldaptool_modify_ext_s( ld, dn, pmods, ldaptool_request_ctrls,
00880                   NULL, "ldap_modify" );
00881        }
00882        if ( i == LDAP_SUCCESS && ldaptool_verbose ) {
00883            printf( "modify complete\n" );
00884        }
00885     } else {
00886        i = LDAP_SUCCESS;
00887     }
00888 
00889     if ( !ldapmodify_quiet) {
00890        putchar( '\n' );
00891     }
00892 
00893     return( i );
00894 }
00895 
00896 
00897 static int
00898 dodelete( char *dn )
00899 {
00900     int       rc;
00901 
00902     printf( "%sdeleting entry %s\n", ldaptool_not ? "!" : "", dn );
00903     if ( !ldaptool_not ) {
00904        if (( rc = ldaptool_delete_ext_s( ld, dn, ldaptool_request_ctrls,
00905               NULL, "ldap_delete" )) == LDAP_SUCCESS && ldaptool_verbose ) {
00906            printf( "delete complete" );
00907        }
00908     } else {
00909        rc = LDAP_SUCCESS;
00910     }
00911 
00912     putchar( '\n' );
00913 
00914     return( rc );
00915 }
00916 
00917 
00918 static int
00919 dorename( char *dn, char *newrdn, char *newparent, int deleteoldrdn )
00920 {
00921     int       rc;
00922 
00923     if ( ldaptool_verbose ) {
00924        if ( newparent == NULL ) {
00925            printf( "new RDN: %s (%skeep existing values)\n",
00926                   newrdn, deleteoldrdn ? "do not " : "" );
00927        } else {
00928            printf( "new RDN: %s, new parent %s (%skeep existing values)\n",
00929                   newrdn, newparent, deleteoldrdn ? "do not " : "" );
00930        }
00931     }
00932 
00933     printf( "%smodifying RDN of entry %s%s\n",
00934            ldaptool_not ? "!" : "", dn, ( newparent == NULL ) ? "" :
00935            " and/or moving it beneath a new parent\n" );
00936 
00937     if ( !ldaptool_not ) {
00938        if (( rc = ldaptool_rename_s( ld, dn, newrdn, newparent, deleteoldrdn,
00939               ldaptool_request_ctrls, NULL, "ldap_rename" )) == LDAP_SUCCESS
00940               && ldaptool_verbose ) {
00941            printf( "rename completed\n" );
00942        }
00943     } else {
00944        rc = LDAP_SUCCESS;
00945     }
00946 
00947     putchar( '\n' );
00948 
00949     return( rc );
00950 }
00951 
00952 
00953 static void
00954 freepmods( LDAPMod **pmods )
00955 {
00956     int       i;
00957 
00958     for ( i = 0; pmods[ i ] != NULL; ++i ) {
00959        if ( pmods[ i ]->mod_bvalues != NULL ) {
00960            ber_bvecfree( pmods[ i ]->mod_bvalues );
00961        }
00962        if ( pmods[ i ]->mod_type != NULL ) {
00963            free( pmods[ i ]->mod_type );
00964        }
00965        free( pmods[ i ] );
00966     }
00967     free( pmods );
00968 }
00969 
00970 
00971 static char *
00972 read_one_record( FILE *fp )
00973 {
00974     int         len, gotnothing;
00975     char        *buf, line[ LDAPMOD_MAXLINE ];
00976     int              lcur, lmax;
00977 
00978     lcur = lmax = 0;
00979     buf = NULL;
00980     gotnothing = 1;
00981 
00982     while ( fgets( line, sizeof(line), fp ) != NULL ) {
00983        if ( (len = strlen( line )) < 2 ) {
00984            if ( gotnothing ) {
00985               continue;
00986            } else {
00987               break;
00988            }
00989        }
00990 
00991        /* Check if the blank line starts with '\r' (CR) */
00992        if ( ((len = strlen( line )) == 2) && (line[0] == '\r') ) {
00993            if ( gotnothing ) {
00994               continue;
00995            } else {
00996               break; 
00997              }
00998        }
00999 
01000        if ( *line == '#' ) {
01001            continue;               /* skip comment lines */
01002        }
01003 
01004        gotnothing = 0;
01005         if ( lcur + len + 1 > lmax ) {
01006             lmax = LDAPMOD_MAXLINE
01007                   * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
01008            if (( buf = (char *)LDAPTOOL_SAFEREALLOC( buf, lmax )) == NULL ) {
01009               perror( "realloc" );
01010               exit( LDAP_NO_MEMORY );
01011            }
01012         }
01013         strcpy( buf + lcur, line );
01014         lcur += len;
01015     }
01016 
01017     return( buf );
01018 }
01019 
01020 
01021 /*
01022  * strdup and trim trailing blanks
01023  */
01024 static char *
01025 strdup_and_trim( char *s )
01026 {
01027     char      *p;
01028 
01029     if (( s = strdup( s )) == NULL ) {
01030        perror( "strdup" );
01031        exit( LDAP_NO_MEMORY );
01032     }
01033 
01034     p = s + strlen( s ) - 1;
01035     while ( p >= s && isspace( *p )) {
01036        --p;
01037     }
01038     *++p = '\0';
01039 
01040     return( s );
01041 }