/* Return all groups for principal == null or all groups for which principal
   * is a member
   *
   */
  private Collection<BwGroup> getGroups(
      final DirConfigProperties dirProps, final BwPrincipal principal) throws CalFacadeException {
    LdapConfigProperties props = (LdapConfigProperties) dirProps;
    InitialLdapContext ctx = null;
    String member = null;

    if (principal != null) {
      if (principal.getKind() == WhoDefs.whoTypeUser) {
        member = getUserEntryValue(props, principal);
      } else if (principal.getKind() == WhoDefs.whoTypeGroup) {
        member = getGroupEntryValue(props, principal);
      }
    }

    try {
      ctx = createLdapInitContext(props);

      BasicAttributes matchAttrs = new BasicAttributes(true);

      if (member != null) {
        matchAttrs.put(props.getGroupMemberAttr(), member);
      }

      String[] idAttr = {props.getGroupIdAttr()};

      ArrayList<BwGroup> groups = new ArrayList<BwGroup>();
      NamingEnumeration response = ctx.search(props.getGroupContextDn(), matchAttrs, idAttr);
      while (response.hasMore()) {
        SearchResult sr = (SearchResult) response.next();
        Attributes attrs = sr.getAttributes();

        Attribute nmAttr = attrs.get(props.getGroupIdAttr());
        if (nmAttr.size() != 1) {
          throw new CalFacadeException("org.bedework.ldap.groups.multiple.result");
        }

        BwGroup group = new BwGroup();
        group.setAccount(nmAttr.get(0).toString());
        group.setPrincipalRef(makePrincipalUri(group.getAccount(), WhoDefs.whoTypeGroup));

        groups.add(group);
      }

      return groups;
    } catch (Throwable t) {
      if (debug) {
        error(t);
      }
      throw new CalFacadeException(t);
    } finally {
      // Close the context to release the connection
      if (ctx != null) {
        closeContext(ctx);
      }
    }
  }
  /* Search for a group to ensure it exists
   *
   */
  private BwGroup findGroup(final DirConfigProperties dirProps, final String groupName)
      throws CalFacadeException {
    LdapConfigProperties props = (LdapConfigProperties) dirProps;
    InitialLdapContext ctx = null;

    try {
      ctx = createLdapInitContext(props);

      BasicAttributes matchAttrs = new BasicAttributes(true);

      matchAttrs.put(props.getGroupIdAttr(), groupName);

      String[] idAttr = {props.getGroupIdAttr()};

      BwGroup group = null;
      NamingEnumeration response = ctx.search(props.getGroupContextDn(), matchAttrs, idAttr);
      while (response.hasMore()) {
        //        SearchResult sr = (SearchResult)response.next();
        //        Attributes attrs = sr.getAttributes();

        if (group != null) {
          throw new CalFacadeException("org.bedework.ldap.groups.multiple.result");
        }

        group = new BwGroup();
        group.setAccount(groupName);
        group.setPrincipalRef(makePrincipalUri(groupName, WhoDefs.whoTypeGroup));
      }

      return group;
    } catch (Throwable t) {
      if (debug) {
        error(t);
      }
      throw new CalFacadeException(t);
    } finally {
      // Close the context to release the connection
      if (ctx != null) {
        closeContext(ctx);
      }
    }
  }
  private InitialLdapContext createLdapInitContext(final LdapConfigProperties props)
      throws CalFacadeException {
    Properties env = new Properties();

    // Map all options into the JNDI InitialLdapContext env

    env.setProperty(Context.INITIAL_CONTEXT_FACTORY, props.getInitialContextFactory());

    env.setProperty(Context.SECURITY_AUTHENTICATION, props.getSecurityAuthentication());

    env.setProperty(Context.SECURITY_PROTOCOL, props.getSecurityProtocol());

    env.setProperty(Context.PROVIDER_URL, props.getProviderUrl());

    String protocol = env.getProperty(Context.SECURITY_PROTOCOL);
    String providerURL = env.getProperty(Context.PROVIDER_URL);

    if (providerURL == null) {
      providerURL =
          "ldap://localhost:" + ((protocol != null) && protocol.equals("ssl") ? "389" : "636");
      env.setProperty(Context.PROVIDER_URL, providerURL);
    }

    if (props.getAuthDn() != null) {
      env.setProperty(Context.SECURITY_PRINCIPAL, props.getAuthDn());
      env.put(Context.SECURITY_CREDENTIALS, props.getAuthPw());
    }

    InitialLdapContext ctx = null;

    try {
      ctx = new InitialLdapContext(env, null);
      if (debug) {
        trace("Logged into LDAP server, " + ctx);
      }

      return ctx;
    } catch (Throwable t) {
      if (debug) {
        error(t);
      }
      throw new CalFacadeException(t);
    }
  }
 private String makeGroupDn(final LdapConfigProperties props, final BwPrincipal p) {
   return props.getGroupDnPrefix() + p.getAccount() + props.getGroupDnSuffix();
 }
  /* Find members for given group
   *
   */
  private void getGroupMembers(final DirConfigProperties dirProps, final BwGroup group)
      throws CalFacadeException {
    LdapConfigProperties props = (LdapConfigProperties) dirProps;
    InitialLdapContext ctx = null;

    try {
      ctx = createLdapInitContext(props);

      BasicAttributes matchAttrs = new BasicAttributes(true);

      matchAttrs.put(props.getGroupIdAttr(), group.getAccount());

      String[] memberAttr = {props.getGroupMemberAttr()};

      ArrayList<String> mbrs = null;

      boolean beenHere = false;

      NamingEnumeration response = ctx.search(props.getGroupContextDn(), matchAttrs, memberAttr);
      while (response.hasMore()) {
        SearchResult sr = (SearchResult) response.next();
        Attributes attrs = sr.getAttributes();

        if (beenHere) {
          throw new CalFacadeException("org.bedework.ldap.groups.multiple.result");
        }

        beenHere = true;

        Attribute membersAttr = attrs.get(props.getGroupMemberAttr());
        mbrs = new ArrayList<String>();

        for (int m = 0; m < membersAttr.size(); m++) {
          mbrs.add(membersAttr.get(m).toString());
        }
      }
      // LDAP We need a way to search recursively for groups.

      /* Search for each user in the group */
      String memberContext = props.getGroupMemberContextDn();
      String memberSearchAttr = props.getGroupMemberSearchAttr();
      String[] idAttr = {
        props.getGroupMemberUserIdAttr(), props.getGroupMemberGroupIdAttr(), "objectclass"
      };

      for (String mbr : mbrs) {
        if (memberContext != null) {
          matchAttrs = new BasicAttributes(true);

          matchAttrs.put(memberSearchAttr, mbr);

          response = ctx.search(memberContext, matchAttrs, idAttr);
        } else {
          response = ctx.search(memberContext, null, idAttr);
        }

        if (response.hasMore()) {
          SearchResult sr = (SearchResult) response.next();
          Attributes attrs = sr.getAttributes();

          Attribute ocsAttr = attrs.get("objectclass");
          String userOc = props.getUserObjectClass();
          String groupOc = props.getGroupObjectClass();
          boolean isGroup = false;

          for (int oci = 0; oci < ocsAttr.size(); oci++) {
            String oc = ocsAttr.get(oci).toString();
            if (userOc.equals(oc)) {
              break;
            }

            if (groupOc.equals(oc)) {
              isGroup = true;
              break;
            }
          }

          BwPrincipal p = null;
          Attribute attr;

          if (isGroup) {
            p = BwPrincipal.makeGroupPrincipal();

            attr = attrs.get(props.getGroupMemberGroupIdAttr());
          } else {
            p = BwPrincipal.makeUserPrincipal();

            attr = attrs.get(props.getGroupMemberUserIdAttr());
          }

          if (attr.size() != 1) {
            throw new CalFacadeException("org.bedework.ldap.groups.multiple.result");
          }

          p.setAccount(attr.get(0).toString());
          p.setPrincipalRef(makePrincipalUri(p.getAccount(), p.getKind()));
          group.addGroupMember(p);
        }
      }
    } catch (Throwable t) {
      if (debug) {
        error(t);
      }
      throw new CalFacadeException(t);
    } finally {
      // Close the context to release the connection
      if (ctx != null) {
        closeContext(ctx);
      }
    }

    /* Recursively fetch members of groups that are members. */

    for (BwGroup g : group.getGroups()) {
      getGroupMembers(props, g);
    }
  }