/*
    ldapdiff
    Copyright (C) 2000-2006 Thomas.Reith@rhoen.de

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <lber.h>
#include <ldap.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ldapdiff.h"

static void cmodentry(struct s_modentry **smodentry,char *var,char *val,size_t val_len,teattrtype val_type,temodentry emodentry)
{
 struct s_modentry *psmodentry;
 struct s_modentry *ptsmodentry;
 struct s_ldifval  *psmodentryval;
 struct s_ldifval  *ptsmodentryval;

 if(var == NULL || val == NULL){
  return;
 }

 psmodentryval          = LDALLOC(1,sizeof(struct s_ldifval));
 psmodentryval->val     = LDBINDUP(val,val_len);
 psmodentryval->val_len = val_len;

 psmodentry = *smodentry;
 while(psmodentry != NULL){
  if(strcasecmp(psmodentry->var,var) == 0){
   ptsmodentryval = psmodentry->vallist;
   while(ptsmodentryval->next != NULL){
    ptsmodentryval = ptsmodentryval->next;
   }
   ptsmodentryval->next = psmodentryval;
   return;
  }
  psmodentry = psmodentry->next;
 } 

 ptsmodentry           = LDALLOC(1,sizeof(struct s_modentry));
 ptsmodentry->var      = LDDUP(var);
 ptsmodentry->vallist  = psmodentryval;
 ptsmodentry->val_type = val_type;
 ptsmodentry->modtype  = emodentry;

 if(*smodentry == NULL){
  *smodentry = ptsmodentry;
 }
 else{
  struct s_modentry *usmodentry;
  usmodentry = *smodentry;
  while(usmodentry != NULL){
   if(usmodentry->next == NULL){
    usmodentry->next = ptsmodentry;
    return;
   }
   usmodentry = usmodentry->next;
  }
 }
}

static void cmod(struct s_mod **smod,char *dnvar,char *dnval,char *var,char *val,size_t val_len,teattrtype val_type,temod emod,temodentry emodentry)
{
 struct s_mod *psmod;
 struct s_mod *ptsmod;

 psmod = *smod;
 while(psmod != NULL){
  if(strcmp(psmod->dnval,dnval) == 0 && psmod->modtype == emod){
   cmodentry(&psmod->attrlist,var,val,val_len,val_type,emodentry);
   return;
  }
  psmod = psmod->next;
 }  

 ptsmod          = LDALLOC(1,sizeof(struct s_mod));
 ptsmod->dnvar   = LDDUP(dnvar);
 ptsmod->dnval   = LDDUP(dnval);
 ptsmod->modtype = emod;

 cmodentry(&ptsmod->attrlist,var,val,val_len,val_type,emodentry);
 if(*smod == NULL){
  *smod = ptsmod;
 }
 else{
  struct s_mod *usmod;
  usmod = *smod;
  while(usmod != NULL){
   if(usmod->next == NULL){
    usmod->next = ptsmod;
    return;
   }
   usmod = usmod->next;
  }
 }
}

void ldifcmp(LDAP *ld,struct s_ldif *sldif,struct s_ldif *sldap,struct s_mod **smod,struct s_schema *sschema)
{
 struct s_ldifentry *psldifentry;
 struct s_ldifentry *psldapentry;
 struct s_ldifval   *psldifval;
 struct s_ldifval   *psldapval;
 int                 attr_found;
 int                 attr_modifybydeladd = 0;

 psldifentry = sldif->attrlist; 
 while(psldifentry != NULL){
  if(ldifcheckignore(psldifentry->var) == ATTRIGNORE){
   ldiflog(LOG2,"ignore  attr: %s",sldif->dnval);
   psldifval = psldifentry->vallist;
   while(psldifval != NULL){
    ldiflogval(LOG2,"              %s: %s",psldifentry->var,psldifval->val,psldifval->val_len);
    psldifval = psldifval->next;
   }
  }
  else{
   attr_found  = 0;
   psldapentry = sldap->attrlist;
   while(psldapentry != NULL){
    if(strcasecmp(psldifentry->var,psldapentry->var) == 0){
     int ldifcount = 0;
     int ldapcount = 0;

     attr_found  = 1;

     psldifval = psldifentry->vallist;
     while(psldifval != NULL){
      if(ldifcheckignoreval(psldifval->val) != ATTRIGNORE){
       ldifcount++;
      }
      else{
       ldiflog(LOG2,"ignore ldif attrval: %s",sldif->dnval);
       ldiflog(LOG2,"ignore ldif attrval: %s",psldifval->val);
      }
      psldifval = psldifval->next;
     }

     psldapval = psldapentry->vallist;
     while(psldapval != NULL){
      if(ldifcheckignoreval(psldapval->val) != ATTRIGNORE){
       ldapcount++;
      }
      else{
       ldiflog(LOG2,"ignore ldap attrval: %s",sldap->dnval);
       ldiflog(LOG2,"ignore ldap attrval: %s",psldapval->val);
      }
      psldapval = psldapval->next;
     }

     if(ldapcount == ldifcount){
      ldapcount = 0;
      psldifval = psldifentry->vallist;
      while(psldifval != NULL){
       psldapval = psldapentry->vallist;
       while(psldapval != NULL){
        if(psldifval->val_len == psldapval->val_len){
         if(memcmp(psldifval->val,psldapval->val,psldifval->val_len) == 0){
          ldapcount++;
         }
        }
        psldapval = psldapval->next;
       }
       psldifval = psldifval->next;
      }
     }

     if(ldifcount != ldapcount){
      /* 
      case 1: attribute is different in ldif file 
      */
      ldiflog(LOG2,"replace attr: %s",sldif->dnval);
      psldapval = psldapentry->vallist;
      while(psldapval != NULL){
       ldiflogval(LOG2,"              %s: %s ->",psldapentry->var,psldapval->val,psldapval->val_len);
       psldapval = psldapval->next;
      }
      psldifval = psldifentry->vallist;
      while(psldifval != NULL){
       if(strcmp(ldifgetpconf(CONFMODIFYBYDELADD),"yes") != 0){
        cmod(smod,sldif->dnvar,sldif->dnval,psldifentry->var,psldifval->val,psldifval->val_len,psldifentry->val_type,MODMODIFY,MODATTRREPLACE);
       }
       ldiflogval(LOG2,"              %s: %s",psldifentry->var,psldifval->val,psldifval->val_len);
       attr_modifybydeladd = 1;
       psldifval = psldifval->next;
      }
     }
    }
    psldapentry = psldapentry->next;
   }

   if(attr_found == 0){
    /* 
    case 2: attribute doesn't exist in ldap 
    */
    psldifval = psldifentry->vallist;
    while(psldifval != NULL){
     if(strcmp(ldifgetpconf(CONFMODIFYBYDELADD),"yes") != 0){
      cmod(smod,sldif->dnvar,sldif->dnval,psldifentry->var,psldifval->val,psldifval->val_len,psldifentry->val_type,MODMODIFY,MODATTRADD);
     }
     ldiflog(LOG2,"add     attr: %s",sldif->dnval);
     ldiflogval(LOG2,"              %s: %s",psldifentry->var,psldifval->val,psldifval->val_len);
     attr_modifybydeladd = 1;
     psldifval = psldifval->next;
    }
   }
  }
  psldifentry = psldifentry->next;
 } 

 psldapentry = sldap->attrlist;
 while(psldapentry != NULL){
  if(ldifcheckignore(psldapentry->var) == ATTRIGNORE){
   ldiflog(LOG2,"ignore  attr: %s",sldap->dnval);
   psldapval = psldapentry->vallist;
   while(psldapval != NULL){
    ldiflogval(LOG2,"              %s: %s",psldapentry->var,psldapval->val,psldapval->val_len);
    psldapval = psldapval->next;
   }
  }
  else{
   psldifentry = sldif->attrlist; 
   attr_found  = 0;
   while(psldifentry != NULL){
    if(strcasecmp(psldapentry->var,psldifentry->var) == 0){
     attr_found++;
    }
    psldifentry = psldifentry->next;
   }
   if(attr_found == 0){
    /* 
    case 3: attribute doesn't exist in ldif 
    */
    if((strcmp(ldifgetpconf(CONFDELETEATTRIBUTE),"yes") == 0) || 
       (strstr(ldifgetpconf(CONFDELETEATTRIBUTE), psldapentry->var) != NULL)){ 
     psldapval = psldapentry->vallist;
     while(psldapval != NULL){
      if(strcmp(ldifgetpconf(CONFMODIFYBYDELADD),"yes") != 0){
       cmod(smod,sldap->dnvar,sldap->dnval,psldapentry->var,psldapval->val,psldapval->val_len,psldapentry->val_type,MODMODIFY,MODATTRDELETE);
      }
      ldiflog(LOG2,"delete  attr: %s",sldap->dnval);
      ldiflogval(LOG2,"              %s: %s",psldapentry->var,psldapval->val,psldapval->val_len);
      attr_modifybydeladd = 1;
      psldapval = psldapval->next;
     }
    }
    else{
     ldiflog(LOG2,"deletion disabled: %s",sldap->dnval);
    }
   }
  }
  psldapentry = psldapentry->next;
 }

 /* 
 if there are any replacement attributes and "confmodbydelete: yes" in ldapdiff.conf, 
 every detected entry will be deleted and the entry from the ldif file will be added
 */
 if(attr_modifybydeladd == 1 && strcmp(ldifgetpconf(CONFMODIFYBYDELADD),"yes") == 0){
  ldiflog(LOG2,"delete & add: %s",sldif->dnval);
  ldifdeletedn(sldif->dnvar,sldif->dnval,smod);
  ldifadddn(sldif,smod);
 }
}

