/*
   attmap.c - attribute mapping values and functions
   This file is part of the nss-pam-ldapd library.

   Copyright (C) 2007, 2008, 2009, 2010, 2011 Arthur de Jong

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

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301 USA
*/

#include "config.h"

#include <stdlib.h>
#include <strings.h>

#include "attmap.h"
#include "log.h"
#include "common/expr.h"

/* these are the bases that are defined per database */
extern const char *alias_bases[];
extern const char *ether_bases[];
extern const char *group_bases[];
extern const char *host_bases[];
extern const char *netgroup_bases[];
extern const char *network_bases[];
extern const char *passwd_bases[];
extern const char *protocol_bases[];
extern const char *rpc_bases[];
extern const char *service_bases[];
extern const char *shadow_bases[];

const char **base_get_var(enum ldap_map_selector map)
{
  switch (map)
  {
    case LM_ALIASES:   return alias_bases;
    case LM_ETHERS:    return ether_bases;
    case LM_GROUP:     return group_bases;
    case LM_HOSTS:     return host_bases;
    case LM_NETGROUP:  return netgroup_bases;
    case LM_NETWORKS:  return network_bases;
    case LM_PASSWD:    return passwd_bases;
    case LM_PROTOCOLS: return protocol_bases;
    case LM_RPC:       return rpc_bases;
    case LM_SERVICES:  return service_bases;
    case LM_SHADOW:    return shadow_bases;
    case LM_NONE:
    default:           return NULL;
  }
}

/* these are the scopes that are defined per database */
extern int alias_scope;
extern int ether_scope;
extern int group_scope;
extern int host_scope;
extern int netgroup_scope;
extern int network_scope;
extern int passwd_scope;
extern int protocol_scope;
extern int rpc_scope;
extern int service_scope;
extern int shadow_scope;

int *scope_get_var(enum ldap_map_selector map)
{
  switch (map)
  {
    case LM_ALIASES:   return &alias_scope;
    case LM_ETHERS:    return &ether_scope;
    case LM_GROUP:     return &group_scope;
    case LM_HOSTS:     return &host_scope;
    case LM_NETGROUP:  return &netgroup_scope;
    case LM_NETWORKS:  return &network_scope;
    case LM_PASSWD:    return &passwd_scope;
    case LM_PROTOCOLS: return &protocol_scope;
    case LM_RPC:       return &rpc_scope;
    case LM_SERVICES:  return &service_scope;
    case LM_SHADOW:    return &shadow_scope;
    case LM_NONE:
    default:           return NULL;
  }
}

/* these are the filters that are defined per database */
extern const char *alias_filter;
extern const char *ether_filter;
extern const char *group_filter;
extern const char *host_filter;
extern const char *netgroup_filter;
extern const char *network_filter;
extern const char *passwd_filter;
extern const char *protocol_filter;
extern const char *rpc_filter;
extern const char *service_filter;
extern const char *shadow_filter;

const char **filter_get_var(enum ldap_map_selector map)
{
  switch (map)
  {
    case LM_ALIASES:   return &alias_filter;
    case LM_ETHERS:    return &ether_filter;
    case LM_GROUP:     return &group_filter;
    case LM_HOSTS:     return &host_filter;
    case LM_NETGROUP:  return &netgroup_filter;
    case LM_NETWORKS:  return &network_filter;
    case LM_PASSWD:    return &passwd_filter;
    case LM_PROTOCOLS: return &protocol_filter;
    case LM_RPC:       return &rpc_filter;
    case LM_SERVICES:  return &service_filter;
    case LM_SHADOW:    return &shadow_filter;
    case LM_NONE:
    default:           return NULL;
  }
}

