mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-15 03:01:09 +08:00
1239 lines
32 KiB
C
1239 lines
32 KiB
C
/*
|
|
* Copyright (c) 1993, 1994 Regents of the University of Michigan.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that this notice is preserved and that due credit is given
|
|
* to the University of Michigan at Ann Arbor. The name of the University
|
|
* may not be used to endorse or promote products derived from this
|
|
* software without specific prior written permission. This software
|
|
* is provided ``as is'' without express or implied warranty.
|
|
*
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ac/string.h>
|
|
#include <ac/ctype.h>
|
|
#include <ac/time.h>
|
|
#include <ac/unistd.h>
|
|
|
|
#ifdef HAVE_IO_H
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#include <lber.h>
|
|
#include <ldap.h>
|
|
#include <ldapconfig.h>
|
|
#include "ud.h"
|
|
|
|
static char * bind_and_fetch(char *name);
|
|
|
|
|
|
void
|
|
add_group( char *name )
|
|
{
|
|
int idx = 0, prompt = 0;
|
|
char tmp[BUFSIZ], dn[BUFSIZ];
|
|
static LDAPMod *attrs[9];
|
|
LDAPMod init_rdn, init_owner, init_domain,
|
|
init_errors, init_request, init_joinable;
|
|
char *init_rdn_value[2], *init_owner_value[2], *init_domain_value[2],
|
|
*init_errors_value[MAX_VALUES], *init_joinable_value[2],
|
|
*init_request_value[MAX_VALUES];
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE) {
|
|
if (name == NULL)
|
|
printf("->add_group(NULL)\n");
|
|
else
|
|
printf("->add_group(%s)\n", name);
|
|
}
|
|
#endif
|
|
|
|
if (bind_status == UD_NOT_BOUND) {
|
|
if (auth((char *) NULL, 1) < 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the user did not supply us with a name, prompt them for
|
|
* a name.
|
|
*/
|
|
if ((name == NULL) || (*name == '\0') || !strcasecmp(name, "group")) {
|
|
++prompt;
|
|
printf(" Group to create? ");
|
|
fflush(stdout);
|
|
fetch_buffer(tmp, sizeof(tmp), stdin);
|
|
if (tmp[0] == '\0')
|
|
return;
|
|
name = strdup(tmp);
|
|
}
|
|
|
|
/* remove quotes, dots, and underscores. */
|
|
name = strip_ignore_chars(name);
|
|
|
|
#ifdef UOFM
|
|
if (isauniqname(name)) {
|
|
printf(" '%s' could be confused with a U-M uniqname.\n", name);
|
|
printf(" You can create the group, but you need to make sure that\n");
|
|
printf(" you reserve the uniqname for use as your groupname\n\n");
|
|
printf(" Are you sure that you want to do this? ");
|
|
fflush(stdout);
|
|
fetch_buffer(tmp, sizeof(tmp), stdin);
|
|
if (!(tmp[0] == 'y' || tmp[0] == 'Y'))
|
|
return;
|
|
printf("\n Be sure to contact your uniqname administrator to reserve\n");
|
|
printf(" the uniqname '%s' for use as your group name.\n", name);
|
|
}
|
|
#endif
|
|
sprintf(dn, "cn=%s, %s", name, group_base);
|
|
|
|
/*
|
|
* Make sure that this group does not already exist.
|
|
*/
|
|
if (vrfy(dn) == TRUE) {
|
|
printf(" The group \"%s\" already exists.\n", name);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Take the easy way out: Fill in some reasonable values for
|
|
* the most important fields, and make the user use the modify
|
|
* command to change them, or to give values to other fields.
|
|
*/
|
|
init_rdn_value[0] = name;
|
|
init_rdn_value[1] = NULL;
|
|
init_rdn.mod_op = LDAP_MOD_ADD;
|
|
init_rdn.mod_type = "cn";
|
|
init_rdn.mod_values = init_rdn_value;
|
|
attrs[idx++] = &init_rdn;
|
|
|
|
init_owner_value[0] = bound_dn;
|
|
init_owner_value[1] = NULL;
|
|
init_owner.mod_op = LDAP_MOD_ADD;
|
|
init_owner.mod_type = "owner";
|
|
init_owner.mod_values = init_owner_value;
|
|
attrs[idx++] = &init_owner;
|
|
|
|
#ifdef UOFM
|
|
init_domain_value[0] = "umich.edu";
|
|
#else
|
|
init_domain_value[0] = ".";
|
|
#endif
|
|
init_domain_value[1] = NULL;
|
|
init_domain.mod_op = LDAP_MOD_ADD;
|
|
init_domain.mod_type = "associatedDomain";
|
|
init_domain.mod_values = init_domain_value;
|
|
attrs[idx++] = &init_domain;
|
|
|
|
init_errors_value[0] = bound_dn;
|
|
init_errors_value[1] = NULL;
|
|
init_errors.mod_op = LDAP_MOD_ADD;
|
|
init_errors.mod_type = "ErrorsTo";
|
|
init_errors.mod_values = init_errors_value;
|
|
attrs[idx++] = &init_errors;
|
|
|
|
init_request_value[0] = bound_dn;
|
|
init_request_value[1] = NULL;
|
|
init_request.mod_op = LDAP_MOD_ADD;
|
|
init_request.mod_type = "RequestsTo";
|
|
init_request.mod_values = init_request_value;
|
|
attrs[idx++] = &init_request;
|
|
|
|
init_joinable_value[0] = "FALSE";
|
|
init_joinable_value[1] = NULL;
|
|
init_joinable.mod_op = LDAP_MOD_ADD;
|
|
init_joinable.mod_type = "joinable";
|
|
init_joinable.mod_values = init_joinable_value;
|
|
attrs[idx++] = &init_joinable;
|
|
|
|
/* end it with a NULL */
|
|
attrs[idx] = NULL;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
LDAPMod **lpp;
|
|
char **cpp;
|
|
int i, j;
|
|
printf(" About to call ldap_add()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", dn);
|
|
for (lpp = attrs, i = 0; *lpp != NULL; lpp++, i++) {
|
|
printf(" attrs[%1d] code = %s type = %s\n", i,
|
|
code_to_str((*lpp)->mod_op), (*lpp)->mod_type);
|
|
for (cpp = (*lpp)->mod_values, j = 0; *cpp != NULL; cpp++, j++)
|
|
printf(" value #%1d = %s\n", j, *cpp);
|
|
printf(" value #%1d = NULL\n", j);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Now add this to the LDAP Directory.
|
|
*/
|
|
if (ldap_add_s(ld, dn, attrs) != 0) {
|
|
ldap_perror(ld, " ldap_add_s");
|
|
printf(" Group not added.\n");
|
|
if (prompt) Free(name);
|
|
return;
|
|
}
|
|
if (verbose)
|
|
printf(" Group \"%s\" has been added to the Directory\n",
|
|
name);
|
|
|
|
/*
|
|
* We need to blow away the cache here.
|
|
*
|
|
* Since we first looked up the name before trying to create it,
|
|
* and that look-up failed, the cache will falsely claim that this
|
|
* entry does not exist.
|
|
*/
|
|
(void) ldap_flush_cache(ld);
|
|
if (prompt) Free(name);
|
|
return;
|
|
}
|
|
|
|
void
|
|
remove_group( char *name )
|
|
{
|
|
char *dn, tmp[BUFSIZ];
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE) {
|
|
if (name == NULL)
|
|
printf("->remove_group(NULL)\n");
|
|
else
|
|
printf("->remove_group(%s)\n", name);
|
|
}
|
|
#endif
|
|
if ((dn = bind_and_fetch(name)) == NULL)
|
|
return;
|
|
|
|
printf("\n The group '%s' will be permanently removed from\n",
|
|
name);
|
|
printf(" the Directory. Are you absolutely sure that you want to\n" ); printf(" remove this entire group? ");
|
|
fflush(stdout);
|
|
fetch_buffer(tmp, sizeof(tmp), stdin);
|
|
if (!(tmp[0] == 'y' || tmp[0] == 'Y'))
|
|
return;
|
|
|
|
/*
|
|
* Now remove this from the LDAP Directory.
|
|
*/
|
|
if (ldap_delete_s(ld, dn) != 0) {
|
|
int ld_errno = 0;
|
|
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
|
|
if (ld_errno == LDAP_INSUFFICIENT_ACCESS)
|
|
printf(" You do not own the group \"%s\".\n", name);
|
|
else
|
|
ldap_perror(ld, " ldap_delete_s");
|
|
printf(" Group not removed.\n");
|
|
Free(dn);
|
|
return;
|
|
}
|
|
ldap_uncache_entry(ld, dn);
|
|
if (verbose)
|
|
if (name == NULL)
|
|
printf(" The group has been removed.\n");
|
|
else
|
|
printf(" The group \"%s\" has been removed.\n", name);
|
|
Free(dn);
|
|
return;
|
|
}
|
|
|
|
void
|
|
x_group( int action, char *name )
|
|
{
|
|
char **vp;
|
|
char *values[2], *group_name;
|
|
LDAPMod mod, *mods[2];
|
|
static char *actions[] = { "join", "resign from", NULL };
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE) {
|
|
if (name == NULL)
|
|
printf("->x_group(%d, NULL)\n", action);
|
|
else
|
|
printf("->x_group(%d, %s)\n", action, name);
|
|
}
|
|
#endif
|
|
|
|
/* the action desired sets the opcode to use */
|
|
switch (action) {
|
|
case G_JOIN:
|
|
mod.mod_op = LDAP_MOD_ADD;
|
|
break;
|
|
case G_RESIGN:
|
|
mod.mod_op = LDAP_MOD_DELETE;
|
|
break;
|
|
default:
|
|
printf("x_group: %d is not a known action\n", action);
|
|
}
|
|
|
|
if ((group_name = bind_and_fetch(name)) == NULL)
|
|
return;
|
|
vp = Entry.attrs[attr_to_index("joinable")].values;
|
|
if (action == G_JOIN) {
|
|
if (vp == NULL) {
|
|
printf(" No one is permitted to join \"%s\"\n", group_name);
|
|
Free(group_name);
|
|
return;
|
|
}
|
|
if (!strcasecmp(*vp, "FALSE")) {
|
|
printf(" No one is permitted to join \"%s\"\n", group_name);
|
|
Free(group_name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* fill in the rest of the modification structure */
|
|
mods[0] = &mod;
|
|
mods[1] = (LDAPMod *) NULL;
|
|
values[0] = Entry.DN;
|
|
values[1] = NULL;
|
|
mod.mod_type = "memberOfGroup";
|
|
mod.mod_values = values;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
register LDAPMod **lpp;
|
|
register char **cp;
|
|
register int i, j;
|
|
printf(" About to call ldap_modify_s()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", bound_dn);
|
|
for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
|
|
printf(" mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
|
|
printf(" mods[%1d] type = %s\n", i, (*lpp)->mod_type);
|
|
for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
|
|
printf(" mods[%1d] v[%1d] = %s\n", i, j, *cp);
|
|
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (ldap_modify_s(ld, bound_dn, mods)) {
|
|
int ld_errno = 0;
|
|
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
|
|
if ((action == G_JOIN) && (ld_errno == LDAP_TYPE_OR_VALUE_EXISTS))
|
|
printf(" You are already subscribed to \"%s\"\n", group_name);
|
|
else if ((action == G_RESIGN) && (ld_errno == LDAP_NO_SUCH_ATTRIBUTE))
|
|
printf(" You are not subscribed to \"%s\"\n", group_name);
|
|
else
|
|
mod_perror(ld);
|
|
Free(group_name);
|
|
return;
|
|
}
|
|
ldap_uncache_entry(ld, bound_dn);
|
|
if (verbose) {
|
|
switch (action) {
|
|
case G_JOIN:
|
|
printf(" You are now subscribed to \"%s\"\n", group_name);
|
|
break;
|
|
case G_RESIGN:
|
|
printf(" You are no longer subscribed to \"%s\"\n", group_name);
|
|
break;
|
|
}
|
|
}
|
|
Free(group_name);
|
|
return;
|
|
}
|
|
|
|
void
|
|
bulk_load( char *group )
|
|
{
|
|
register int idx_mail, idx_x500;
|
|
register int count_mail, count_x500;
|
|
char *values_mail[MAX_VALUES + 1], *values_x500[MAX_VALUES + 1];
|
|
int added_mail_entries = FALSE, added_x500_entries = FALSE;
|
|
char s[MED_BUF_SIZE];
|
|
LDAPMod mod, *mods[2];
|
|
LDAPMessage *lm;
|
|
FILE *fp;
|
|
int len;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE)
|
|
printf("->bulk_load(%s)\n", group);
|
|
#endif
|
|
|
|
/* you lose if going through MichNet */
|
|
if ( !isatty( 1 )) {
|
|
#ifdef UOFM
|
|
printf(" Not allowed via UM-X500 connections.\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* fetch entries from the file containing the e-mail addresses */
|
|
printf("\n File to load? ");
|
|
fflush(stdout);
|
|
fetch_buffer(s, sizeof(s), stdin);
|
|
if (s[0] == '\0') {
|
|
return;
|
|
/*NOTREACHED*/
|
|
}
|
|
if ((fp = fopen(s, "r")) == NULL) {
|
|
perror("bulk_load: fopen");
|
|
return;
|
|
}
|
|
if (verbose)
|
|
printf(" Loading group members from %s\n", s);
|
|
|
|
/* load them in MAX_VALUES at a time */
|
|
for (;;) {
|
|
for (idx_mail = 0, idx_x500 = 0;
|
|
idx_mail < MAX_VALUES && idx_x500 < MAX_VALUES; ) {
|
|
(void) fgets(s, sizeof(s), fp);
|
|
if (feof(fp))
|
|
break;
|
|
len = strlen(s) - 1;
|
|
if (len == 0)
|
|
continue;
|
|
s[len] = '\0';
|
|
if (strchr(s, '@'))
|
|
values_mail[idx_mail++] = strdup(s);
|
|
else {
|
|
if ((lm = find(s, !verbose)) == (LDAPMessage *) NULL) {
|
|
printf(" Could not locate \"%s\" -- skipping.\n", s);
|
|
}
|
|
else {
|
|
parse_answer(lm);
|
|
values_x500[idx_x500++] = strdup(Entry.DN);
|
|
}
|
|
}
|
|
}
|
|
values_mail[idx_mail] = NULL;
|
|
values_x500[idx_x500] = NULL;
|
|
count_mail = idx_mail;
|
|
count_x500 = idx_x500;
|
|
|
|
/*
|
|
* Add the e-mail addresses.
|
|
*/
|
|
if (count_mail > 0) {
|
|
mods[0] = &mod;
|
|
mods[1] = (LDAPMod *) NULL;
|
|
mod.mod_type = "mail";
|
|
mod.mod_values = values_mail;
|
|
if (added_mail_entries)
|
|
mod.mod_op = LDAP_MOD_ADD;
|
|
else
|
|
mod.mod_op = LDAP_MOD_REPLACE;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
register LDAPMod **lpp;
|
|
register char **cp;
|
|
register int i, j;
|
|
printf(" About to call ldap_modify_s()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", group);
|
|
for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
|
|
printf(" mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
|
|
printf(" mods[%1d] type = %s\n", i, (*lpp)->mod_type);
|
|
for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
|
|
printf(" mods[%1d] v[%1d] = %s\n", i, j, *cp);
|
|
}
|
|
}
|
|
#endif
|
|
if (ldap_modify_s(ld, group, mods))
|
|
mod_perror(ld);
|
|
for (idx_mail--; idx_mail >= 0; idx_mail--)
|
|
Free(values_mail[idx_mail]);
|
|
ldap_uncache_entry(ld, group);
|
|
added_mail_entries = TRUE;
|
|
|
|
}
|
|
|
|
/*
|
|
* Add the LDAP style names.
|
|
*/
|
|
if (count_x500 > 0) {
|
|
mods[0] = &mod;
|
|
mods[1] = (LDAPMod *) NULL;
|
|
mod.mod_type = "member";
|
|
mod.mod_values = values_x500;
|
|
if (added_x500_entries)
|
|
mod.mod_op = LDAP_MOD_ADD;
|
|
else
|
|
mod.mod_op = LDAP_MOD_REPLACE;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
register LDAPMod **lpp;
|
|
register char **cp;
|
|
register int i, j;
|
|
printf(" About to call ldap_modify_s()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", group);
|
|
for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
|
|
printf(" mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
|
|
printf(" mods[%1d] type = %s\n", i, (*lpp)->mod_type);
|
|
for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
|
|
printf(" mods[%1d] v[%1d] = %s\n", i, j, *cp);
|
|
}
|
|
}
|
|
#endif
|
|
if (ldap_modify_s(ld, group, mods))
|
|
mod_perror(ld);
|
|
for (idx_x500--; idx_x500 >= 0; idx_x500--)
|
|
Free(values_x500[idx_x500]);
|
|
ldap_uncache_entry(ld, group);
|
|
added_x500_entries = TRUE;
|
|
|
|
}
|
|
|
|
/*
|
|
* If both counts were less than the maximum number we
|
|
* can handle at a time, then we are done.
|
|
*/
|
|
if ((count_mail < MAX_VALUES) && (count_x500 < MAX_VALUES))
|
|
break;
|
|
}
|
|
fclose(fp);
|
|
return;
|
|
}
|
|
|
|
void
|
|
purge_group( char *group )
|
|
{
|
|
int isclean = TRUE;
|
|
LDAPMessage *lm;
|
|
LDAPMod mod, *mods[2];
|
|
char dn[BUFSIZ], tmp[BUFSIZ], *values[2], **vp, **rdns;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE) {
|
|
if (group == NULL)
|
|
printf("->purge_group(NULL)\n");
|
|
else
|
|
printf("->purge_group(%s)\n", group);
|
|
}
|
|
#endif
|
|
if (bind_status == UD_NOT_BOUND) {
|
|
if (auth((char *) NULL, 1) < 0)
|
|
return;
|
|
}
|
|
/*
|
|
* If the user did not supply us with a name, prompt them for
|
|
* a name.
|
|
*/
|
|
if ((group == NULL) || (*group == '\0')) {
|
|
printf("Group to purge? ");
|
|
fflush(stdout);
|
|
fetch_buffer(tmp, sizeof(tmp), stdin);
|
|
if (tmp[0] == '\0')
|
|
return;
|
|
group = tmp;
|
|
}
|
|
sprintf(dn, "cn=%s, %s", group, group_base);
|
|
|
|
/* make sure the group in question exists */
|
|
if ((lm = find(group, FALSE)) == (LDAPMessage *) NULL) {
|
|
printf(" Could not locate group \"%s\"\n", group);
|
|
return;
|
|
}
|
|
parse_answer(lm);
|
|
ldap_msgfree(lm);
|
|
|
|
/* none of this stuff changes */
|
|
mods[0] = &mod;
|
|
mods[1] = (LDAPMod *) NULL;
|
|
|
|
values[1] = NULL;
|
|
|
|
mod.mod_values = values;
|
|
mod.mod_type = "member";
|
|
mod.mod_op = LDAP_MOD_DELETE;
|
|
|
|
/*
|
|
* Now cycle through all of the names in the "members" part of the
|
|
* group (but not the e-mail address part). Lookup each one, and
|
|
* if it isn't found, let the user know so s/he can delete it.
|
|
*/
|
|
vp = Entry.attrs[attr_to_index("member")].values;
|
|
if (vp == NULL) {
|
|
if (verbose)
|
|
printf(" \"%s\" has no LDAP members. There is nothing to purge.\n", group);
|
|
return;
|
|
}
|
|
for (; *vp != NULL; vp++) {
|
|
char ans[BUFSIZ], *ufn, *label = "Did not find: ";
|
|
int len = strlen(label);
|
|
|
|
if (vrfy(*vp))
|
|
continue;
|
|
isclean = FALSE;
|
|
ufn = my_ldap_dn2ufn(*vp);
|
|
format2(ufn, label, (char *) NULL, 2, 2 + len, col_size);
|
|
ask:
|
|
printf(" Purge, Keep, Replace, Abort [Keep]? ");
|
|
fflush(stdout);
|
|
fetch_buffer(ans, sizeof(ans), stdin);
|
|
if ((ans[0] == '\0') || !strncasecmp(ans, "Keep", strlen(ans)))
|
|
continue;
|
|
if (!strncasecmp(ans, "Abort", strlen(ans))) {
|
|
ldap_uncache_entry(ld, dn);
|
|
return;
|
|
}
|
|
if (!strncasecmp(ans, "Purge", strlen(ans)) || !strncasecmp(ans, "Replace", strlen(ans))) {
|
|
values[0] = *vp;
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
register LDAPMod **lpp;
|
|
register char **cp;
|
|
register int i, j;
|
|
printf(" About to call ldap_modify_s()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", Entry.DN);
|
|
for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
|
|
printf(" mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
|
|
printf(" mods[%1d] type = %s\n", i, (*lpp)->mod_type);
|
|
for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
|
|
printf(" mods[%1d] v[%1d] = %s\n", i, j, *cp);
|
|
|
|
}
|
|
}
|
|
#endif
|
|
if (ldap_modify_s(ld, Entry.DN, mods))
|
|
mod_perror(ld);
|
|
|
|
/* now add the replacement if requested */
|
|
if (!strncasecmp(ans, "Purge", strlen(ans)))
|
|
continue;
|
|
rdns = ldap_explode_dn(*vp, TRUE);
|
|
if ((lm = find(*rdns, FALSE)) == NULL) {
|
|
printf(" Could not find a replacement for %s; purged only.\n", *rdns);
|
|
ldap_msgfree(lm);
|
|
ldap_value_free(rdns);
|
|
break;
|
|
}
|
|
values[0] = ldap_get_dn(ld, ldap_first_entry(ld, lm));
|
|
mod.mod_op = LDAP_MOD_ADD;
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
register LDAPMod **lpp;
|
|
register char **cp;
|
|
register int i, j;
|
|
printf(" About to call ldap_modify_s()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", Entry.DN);
|
|
for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
|
|
printf(" mods[%1d] code = %1d\n", i, (*lpp)->mod_op);
|
|
printf(" mods[%1d] type = %s\n", i, (*lpp)->mod_type);
|
|
for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
|
|
printf(" mods[%1d] v[%1d] = %s\n", i, j, *cp);
|
|
|
|
}
|
|
}
|
|
#endif
|
|
if (ldap_modify_s(ld, Entry.DN, mods))
|
|
mod_perror(ld);
|
|
ldap_msgfree(lm);
|
|
ldap_value_free(rdns);
|
|
|
|
/* set this back to DELETE for other purges */
|
|
mod.mod_op = LDAP_MOD_DELETE;
|
|
}
|
|
else {
|
|
printf(" Did not recognize that answer.\n\n");
|
|
goto ask;
|
|
}
|
|
}
|
|
ldap_uncache_entry(ld, Entry.DN);
|
|
if (isclean)
|
|
printf(" No entries were purged.\n");
|
|
return;
|
|
}
|
|
|
|
void
|
|
tidy_up( void )
|
|
{
|
|
register int i = 0;
|
|
int found_one = 0;
|
|
register char **vp;
|
|
LDAPMessage *lm;
|
|
static LDAPMod mod;
|
|
static LDAPMod *mods[2] = { &mod, NULL };
|
|
static char *values[MAX_VALUES];
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE)
|
|
printf("->tidy()\n");
|
|
#endif
|
|
|
|
if (bind_status == UD_NOT_BOUND) {
|
|
if (auth((char *) NULL, 1) < 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* lookup the user, and see to which groups he has subscribed */
|
|
vp = ldap_explode_dn(bound_dn, TRUE);
|
|
if ((lm = find(*vp, TRUE)) == (LDAPMessage *) NULL) {
|
|
printf(" Could not locate \"%s\"\n", *vp);
|
|
ldap_value_free(vp);
|
|
return;
|
|
}
|
|
ldap_value_free(vp);
|
|
parse_answer(lm);
|
|
ldap_msgfree(lm);
|
|
vp = Entry.attrs[attr_to_index("memberOfGroup")].values;
|
|
if (vp == NULL) {
|
|
printf(" You have not subscribed to any groups.\n");
|
|
return;
|
|
}
|
|
|
|
/* now, loop through these groups, deleting the bogus */
|
|
for ( ; *vp != NULL; vp++) {
|
|
if (vrfy(*vp))
|
|
continue;
|
|
found_one++;
|
|
printf(" \"%s\" is not a valid group name.\n", *vp);
|
|
values[i++] = strdup(*vp);
|
|
if ( i >= MAX_VALUES ) {
|
|
printf( " At most %d invalid groups can be removed at one time; skipping the rest.\n", MAX_VALUES );
|
|
break;
|
|
}
|
|
}
|
|
if (found_one == 0) {
|
|
if (verbose)
|
|
printf(" You are not a member of any invalid groups. There is nothing to tidy.\n");
|
|
return;
|
|
}
|
|
|
|
/* delete the most heinous entries */
|
|
values[i] = NULL;
|
|
mod.mod_values = values;
|
|
mod.mod_op = LDAP_MOD_DELETE;
|
|
mod.mod_type = "memberOfGroup";
|
|
if (ldap_modify_s(ld, bound_dn, mods))
|
|
mod_perror(ld);
|
|
ldap_uncache_entry(ld, bound_dn);
|
|
|
|
/* tidy up before we finish tidy_up */
|
|
for ( ; i >= 1; i--)
|
|
Free(values[i - 1]);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This routine is used to modify lists that can contain either Distinguished
|
|
* Names or e-mail addresses. This includes things like group members,
|
|
* the errors-to field in groups, and so on.
|
|
*/
|
|
void
|
|
mod_addrDN( char *group, int offset )
|
|
{
|
|
char s[BUFSIZ], *new_value /* was member */, *values[2];
|
|
char attrtype[ 64 ];
|
|
LDAPMod mod, *mods[2];
|
|
LDAPMessage *mp;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE)
|
|
printf("->mod_addrDN(%s)\n", group);
|
|
#endif
|
|
/*
|
|
* At this point the user can indicate that he wishes to add values
|
|
* to the attribute, delete values from the attribute, or replace the
|
|
* current list of values with a new list. The first two cases
|
|
* are very straight-forward, but the last case requires a little
|
|
* extra care and work.
|
|
*/
|
|
if (verbose) {
|
|
printf("\n");
|
|
if ( !isatty( 1 ))
|
|
format("There are three options available at this point. You may: Add additional values; Delete values; or Replace the entire list of values with a new list entered interactively.\n", 75, 2);
|
|
else
|
|
format("There are four options available at this point. You may: Add one or more additional values; Delete one or more existing values; Replace the entire list of values with a new list entered interactively; or Bulk load a new list of values from a file, overwriting the existing list.\n", 75, 2);
|
|
}
|
|
|
|
/* initialize the modififier type */
|
|
mod.mod_type = NULL;
|
|
|
|
for (;;) {
|
|
if ( !isatty( 1 ))
|
|
printf(" Do you want to Add, Delete, or Replace? ");
|
|
else
|
|
printf(" Do you want to Add, Delete, Replace, or Bulk load? ");
|
|
fflush(stdout);
|
|
fetch_buffer(s, sizeof(s), stdin);
|
|
if (s[0] == '\0') {
|
|
return;
|
|
/*NOTREACHED*/
|
|
}
|
|
if (!strncasecmp(s, "add", strlen(s))) {
|
|
mod.mod_op = LDAP_MOD_ADD;
|
|
break;
|
|
}
|
|
else if (!strncasecmp(s, "delete", strlen(s))) {
|
|
mod.mod_op = LDAP_MOD_DELETE;
|
|
break;
|
|
}
|
|
else if (!strncasecmp(s, "replace", strlen(s))) {
|
|
mod.mod_op = LDAP_MOD_REPLACE;
|
|
break;
|
|
}
|
|
else if(!strncasecmp(s, "bulk", strlen(s))) {
|
|
bulk_load(group);
|
|
return;
|
|
}
|
|
else if (verbose) {
|
|
printf("\n");
|
|
if ( !isatty( 1 ))
|
|
format("Did not recognize that response. Please use 'A' to add, 'D' to delete, or 'R' to replace the entire list with a new list\n", 75, 2);
|
|
else
|
|
format("Did not recognize that response. Please use 'A' to add, 'D' to delete, 'R' to replace the entire list with a new list, or 'B' to bulk load a new list from a file\n", 75, 2);
|
|
}
|
|
}
|
|
if (mod.mod_op == LDAP_MOD_REPLACE) {
|
|
if ( verbose && !confirm_action( "The entire existing list will be overwritten with the new values you are about to enter." )) {
|
|
printf("\n Modification halted.\n");
|
|
return;
|
|
}
|
|
}
|
|
if (verbose) {
|
|
printf("\n");
|
|
format("Values may be specified as a name (which is then looked up in the LDAP Directory) or as a domain-style (i.e., user@domain) e-mail address. Simply hit the RETURN key at the prompt when finished.\n", 75, 2);
|
|
printf("\n");
|
|
}
|
|
|
|
for (;;) {
|
|
printf("%s? ", attrlist[offset].output_string);
|
|
fflush(stdout);
|
|
fetch_buffer(s, sizeof(s), stdin);
|
|
if (s[0] == '\0')
|
|
return;
|
|
|
|
/*
|
|
* If the string the user has just typed has an @-sign in it,
|
|
* then we assume it is an e-mail address. In this case, we
|
|
* just store away whatever it is they have typed.
|
|
*
|
|
* If the string had no @-sign, then we look in the Directory,
|
|
* make sure it exists, and if it does, we add that.
|
|
*
|
|
* If the string begins with a comma, then strip off the
|
|
* comma, and pass it along to the LDAP server. This is
|
|
* the way one can force ud to accept a name. Handy for
|
|
* debugging purposes.
|
|
*/
|
|
if (*s == ',') {
|
|
new_value = s + 1;
|
|
mod.mod_type = attrlist[offset].quipu_name;
|
|
}
|
|
else if (strchr(s, '@') == NULL) {
|
|
if ((mp = find(s, FALSE)) == (LDAPMessage *) NULL) {
|
|
printf(" Could not find \"%s\"\n", s);
|
|
if (verbose && (mod.mod_op == LDAP_MOD_DELETE)){
|
|
printf("\n");
|
|
format("I could not find anything that matched what you typed. You might try the \"purge\" command instead. It is used to purge corrupted or unlocatable entries from a group.", 75, 2);
|
|
printf("\n");
|
|
}
|
|
continue;
|
|
}
|
|
parse_answer(mp);
|
|
new_value = Entry.DN;
|
|
mod.mod_type = attrlist[offset].quipu_name;
|
|
}
|
|
else if (mod.mod_op != LDAP_MOD_DELETE) {
|
|
/*
|
|
* Don't screw around with what the user has typed
|
|
* if they are simply trying to delete a rfc822mailbox
|
|
* value.
|
|
*
|
|
* spaces on the left hand side of the e-mail
|
|
* address are bad news - we know that there
|
|
* must be a @-sign in the string, or else we
|
|
* would not be here
|
|
*
|
|
* note that this means a value like:
|
|
*
|
|
* first m. last@host.domain
|
|
*
|
|
* will be turned into:
|
|
*
|
|
* first.m..last@host.domain
|
|
*
|
|
* and the mailer will need to do the right thing
|
|
* with this; alternatively we could add code that
|
|
* collapsed multiple dots into a single dot
|
|
*
|
|
* Don't screw up things like:
|
|
*
|
|
* "Bryan Beecher" <bryan@umich.edu>
|
|
* Bryan Beecher <bryan@umich.edu>
|
|
*/
|
|
char *cp;
|
|
if (strchr(s, '<') == NULL) {
|
|
for (cp = s; *cp != '@'; cp++)
|
|
if (isspace(*cp))
|
|
*cp = '.';
|
|
}
|
|
new_value = s;
|
|
strcpy(attrtype, "rfc822");
|
|
strcat(attrtype, attrlist[offset].quipu_name);
|
|
mod.mod_type = attrtype;
|
|
}
|
|
else {
|
|
new_value = s;
|
|
strcpy(attrtype, "rfc822");
|
|
strcat(attrtype, attrlist[offset].quipu_name);
|
|
mod.mod_type = attrtype;
|
|
}
|
|
|
|
/* fill in the rest of the ldap_mod() structure */
|
|
mods[0] = &mod;
|
|
mods[1] = (LDAPMod *) NULL;
|
|
|
|
values[0] = new_value;
|
|
values[1] = NULL;
|
|
mod.mod_values = values;
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
LDAPMod **lpp;
|
|
char **cp;
|
|
int i, j;
|
|
printf(" About to call ldap_modify_s()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", group);
|
|
for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
|
|
printf(" mods[%1d] code = %1d\n",
|
|
i, (*lpp)->mod_op);
|
|
printf(" mods[%1d] type = %s\n",
|
|
i, (*lpp)->mod_type);
|
|
for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
|
|
printf(" mods[%1d] v[%1d] = %s\n",
|
|
i, j, *cp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (my_ldap_modify_s(ld, group, mods)) {
|
|
int ld_errno = 0;
|
|
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
|
|
if (ld_errno == LDAP_NO_SUCH_ATTRIBUTE) {
|
|
printf(" Could not locate value \"%s\"\n",
|
|
new_value);
|
|
continue;
|
|
}
|
|
else {
|
|
mod_perror(ld);
|
|
return;
|
|
}
|
|
}
|
|
ldap_uncache_entry(ld, group);
|
|
|
|
/*
|
|
* If the operation was REPLACE, we now need to "zero out" the
|
|
* other "half" of the list (e.g., user specified an e-mail
|
|
* address; now we need to clear the DN part of the list).
|
|
*
|
|
* NOTE: WE HAVE ALREADY DONE HALF OF THE REPLACE ABOVE.
|
|
*
|
|
* Also, change the opcode to LDAP_MOD_ADD and give the user an
|
|
* opportunity to add additional members to the group. We
|
|
* only take this branch the very first time during a REPLACE
|
|
* operation.
|
|
*/
|
|
if (mod.mod_op == LDAP_MOD_REPLACE) {
|
|
if (!strncmp(mod.mod_type, "rfc822", 6))
|
|
mod.mod_type = mod.mod_type + 6;
|
|
else {
|
|
strcpy(attrtype, "rfc822");
|
|
strcat(attrtype, mod.mod_type);
|
|
mod.mod_type = attrtype;
|
|
}
|
|
mods[0] = &mod;
|
|
values[0] = NULL;
|
|
mod.mod_values = values;
|
|
mod.mod_op = LDAP_MOD_DELETE;
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS) {
|
|
LDAPMod **lpp;
|
|
char **cp;
|
|
int i, j;
|
|
printf(" About to call ldap_modify_s()\n");
|
|
printf(" ld = 0x%x\n", ld);
|
|
printf(" dn = [%s]\n", group);
|
|
for (lpp = mods, i = 1; *lpp != NULL; lpp++, i++) {
|
|
printf(" mods[%1d] code = %1d\n",
|
|
i, (*lpp)->mod_op);
|
|
printf(" mods[%1d] type = %s\n",
|
|
i, (*lpp)->mod_type);
|
|
for (cp = (*lpp)->mod_values, j = 1; *cp != NULL; cp++, j++)
|
|
printf(" mods[%1d] v[%1d] = %s\n", i, j, *cp);
|
|
}
|
|
}
|
|
#endif
|
|
if (my_ldap_modify_s(ld, group, mods)) {
|
|
/*
|
|
* A "No such attribute" error is no big deal.
|
|
* We only wanted to clear the attribute anyhow.
|
|
*/
|
|
int ld_errno = 0;
|
|
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
|
|
if (ld_errno != LDAP_NO_SUCH_ATTRIBUTE) {
|
|
mod_perror(ld);
|
|
return;
|
|
}
|
|
}
|
|
ldap_uncache_entry(ld, group);
|
|
if (verbose)
|
|
printf(" \"%s\" has been added\n", new_value);
|
|
mod.mod_op = LDAP_MOD_ADD;
|
|
}
|
|
else if (verbose && (mod.mod_op == LDAP_MOD_ADD))
|
|
printf(" \"%s\" has been added\n", new_value);
|
|
else if (verbose && (mod.mod_op == LDAP_MOD_DELETE))
|
|
printf(" \"%s\" has been removed\n", new_value);
|
|
}
|
|
}
|
|
|
|
int
|
|
my_ldap_modify_s( LDAP *ldap, char *group, LDAPMod **mods )
|
|
{
|
|
int was_rfc822member, rc;
|
|
|
|
was_rfc822member = 0;
|
|
|
|
if (!strcasecmp(mods[0]->mod_type, "rfc822member")) {
|
|
mods[0]->mod_type = "mail";
|
|
was_rfc822member = 1;
|
|
}
|
|
|
|
rc = ldap_modify_s(ldap, group, mods);
|
|
|
|
if (was_rfc822member)
|
|
mods[0]->mod_type = "rfc822member";
|
|
|
|
return(rc);
|
|
}
|
|
|
|
void
|
|
list_groups( char *who )
|
|
{
|
|
LDAPMessage *mp;
|
|
char name[BUFSIZ], filter[BUFSIZ], *search_attrs[2];
|
|
char *work_area[MAX_NUM_NAMES];
|
|
char *dn, **rdns;
|
|
int i, rc;
|
|
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE) {
|
|
if (who == NULL)
|
|
printf("->list_groups(NULL)\n");
|
|
else
|
|
printf("->list_groups(%s)\n", who);
|
|
}
|
|
#endif
|
|
/*
|
|
* First, decide what entry we are going to list. If the
|
|
* user has not included a name on the list command line,
|
|
* we will use the person who was last looked up with a find
|
|
* command.
|
|
*
|
|
* Once we know who to modify, be sure that they exist, and
|
|
* parse out their DN.
|
|
*/
|
|
if (who == NULL) {
|
|
printf(" List groups belonging to whose entry? ");
|
|
fflush(stdout);
|
|
fetch_buffer(name, sizeof(name), stdin);
|
|
if (name[0] != '\0')
|
|
who = name;
|
|
else
|
|
return;
|
|
}
|
|
if ((mp = find(who, TRUE)) == NULL) {
|
|
(void) ldap_msgfree(mp);
|
|
printf(" Could not locate \"%s\" in the Directory.\n", who);
|
|
return;
|
|
}
|
|
dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
|
|
ldap_msgfree(mp);
|
|
rdns = ldap_explode_dn(dn, TRUE);
|
|
if (verbose)
|
|
printf("\n Listing groups belonging to \"%s\"\n", *rdns);
|
|
|
|
/* lookup the groups belonging to this person */
|
|
sprintf(filter, "owner=%s", dn);
|
|
Free(dn);
|
|
search_attrs[0] = "cn";
|
|
search_attrs[1] = NULL;
|
|
if ((rc = ldap_search_s(ld, UD_WHERE_ALL_GROUPS_LIVE, LDAP_SCOPE_SUBTREE,
|
|
filter, search_attrs, FALSE, &mp)) != LDAP_SUCCESS &&
|
|
rc != LDAP_SIZELIMIT_EXCEEDED && rc != LDAP_TIMELIMIT_EXCEEDED) {
|
|
ldap_perror(ld, "ldap_search_s");
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|
|
if ((rc = ldap_count_entries(ld, mp)) < 0) {
|
|
ldap_perror(ld, "ldap_count_entries");
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|
|
if (rc == 0) {
|
|
printf(" %s owns no groups in this portion of the Directory.\n", *rdns);
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|
|
if (verbose)
|
|
printf(" %s owns %d groups.\n\n", *rdns, rc);
|
|
print_list(mp, work_area, &rc);
|
|
for (i = 1; work_area[i] != NULL; i++)
|
|
Free(work_area[i]);
|
|
ldap_msgfree(mp);
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|
|
|
|
static char *
|
|
bind_and_fetch( char *name )
|
|
{
|
|
LDAPMessage *lm;
|
|
char tmp[MED_BUF_SIZE];
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE) {
|
|
if (name == NULL)
|
|
printf("->bind_and_fetch(NULL)\n");
|
|
else
|
|
printf("->bind_and_fetch(%s)\n", name);
|
|
}
|
|
#endif
|
|
if (bind_status == UD_NOT_BOUND) {
|
|
if (auth((char *) NULL, 1) < 0)
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* If the user did not supply us with a name, prompt them for
|
|
* a name.
|
|
*/
|
|
if ((name == NULL) || (*name == '\0')) {
|
|
printf(" Group? ");
|
|
fflush(stdout);
|
|
fetch_buffer(tmp, sizeof(tmp), stdin);
|
|
if (tmp[0] == '\0')
|
|
return(NULL);
|
|
name = tmp;
|
|
}
|
|
/* remove quotes, dots, and underscores. */
|
|
name = strip_ignore_chars(name);
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS)
|
|
printf("Group name = (%s)\n", name);
|
|
#endif
|
|
|
|
/* make sure the group in question exists and is joinable */
|
|
if ((lm = find(name, TRUE)) == (LDAPMessage *) NULL) {
|
|
printf(" Could not locate group \"%s\"\n", name);
|
|
return(NULL);
|
|
}
|
|
parse_answer(lm);
|
|
ldap_msgfree(lm);
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_GROUPS)
|
|
printf("Group DN = (%s)\n", Entry.DN);
|
|
#endif
|
|
return(strdup(Entry.DN));
|
|
}
|
|
|
|
void
|
|
list_memberships( char *who )
|
|
{
|
|
LDAPMessage *mp;
|
|
char name[BUFSIZ], filter[BUFSIZ], *search_attrs[2];
|
|
char *work_area[MAX_NUM_NAMES];
|
|
char *dn, **rdns;
|
|
int i, rc;
|
|
|
|
|
|
#ifdef DEBUG
|
|
if (debug & D_TRACE) {
|
|
if (who == NULL)
|
|
printf("->list_memberships(NULL)\n");
|
|
else
|
|
printf("->list_memberships(%s)\n", who);
|
|
}
|
|
#endif
|
|
/*
|
|
* First, decide what entry we are going to list. If the
|
|
* user has not included a name on the list command line,
|
|
* we will use the person who was last looked up with a find
|
|
* command.
|
|
*
|
|
* Once we know who to modify, be sure that they exist, and
|
|
* parse out their DN.
|
|
*/
|
|
if (who == NULL) {
|
|
printf(" List memberships containing whose entry? ");
|
|
fflush(stdout);
|
|
fetch_buffer(name, sizeof(name), stdin);
|
|
if (name[0] != '\0')
|
|
who = name;
|
|
else
|
|
return;
|
|
}
|
|
if ((mp = find(who, TRUE)) == NULL) {
|
|
(void) ldap_msgfree(mp);
|
|
printf(" Could not locate \"%s\" in the Directory.\n", who);
|
|
ldap_msgfree(mp);
|
|
return;
|
|
}
|
|
dn = ldap_get_dn(ld, ldap_first_entry(ld, mp));
|
|
rdns = ldap_explode_dn(dn, TRUE);
|
|
if (verbose)
|
|
printf("\n Listing memberships of \"%s\"\n", *rdns);
|
|
|
|
/* lookup the groups belonging to this person */
|
|
sprintf(filter, "member=%s", dn);
|
|
Free(dn);
|
|
search_attrs[0] = "cn";
|
|
search_attrs[1] = NULL;
|
|
ldap_msgfree(mp);
|
|
if ((rc = ldap_search_s(ld, UD_WHERE_ALL_GROUPS_LIVE, LDAP_SCOPE_SUBTREE,
|
|
filter, search_attrs, FALSE, &mp)) != LDAP_SUCCESS &&
|
|
rc != LDAP_SIZELIMIT_EXCEEDED && rc != LDAP_TIMELIMIT_EXCEEDED) {
|
|
ldap_perror(ld, "ldap_search_s");
|
|
ldap_msgfree(mp);
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|
|
if ((rc = ldap_count_entries(ld, mp)) < 0) {
|
|
ldap_perror(ld, "ldap_count_entries");
|
|
ldap_msgfree(mp);
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|
|
if (rc == 0) {
|
|
printf(" %s is not a member of any groups in this portion of the Directory.\n", *rdns);
|
|
ldap_msgfree(mp);
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|
|
if (verbose)
|
|
printf(" %s is a member of %d groups.\n\n", *rdns, rc);
|
|
|
|
/*
|
|
* print_list fills in the char * array starting at 1, not 0
|
|
*/
|
|
print_list(mp, work_area, &rc);
|
|
for (i = 1; work_area[i] != NULL; i++)
|
|
Free(work_area[i]);
|
|
ldap_msgfree(mp);
|
|
ldap_value_free(rdns);
|
|
return;
|
|
}
|