void ldifadddn(struct s_ldif *sldif,struct s_mod **smod)
{
 struct s_ldifentry *psldifentry; 
 struct s_ldifval   *psldifval;

 psldifentry = sldif->attrlist;
 while(psldifentry != NULL){
  if(ldifcheckignore(psldifentry->var) == ATTRIGNORE){
   ldiflog(LOG2,"ignore  attr: %s",sldif->dnval);
   psldifval = psldifentry->vallist;
   while(psldifval != NULL){
    ldiflogval(LOG2,"              %s: %s",psldifentry->var,psldifval->val,psldifval->val_len);
    psldifval = psldifval->next;
   }
  }
  else{
   if(strcmp(ldifgetpconf(CONFADDENTRY),"yes") == 0){
    psldifval = psldifentry->vallist;
    while(psldifval != NULL){
     cmod(smod,sldif->dnvar,sldif->dnval,psldifentry->var,psldifval->val,psldifval->val_len,psldifentry->val_type,MODADD,MODATTRADD);
     ldiflog(LOG2,"add    entry: %s",sldif->dnval);
     ldiflogval(LOG2,"              %s: %s",psldifentry->var,psldifval->val,psldifval->val_len);
     psldifval = psldifval->next;
    }
   } 
   else{
    ldiflog(LOG2,"addition disabled: %s", sldif->dnval);
   }
  }
  psldifentry = psldifentry->next;
 }
}

void ldifdeletedn(char *dnvar,char *dnval,struct s_mod **smod)
{
 if(strcmp(ldifgetpconf(CONFDELETEENTRY),"yes") == 0){ 
  cmod(smod,dnvar,dnval,NULL,NULL,0,0,MODDELETE,MODATTRDELETE); 
  ldiflog(LOG2,"del    entry: %s",dnval);
 }
 else{
  ldiflog(LOG2,"deletion disabled: %s",dnval);
 }
}


syntax highlighted by Code2HTML, v. 0.9.1