const char **attmap_get_var(enum ldap_map_selector map,const char *name)
{
  if (map==LM_ALIASES)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_alias_cn;
    if (strcasecmp(name,"rfc822MailMember")==0)  return &attmap_alias_rfc822MailMember;
  }
  else if (map==LM_ETHERS)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_ether_cn;
    if (strcasecmp(name,"macAddress")==0)        return &attmap_ether_macAddress;
  }
  else if (map==LM_GROUP)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_group_cn;
    if (strcasecmp(name,"userPassword")==0)      return &attmap_group_userPassword;
    if (strcasecmp(name,"gidNumber")==0)         return &attmap_group_gidNumber;
    if (strcasecmp(name,"memberUid")==0)         return &attmap_group_memberUid;
    if (strcasecmp(name,"member")==0)            return &attmap_group_member;
  }
  else if (map==LM_HOSTS)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_host_cn;
    if (strcasecmp(name,"ipHostNumber")==0)      return &attmap_host_ipHostNumber;
  }
  else if (map==LM_NETGROUP)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_netgroup_cn;
    if (strcasecmp(name,"nisNetgroupTriple")==0) return &attmap_netgroup_nisNetgroupTriple;
    if (strcasecmp(name,"memberNisNetgroup")==0) return &attmap_netgroup_memberNisNetgroup;
  }
  else if (map==LM_NETWORKS)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_network_cn;
    if (strcasecmp(name,"ipNetworkNumber")==0)   return &attmap_network_ipNetworkNumber;
  }
  else if (map==LM_PASSWD)
  {
    if (strcasecmp(name,"uid")==0)               return &attmap_passwd_uid;
    if (strcasecmp(name,"userPassword")==0)      return &attmap_passwd_userPassword;
    if (strcasecmp(name,"uidNumber")==0)         return &attmap_passwd_uidNumber;
    if (strcasecmp(name,"gidNumber")==0)         return &attmap_passwd_gidNumber;
    if (strcasecmp(name,"gecos")==0)             return &attmap_passwd_gecos;
    if (strcasecmp(name,"homeDirectory")==0)     return &attmap_passwd_homeDirectory;
    if (strcasecmp(name,"loginShell")==0)        return &attmap_passwd_loginShell;
  }
  else if (map==LM_PROTOCOLS)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_protocol_cn;
    if (strcasecmp(name,"ipProtocolNumber")==0)  return &attmap_protocol_ipProtocolNumber;
  }
  else if (map==LM_RPC)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_rpc_cn;
    if (strcasecmp(name,"oncRpcNumber")==0)      return &attmap_rpc_oncRpcNumber;
  }
  else if (map==LM_SERVICES)
  {
    if (strcasecmp(name,"cn")==0)                return &attmap_service_cn;
    if (strcasecmp(name,"ipServicePort")==0)     return &attmap_service_ipServicePort;
    if (strcasecmp(name,"ipServiceProtocol")==0) return &attmap_service_ipServiceProtocol;
  }
  else if (map==LM_SHADOW)
  {
    if (strcasecmp(name,"uid")==0)               return &attmap_shadow_uid;
    if (strcasecmp(name,"userPassword")==0)      return &attmap_shadow_userPassword;
    if (strcasecmp(name,"shadowLastChange")==0)  return &attmap_shadow_shadowLastChange;
    if (strcasecmp(name,"shadowMin")==0)         return &attmap_shadow_shadowMin;
    if (strcasecmp(name,"shadowMax")==0)         return &attmap_shadow_shadowMax;
    if (strcasecmp(name,"shadowWarning")==0)     return &attmap_shadow_shadowWarning;
    if (strcasecmp(name,"shadowInactive")==0)    return &attmap_shadow_shadowInactive;
    if (strcasecmp(name,"shadowExpire")==0)      return &attmap_shadow_shadowExpire;
    if (strcasecmp(name,"shadowFlag")==0)        return &attmap_shadow_shadowFlag;
  }
  return NULL;
}

const char *attmap_set_mapping(const char **var,const char *value)
{
  /* check if we are setting an expression */
  if (value[0]=='"')
  {
    /* these attributes may contain an expression
       (note that this needs to match the functionality in the specific
       lookup module) */
    if ( (var!=&attmap_group_userPassword) &&
         (var!=&attmap_passwd_userPassword) &&
         (var!=&attmap_passwd_gidNumber) &&
         (var!=&attmap_passwd_gecos) &&
         (var!=&attmap_passwd_homeDirectory) &&
         (var!=&attmap_passwd_loginShell) &&
         (var!=&attmap_shadow_userPassword) &&
         (var!=&attmap_shadow_shadowLastChange) &&
         (var!=&attmap_shadow_shadowMin) &&
         (var!=&attmap_shadow_shadowMax) &&
         (var!=&attmap_shadow_shadowWarning) &&
         (var!=&attmap_shadow_shadowInactive) &&
         (var!=&attmap_shadow_shadowExpire) &&
         (var!=&attmap_shadow_shadowFlag) )
      return NULL;
  }
  /* check if the value will be changed */
  if ( (*var==NULL) || (strcmp(*var,value)!=0) )
    *var=strdup(value);
  return *var;
}

static const char *entry_expand(const char *name,void *expander_attr)
{
  MYLDAP_ENTRY *entry=(MYLDAP_ENTRY *)expander_attr;
  const char **values;
  if (strcasecmp(name,"dn")==0)
    return myldap_get_dn(entry);
  values=myldap_get_values(entry,name);
  if (values==NULL)
    return "";
  /* TODO: handle userPassword attribute specially */
  if ((values[0]!=NULL)&&(values[1]!=NULL))
  {
    log_log(LOG_WARNING,"%s: %s: multiple values",
                        myldap_get_dn(entry),name);
  }
  return values[0];
}

const char *attmap_get_value(MYLDAP_ENTRY *entry,const char *attr,char *buffer,size_t buflen)
{
  const char **values;
  /* check and clear buffer */
  if ((buffer==NULL)||(buflen<=0))
    return NULL;
  buffer[0]='\0';
  /* for simple values just return the attribute */
  if (attr[0]!='"')
  {
    values=myldap_get_values(entry,attr);
    if ((values==NULL)||(values[0]==NULL))
      return NULL;
    strncpy(buffer,values[0],buflen);
    buffer[buflen-1]='\0';
    return buffer;
    /* TODO: maybe warn when multiple values are found */
  }
  /* we have an expression, try to parse */
  if ( (attr[strlen(attr)-1]!='"') ||
       (expr_parse(attr+1,buffer,buflen,entry_expand,(void *)entry)==NULL) )
  {
    log_log(LOG_ERR,"attribute mapping %s is invalid",attr);
    buffer[0]='\0';
    return NULL;
  }
  /* strip trailing " */
  if (buffer[strlen(buffer)-1]=='"')
    buffer[strlen(buffer)-1]='\0';
  return buffer;
}

SET *attmap_add_attributes(SET *set,const char *attr)
{
  if (attr[0]!='\"')
    set_add(set,attr);
  else
    expr_vars(attr,set);
  return set;
}