>From 35a59f31cd0897e5ea66ebac5271598cfe97f22a Mon Sep 17 00:00:00 2001
From: Yi Zhang <yzhang@redhat.com>
Date: Mon, 2 Nov 2009 15:09:40 -0800
Subject: [PATCH] 459181 - Add attreplacefile option to ldclt

This option will accept format like "-e attreplacefile=jpegPhoto:/some/binary.file"
to ldclt. The content of the given file will be used to replace the attribute
"jpegPhoto" (in this case). The given file could be plain text or binary file.
---
 ldap/servers/slapd/tools/ldclt/ldapfct.c    |  240 +++++++++++++++++++++++++-
 ldap/servers/slapd/tools/ldclt/ldclt.c      |  100 +++++++++++
 ldap/servers/slapd/tools/ldclt/ldclt.h      |    8 +-
 ldap/servers/slapd/tools/ldclt/ldcltU.c     |    1 +
 ldap/servers/slapd/tools/ldclt/threadMain.c |   25 +++
 5 files changed, 362 insertions(+), 12 deletions(-)

diff --git a/ldap/servers/slapd/tools/ldclt/ldapfct.c b/ldap/servers/slapd/tools/ldclt/ldapfct.c
index 74b1812..c067f26 100644
--- a/ldap/servers/slapd/tools/ldclt/ldapfct.c
+++ b/ldap/servers/slapd/tools/ldclt/ldapfct.c
@@ -1349,18 +1349,22 @@ int
 freeAttrib (
 	LDAPMod	**attrs)
 {
-  int	 i;
-  for (i=0 ; attrs[i]!=NULL ; i++)
-  {
-    if (attrs[i]->mod_op & LDAP_MOD_BVALUES)
-    {
-      free (attrs[i]->mod_bvalues[0]);
+  int	i;
+  int	j;
+
+  for (i=0 ; attrs[i]!=NULL ; i++) {
+    if (attrs[i]->mod_op & LDAP_MOD_BVALUES) {
+      for (j=0; attrs[i]->mod_bvalues[j] != NULL; j++) {
+        free (attrs[i]->mod_bvalues[j]);
+      }
       free (attrs[i]->mod_bvalues);
-    }
-    else
+    } else {
       free (attrs[i]->mod_values);
+    }
+
     free (attrs[i]);
   }
+
   return (0);
 }
 
@@ -1478,7 +1482,70 @@ printErrorFromLdap (
 
 
 
-					/* New function */	/*JLS 21-11-00*/
+
+
+
+
+/* ****************************************************************************
+	FUNCTION :	buildNewModAttribFile
+	PURPOSE :	Build a new (random or incremental) target DN and the
+			corresponding LDAPMod for attribute modification.
+	INPUT :		tttctx	= thread context
+	OUTPUT :	newDN	= DN of the new entry
+			attrs	= attributes for the ldap_modify
+	RETURN :	-1 if error, 0 else.
+ *****************************************************************************/
+int
+buildNewModAttribFile (
+	thread_context	 *tttctx,
+	char		 *newDn,
+	LDAPMod		**attrs)
+{
+  int		 nbAttribs;	/* Nb of attributes */
+  LDAPMod	 attribute;	/* To build the attributes */
+  struct berval	*bv = malloc(sizeof(struct berval *));
+  attribute.mod_bvalues = (struct berval **)malloc(2 * sizeof(struct berval *));
+
+  if ((bv == NULL) || (attribute.mod_bvalues == NULL)) {
+    return -1;
+  }
+
+  /*
+   * Build the new DN
+   * We will assume that the filter (-f argument) is set to use it
+   * to build the rdn of the new entry.
+   * Note that the random new attribute is also build by this function.
+   */
+  if (buildRandomRdnOrFilter (tttctx) < 0)
+    return (-1);
+  strcpy (newDn, tttctx->bufFilter);
+  strcat (newDn, ",");
+  strcat (newDn, tttctx->bufBaseDN);
+
+  /*
+   * Build the attributes modification
+   */
+  bv->bv_len = mctx.attrplFileSize;
+  bv->bv_val = mctx.attrplFileContent;
+  attrs[0]  = NULL;	/* No attributes yet */
+  nbAttribs = 0;	/* No attributes yet */
+  attribute.mod_op     = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+  attribute.mod_type   = mctx.attrplName;
+  attribute.mod_bvalues[0] = bv;
+  attribute.mod_bvalues[1] = NULL;
+
+  if (addAttrib (attrs, nbAttribs++, &attribute) < 0)
+    return (-1);
+
+  /*
+   * Normal end
+   */
+  return (0);
+}
+
+
+
+/* New function */	/*JLS 21-11-00*/
 /* ****************************************************************************
 	FUNCTION :	buildNewModAttrib
 	PURPOSE :	Build a new (random or incremental) target DN and the
@@ -3050,7 +3117,6 @@ doAddEntry (
 
 
 
-
 /* ****************************************************************************
 	FUNCTION :	doAttrReplace
 	PURPOSE :	Perform an ldap_modify() operation, to replace an
@@ -3207,7 +3273,161 @@ doAttrReplace (
 
 
 
+/* ****************************************************************************
+	FUNCTION :	doAttrFileReplace
+	PURPOSE :	Perform an ldap_modify() operation, to replace an
+			attribute of the entry with content read from file .
+	INPUT :		tttctx	= thread context
+	OUTPUT :	None.
+	RETURN :	-1 if error, 0 else.
+	DESCRIPTION :
+ *****************************************************************************/
+int
+doAttrFileReplace (
+	thread_context	*tttctx)
+{
+  char		 newDn[MAX_DN_LENGTH];	/* DN of the new entry */
+  LDAPMod	*attrs[MAX_ATTRIBS];	/* Attributes of this entry */
+  int		 ret;			/* Return values */
+  int		 msgid;			/* For asynchronous mode */
+
+  /*
+   * Connection to the server
+   * The function connectToServer() will take care of the various connection/
+   * disconnection, bind/unbind/close etc... requested by the user.
+   * The cost is one more function call in this application, but the
+   * resulting source code will be much more easiest to maintain.
+   */
+  if (connectToServer (tttctx) < 0)	/* if connection is being established, */
+    return (-1);			/* then tttctx->ldapCtx would exist and holds connection */
+  if (!(tttctx->binded))
+    return (0);
+
+  /*
+   * Do the modify
+   * Maybe we are in synchronous mode ?
+   */
+  if (!(mctx.mode & ASYNC))
+  {
+    /*
+     * Build the new entry
+     */
+    if (buildNewModAttribFile (tttctx, newDn, attrs) < 0)
+      return (-1);
 
+    /*
+     * We will modify this entry
+     */
+    ret = ldap_modify_ext_s (tttctx->ldapCtx, newDn, attrs, NULL, NULL);
+    if (ret != LDAP_SUCCESS)
+    {
+      if (!((mctx.mode & QUIET) && ignoreError (ret)))
+      {
+	printf ("ldclt[%d]: T%03d: AttriFileReplace Error Cannot modify (%s), error=%d (%s)\n",
+		mctx.pid, tttctx->thrdNum, newDn, ret, my_ldap_err2string (ret));
+	fflush (stdout);
+      }
+      if (addErrorStat (ret) < 0)
+	return (-1);
+    }
+    else
+    {
+      printf ("ldclt[%d]: T%03d: AttriFileReplace modify (%s) success ,\n",
+		mctx.pid, tttctx->thrdNum, newDn);
+      if (incrementNbOpers (tttctx) < 0)	/* Memorize operation */
+	return (-1);
+    }
+
+    /*
+     * Free the attributes
+     */
+    if (freeAttrib (attrs) < 0)
+      return (-1);
+
+    /*
+     * End of synchronous operations
+     */
+    return (0);
+  }
+
+  /*
+   * Here, we are in asynchronous mode...
+   * Too bad, lot of things to do here.
+   * First, let's see if we are above the reading threshold.
+   */
+  if (getPending (tttctx, &(mctx.timeval)) < 0)
+    return (-1);
+
+  /*
+   * Maybe we may send another request ?
+   * Well... there is no proper way to retrieve the error number for
+   * this, so I guess I may use direct access to the ldap context
+   * to read the field ld_errno.
+   */
+  if (tttctx->pendingNb > mctx.asyncMax)
+  {
+    if ((mctx.mode & VERBOSE)   &&
+	(tttctx->asyncHit == 1) &&
+	(!(mctx.mode & SUPER_QUIET)))
+    {
+      tttctx->asyncHit = 1;
+      printf ("ldclt[%d]: T%03d: Max pending request hit.\n",
+               mctx.pid, tttctx->thrdNum);
+      fflush (stdout);
+    }
+  }
+  else
+  {
+    if ((mctx.mode & VERBOSE)   &&
+	(tttctx->asyncHit == 1) &&
+	(!(mctx.mode & SUPER_QUIET)))
+    {
+      tttctx->asyncHit = 0;
+      printf ("ldclt[%d]: T%03d: Restart sending.\n",
+               mctx.pid, tttctx->thrdNum);
+      fflush (stdout);
+    }
+
+    /*
+     * Build the new entry
+     */
+    if (buildNewModAttrib (tttctx, newDn, attrs) < 0)
+      return (-1);
+
+    ret = ldap_modify_ext (tttctx->ldapCtx, newDn, attrs, NULL, NULL, &msgid);
+    if (ret != LDAP_SUCCESS)
+    {
+      if (!((mctx.mode & QUIET) && ignoreError (ret)))
+      {
+	printf ("ldclt[%d]: T%03d: Cannot modify(%s), error=%d (%s)\n",
+		mctx.pid, tttctx->thrdNum, newDn, ret, my_ldap_err2string (ret));
+	fflush (stdout);
+      }
+      if (addErrorStat (ret) < 0)
+	return (-1);
+    }
+    else
+    {
+      /*
+       * Memorize the operation
+       */
+      if (msgIdAdd (tttctx, msgid, newDn, newDn, attrs) < 0)
+	return (-1);
+      if (incrementNbOpers (tttctx) < 0)
+	return (-1);
+      tttctx->pendingNb++;
+    }
+  }
+
+  if (mctx.mode & VERY_VERBOSE)
+    printf ("ldclt[%d]: T%03d: pendingNb=%d\n",
+             mctx.pid, tttctx->thrdNum, tttctx->pendingNb);
+
+  /*
+   * End of asynchronous operation... and also end of function.
+   */
+  return (0);
+}
 
 
 /* ****************************************************************************
diff --git a/ldap/servers/slapd/tools/ldclt/ldclt.c b/ldap/servers/slapd/tools/ldclt/ldclt.c
index 753b214..05774c0 100644
--- a/ldap/servers/slapd/tools/ldclt/ldclt.c
+++ b/ldap/servers/slapd/tools/ldclt/ldclt.c
@@ -1267,6 +1267,10 @@ basicInit (void)
   int		 i;	/* For the loops */			/*JLS 21-11-00*/
   int		 ret;	/* Return value */
   int		 oflags;/* open() flags */			/*JLS 05-04-01*/
+  struct stat file_st ; /* file status checker for attreplacefile option */
+  FILE *attrF;		/* file pointer for attreplacefile option */
+  int	buffersize=1024;	/* buffer size for buffer */
+  char	buffer[buffersize];	/* buffer used to read attreplacefile content */
 
   /*
    * Misc inits
@@ -1471,6 +1475,86 @@ basicInit (void)
     }								/*JLS 21-11-00*/
   }								/*JLS 21-11-00*/
 
+  /*
+   * Parse attreplacefile subvalue
+   */
+  if (mctx.mod2 & M2_ATTR_REPLACE_FILE)
+  {
+    printf ("debug: parse attreplacefile subvalue\n");
+    /*
+     * Find the attribute name
+     */
+    for (i=0 ; (i<strlen(mctx.attrpl)) &&
+                        (mctx.attrpl[i]!=':') ; i++);
+    mctx.attrplName = (char *)malloc(i+1);
+    strncpy (mctx.attrplName, mctx.attrpl, i);
+    mctx.attrplName[i] = '\0';
+
+    /*
+     * Parse the attribute value
+     */
+    mctx.attrplFile = (char *)malloc(strlen(mctx.attrpl+i+1) + 1);
+    if (mctx.attrplFile == NULL) {
+      printf ("Error: unable to allocate memory for attreplfile\n");
+      return (-1);
+    }
+
+    strncpy(mctx.attrplFile, mctx.attrpl+i+1, strlen(mctx.attrpl+i+1));
+    mctx.attrplFile[strlen(mctx.attrpl+i+1)] = '\0';
+
+   /* 
+    * start working on file verification here 
+      (1) check whether file exist 
+      (2) check whether we have permission to read it 
+      (3) save the content into mctx.attrplFileContent 
+    */
+
+    /* determine file size here */
+    if (stat(mctx.attrplFile, &file_st) < 0){
+      printf ("attr replace file [%s] does not exist, exit\n", mctx.attrplFile);
+      return (-1);
+    }else{
+      mctx.attrplFileSize = file_st.st_size;
+      printf ("file has size [%d] bytes\n", mctx.attrplFileSize );
+    }   
+   
+    /* open file to read */ 
+    if ((attrF = fopen(mctx.attrplFile, "r")) == NULL )
+    {
+      printf("ERROR reading attr file [%s]\n",mctx.attrplFile); 
+      return (-1);
+    }else{
+      printf("file opened for reading\n");
+    }
+
+    /* start to read file content */
+    mctx.attrplFileContent = (char *)malloc(mctx.attrplFileSize + 1);    
+    i=0;
+    while ( fread(buffer, buffersize , 1, attrF) )
+    {
+      memcpy(mctx.attrplFileContent+i, buffer , buffersize );
+      memset(buffer ,'\0', buffersize );
+      i = i + buffersize;
+    } 
+    /* copy remainding content into mctx.attrplFileContent */
+    if (i<mctx.attrplFileSize)
+    {
+      memcpy(mctx.attrplFileContent+i, buffer , (mctx.attrplFileSize - 1 - i));
+      memset(buffer ,'\0', buffersize );  /* clear the buffer */
+    }
+
+    mctx.attrplFileContent[mctx.attrplFileSize]='\0'; // append the close bit
+
+    if ((fclose(attrF)) == EOF )
+    {
+      printf("ERROR closing attr file [%s]\n",mctx.attrplFile);
+      return (-1);
+    }else{
+      printf("file closed\n");
+    }
+
+  }
+
 
   /*
    * Initiates statistics fields
@@ -2123,6 +2207,8 @@ char *execParams[] = {
 	"abandon",
 #define EP_DEREF		 	50
 	"deref",
+#define EP_ATT_REPLACE_FILE		51
+	"attreplacefile",
 	NULL
 };
 
@@ -2165,6 +2251,15 @@ decodeExecParams (
 	}							/*JLS 21-11-00*/
 	mctx.attrpl = strdup (subvalue);			/*JLS 21-11-00*/
 	break;							/*JLS 21-11-00*/
+      case EP_ATT_REPLACE_FILE:
+        mctx.mod2 |= M2_ATTR_REPLACE_FILE;
+        if (subvalue == NULL)
+        {
+          fprintf (stderr, "Error: missing arg attreplacefile\n");
+          return (-1);
+        }
+        mctx.attrpl = strdup (subvalue);
+        break;
       case EP_ATTRLIST:						/*JLS 15-03-01*/
 	return (addAttrToList (subvalue));			/*JLS 15-03-01*/
 	break;							/*JLS 15-03-01*/
@@ -3094,6 +3189,11 @@ main (
       printf ("Attribute's head   = \"%s\"\n", mctx.attrplHead);/*JLS 21-11-00*/
       printf ("Attribute's tail   = \"%s\"\n", mctx.attrplTail);/*JLS 21-11-00*/
     }								/*JLS 21-11-00*/
+    if (mctx.mod2 & M2_ATTR_REPLACE_FILE)
+    {
+      printf ("Attribute to replace  = \"%s\"\n", mctx.attrplName);
+      printf ("Attribute value file  = \"%s\"\n", mctx.attrplFile);
+    }
     if (mctx.mode & ASYNC)
     {
       printf ("Async max pending  = %d\n", mctx.asyncMax);
diff --git a/ldap/servers/slapd/tools/ldclt/ldclt.h b/ldap/servers/slapd/tools/ldclt/ldclt.h
index ee774f5..09e35b1 100644
--- a/ldap/servers/slapd/tools/ldclt/ldclt.h
+++ b/ldap/servers/slapd/tools/ldclt/ldclt.h
@@ -279,6 +279,7 @@ dd/mm/yy | Author	| Comments
 #define M2_RANDOM_SASLAUTHID     0x00000080 /* -e randomauthid */
 #define M2_ABANDON     0x00000100 /* -e abandon */
 #define M2_DEREF     0x00000200 /* -e deref */
+#define M2_ATTR_REPLACE_FILE	 0x00000400 /* -e attreplacefile */
 
 /*
  * Combinatory defines
@@ -288,11 +289,11 @@ dd/mm/yy | Author	| Comments
  *  - VALID_OPERS	: valid operations
  */
 #define NEED_FILTER	(ADD_ENTRIES|DELETE_ENTRIES|EXACT_SEARCH|RENAME_ENTRIES|ATTR_REPLACE|SCALAB01)
-#define M2_NEED_FILTER	(M2_ABANDON)
+#define M2_NEED_FILTER	(M2_ABANDON|M2_ATTR_REPLACE_FILE)
 #define NEED_RANGE	(INCREMENTAL|RANDOM)
 #define NEED_RND_INCR	(ADD_ENTRIES|DELETE_ENTRIES|RENAME_ENTRIES)
 #define VALID_OPERS	(ADD_ENTRIES|DELETE_ENTRIES|EXACT_SEARCH|RENAME_ENTRIES|ATTR_REPLACE|SCALAB01)
-#define M2_VALID_OPERS	(M2_GENLDIF|M2_BINDONLY|M2_ABANDON)
+#define M2_VALID_OPERS	(M2_GENLDIF|M2_BINDONLY|M2_ABANDON|M2_ATTR_REPLACE_FILE)
 #define NEED_CLASSES	(ADD_ENTRIES)
 #define THE_CLASSES	(OC_PERSON|OC_EMAILPERSON|OC_INETORGPRSON)
 
@@ -506,6 +507,9 @@ typedef struct main_context {
 	char		*attrlist[MAX_ATTRIBS];			/*JLS 15-03-01*/
 	int		 attrlistNb;	/* Nb attrib in list */	/*JLS 15-03-01*/
 	char		*attrpl;	/* Attrib argument */	/*JLS 21-11-00*/
+	char		*attrplFile;	/* Attrib file to get value from */
+	char		*attrplFileContent;	/* Attrib file content */
+	int		attrplFileSize;		/* Attrib file size*/
 	char		*attrplHead;	/* Attrib value head */	/*JLS 21-11-00*/
 	char		*attrplName;	/* Attrib name */	/*JLS 21-11-00*/
 	int		 attrplNbDigit;	/* Attrib nb digits */	/*JLS 21-11-00*/
diff --git a/ldap/servers/slapd/tools/ldclt/ldcltU.c b/ldap/servers/slapd/tools/ldclt/ldcltU.c
index b002310..7540c5f 100644
--- a/ldap/servers/slapd/tools/ldclt/ldcltU.c
+++ b/ldap/servers/slapd/tools/ldclt/ldcltU.c
@@ -163,6 +163,7 @@ void usage ()
   (void) printf ("		add               : ldap_add() entries.\n");
   (void) printf ("		append            : append entries to the genldif file.\n");
   (void) printf ("		ascii             : ascii 7-bits strings.\n");
+  (void) printf ("		attreplacefile=attrname:<file name> : replace attribute with given file content.\n");
   (void) printf ("		attreplace=name:mask    : replace attribute of existing entry.\n");
   (void) printf ("		attrlist=name:name:name : specify list of attribs to retrieve\n");
   (void) printf ("		attrsonly=0|1     : ldap_search() parameter. Set 0 to read values.\n");
diff --git a/ldap/servers/slapd/tools/ldclt/threadMain.c b/ldap/servers/slapd/tools/ldclt/threadMain.c
index 32df9b7..46b1b63 100644
--- a/ldap/servers/slapd/tools/ldclt/threadMain.c
+++ b/ldap/servers/slapd/tools/ldclt/threadMain.c
@@ -1114,6 +1114,23 @@ threadMain (
 			mctx.attrplTail);
   }
 
+
+  /*
+   * Initiates the attribute replace buffers attrplName
+   */
+  if ( mctx.mod2 & M2_ATTR_REPLACE_FILE )
+  {
+    /* bufAttrpl should point to the same memory location that mctx.attrplFileContent points to */
+    tttctx->bufAttrpl = mctx.attrplFileContent;
+    if (tttctx->bufAttrpl == NULL)
+    {
+      printf ("ldclt[%d]: T%03d: cannot malloc(tttctx->bufAttrpl), error=%d (%s), can we read file [%s]\n",
+		mctx.pid, tttctx->thrdNum, errno, strerror (errno), mctx.attrplFile);
+      ldcltExit (EXIT_INIT);					/*JLS 18-12-00*/
+    }
+  }
+
+
   /*
    * We are ready to go !
    */
@@ -1154,6 +1171,14 @@ threadMain (
 	go = 0;							/*JLS 21-11-00*/
 	continue;						/*JLS 21-11-00*/
       }								/*JLS 21-11-00*/
+
+    if (mctx.mod2 & M2_ATTR_REPLACE_FILE )
+      if (doAttrFileReplace (tttctx) < 0)
+      {	
+	go = 0;	
+	continue;
+      }	
+
     if (tttctx->mode & DELETE_ENTRIES)
       if (doDeleteEntry (tttctx) < 0)
       {
-- 
1.6.2.5

