mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
eed7408e7e
BerElement argument per latest IETF ldapext draft c api spec. That is, caller is solely responsible for freeing the BerElement allocated and returned by ldap_first_attribute. Update man pages accordingly. Update applications accordingly.
641 lines
15 KiB
C
641 lines
15 KiB
C
/*
|
||
* Copyright (c) 1991, 1993
|
||
* 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/ctype.h>
|
||
#include <ac/string.h>
|
||
#include <ac/time.h>
|
||
|
||
#include <lber.h>
|
||
#include <ldap.h>
|
||
|
||
#include "ud.h"
|
||
|
||
struct entry Entry;
|
||
|
||
static char *time2text(char *ldtimestr, int dateonly);
|
||
static long gtime(struct tm *tm);
|
||
|
||
/*
|
||
* When displaying entries, display only these attributes, and in this
|
||
* order.
|
||
*/
|
||
static char *person_attr_print_order[] = {
|
||
"cn",
|
||
"mail",
|
||
"telephoneNumber",
|
||
"facsimileTelephoneNumber",
|
||
"pager",
|
||
"postalAddress",
|
||
"title",
|
||
"uid",
|
||
"multiLineDescription",
|
||
"homePhone",
|
||
"homePostalAddress",
|
||
"drink",
|
||
"labeledURL",
|
||
"onVacation",
|
||
"vacationMessage",
|
||
"memberOfGroup",
|
||
"lastModifiedBy",
|
||
"lastModifiedTime",
|
||
NULL
|
||
};
|
||
|
||
static char *group_attr_print_order[] = {
|
||
"cn",
|
||
"facsimileTelephoneNumber",
|
||
"telephoneNumber",
|
||
"postalAddress",
|
||
"multiLineDescription",
|
||
"joinable",
|
||
"associatedDomain",
|
||
"owner",
|
||
"moderator",
|
||
"ErrorsTo",
|
||
"rfc822ErrorsTo",
|
||
"RequestsTo",
|
||
"rfc822RequestsTo",
|
||
"member",
|
||
"mail",
|
||
"labeledURL",
|
||
"lastModifiedBy",
|
||
"lastModifiedTime",
|
||
NULL
|
||
};
|
||
|
||
|
||
void
|
||
parse_answer( LDAPMessage *s )
|
||
{
|
||
int idx;
|
||
char **rdns;
|
||
register LDAPMessage *ep;
|
||
register char *ap;
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_TRACE)
|
||
printf("->parse_answer(%x)\n", s);
|
||
#endif
|
||
|
||
clear_entry();
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_PARSE)
|
||
printf(" Done clearing entry\n");
|
||
#endif
|
||
for (ep = ldap_first_entry(ld, s); ep != NULL; ep = ldap_next_entry(ld, ep)) {
|
||
BerElement *cookie = NULL;
|
||
#ifdef DEBUG
|
||
if (debug & D_PARSE)
|
||
printf(" Determining DN and name\n");
|
||
#endif
|
||
Entry.DN = ldap_get_dn(ld, ep);
|
||
#ifdef DEBUG
|
||
if (debug & D_PARSE)
|
||
printf(" DN = %s\n", Entry.DN);
|
||
#endif
|
||
rdns = ldap_explode_dn(Entry.DN, TRUE);
|
||
#ifdef DEBUG
|
||
if (debug & D_PARSE)
|
||
printf(" Name = %s\n", *rdns);
|
||
#endif
|
||
Entry.name = strdup(*rdns);
|
||
ldap_value_free(rdns);
|
||
for (ap = ldap_first_attribute(ld, ep, &cookie); ap != NULL; ap = ldap_next_attribute(ld, ep, cookie)) {
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_PARSE)
|
||
printf("parsing ap = %s\n", ap);
|
||
#endif
|
||
if ((idx = attr_to_index(ap)) < 0) {
|
||
printf(" Unknown attribute \"%s\"\n", ap);
|
||
continue;
|
||
}
|
||
add_value(&(Entry.attrs[idx]), ep, ap);
|
||
}
|
||
|
||
if( cookie != NULL ) {
|
||
ber_free( cookie, 0 );
|
||
}
|
||
}
|
||
#ifdef DEBUG
|
||
if (debug & D_PARSE)
|
||
printf(" Done parsing entry\n");
|
||
#endif
|
||
}
|
||
|
||
void
|
||
add_value( struct attribute *attr, LDAPMessage *ep, char *ap )
|
||
{
|
||
register int i = 0;
|
||
char **vp, **tp, **avp;
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_TRACE)
|
||
printf("->add_value(%x, %x, %s)\n", attr, ep, ap);
|
||
#endif
|
||
vp = (char **) ldap_get_values(ld, ep, ap);
|
||
|
||
/*
|
||
* Fill in the attribute structure for this attribute. This
|
||
* stores away the values (using strdup()) and the count. Terminate
|
||
* the list with a NULL pointer.
|
||
*
|
||
* attr->quipu_name has already been set during initialization.
|
||
*/
|
||
if ((attr->number_of_values = ldap_count_values(vp)) > 0) {
|
||
attr->values = (char **) Malloc((unsigned) ((attr->number_of_values + 1) * sizeof(char *)));
|
||
avp = attr->values;
|
||
|
||
for (i = 1, tp = vp; *tp != NULL; i++, tp++) {
|
||
#ifdef DEBUG
|
||
if (debug & D_PARSE)
|
||
printf(" value #%d %s\n", i, *tp);
|
||
#endif
|
||
/*
|
||
* The 'name' field of the Entry structure already has
|
||
* has the first part of the DN copied into it. Thus,
|
||
* we don't need to save it away here again. Also, by
|
||
* tossing it away here, we make printing this info out
|
||
* a bit easier later.
|
||
*/
|
||
if (!strcmp(ap, "cn") && !strcmp(*tp, Entry.name)) {
|
||
attr->number_of_values--;
|
||
continue;
|
||
}
|
||
*avp++ = strdup(*tp);
|
||
}
|
||
*avp = NULL;
|
||
}
|
||
ldap_value_free(vp);
|
||
}
|
||
|
||
void
|
||
print_an_entry( void )
|
||
{
|
||
int n = 0, i, idx;
|
||
char is_a_group, **order;
|
||
char *sub_list[MAX_VALUES], buf[SMALL_BUF_SIZE];
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_TRACE)
|
||
printf("->print_an_entry()\n");
|
||
#endif
|
||
printf(" \"%s\"\n", Entry.name);
|
||
|
||
/*
|
||
* If the entry is a group, find all of the subscribers to that
|
||
* group. A subscriber is an entry that *points* to a group entry,
|
||
* and a member is an entry that is included as part of a group
|
||
* entry.
|
||
*
|
||
* We also need to select the appropriate output format here.
|
||
*/
|
||
is_a_group = isgroup();
|
||
if (is_a_group) {
|
||
order = (char **) group_attr_print_order;
|
||
n = find_all_subscribers(sub_list, Entry.DN);
|
||
#ifdef DEBUG
|
||
if (debug & D_PRINT)
|
||
printf(" Group \"%s\" has %d subscribers\n",
|
||
Entry.name, n);
|
||
#endif
|
||
}
|
||
else
|
||
order = (char **) person_attr_print_order;
|
||
|
||
for (i = 0; order[i] != NULL; i++) {
|
||
idx = attr_to_index(order[i]);
|
||
#ifdef DEBUG
|
||
if (debug & D_PRINT) {
|
||
printf(" ATTR #%2d = %s [%s] (%d values)\n", i + 1,
|
||
Entry.attrs[idx].output_string,
|
||
Entry.attrs[idx].quipu_name,
|
||
Entry.attrs[idx].number_of_values);
|
||
}
|
||
#endif
|
||
if (idx < 0)
|
||
continue;
|
||
if (Entry.attrs[idx].number_of_values == 0)
|
||
continue;
|
||
if (isadn(order[i]))
|
||
print_DN(Entry.attrs[idx]);
|
||
else if (isaurl(order[i]))
|
||
print_URL(Entry.attrs[idx]);
|
||
else if (isadate(order[i])) {
|
||
/* fix time and date, then call usual routine */
|
||
Entry.attrs[idx].values[0] =
|
||
time2text(Entry.attrs[idx].values[0], FALSE);
|
||
print_values(Entry.attrs[idx]);
|
||
}
|
||
else
|
||
print_values(Entry.attrs[idx]);
|
||
}
|
||
|
||
/*
|
||
* If it is a group, then we should print the subscriber list (if
|
||
* there are any). If there are a lot of them, prompt the user
|
||
* before printing them.
|
||
*/
|
||
if (is_a_group && (n > 0)) {
|
||
char *label = "Subscribers: ";
|
||
|
||
if (n > TOO_MANY_TO_PRINT) {
|
||
printf(" There are %d subscribers. Print them? ", n);
|
||
fflush(stdout);
|
||
fetch_buffer(buf, sizeof(buf), stdin);
|
||
if (!((buf[0] == 'y') || (buf[0] == 'Y')))
|
||
return;
|
||
}
|
||
format2((char *) my_ldap_dn2ufn(sub_list[n - 1]), label, (char *) NULL, 2,
|
||
2 + strlen(label) + 1, col_size);
|
||
for (n--; n > 0; n--)
|
||
format2((char *) my_ldap_dn2ufn(sub_list[n - 1]), (char *) NULL,
|
||
(char *) NULL, 2 + strlen(label),
|
||
2 + strlen(label) + 2, col_size);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
#define OUT_LABEL_LEN 20
|
||
|
||
/* prints the values associated with an attribute */
|
||
void
|
||
print_values( struct attribute A )
|
||
{
|
||
register int i, k;
|
||
register char *cp, **vp;
|
||
char out_buf[MED_BUF_SIZE], *padding = NULL;
|
||
int lead;
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_TRACE)
|
||
printf("->print_values(%x)\n", A);
|
||
#endif
|
||
if (A.number_of_values == 0)
|
||
return;
|
||
if ((vp = A.values) == NULL)
|
||
return;
|
||
|
||
/*
|
||
* Pad out the output string label so that it fills the
|
||
* whole field of length OUT_LABEL_LEN.
|
||
*/
|
||
out_buf[0] = '\0';
|
||
i = OUT_LABEL_LEN - strlen(A.output_string);
|
||
if (i < 0) {
|
||
printf("Output string for \"%s\" is too long. Maximum length is %d characters\n", A.quipu_name, OUT_LABEL_LEN);
|
||
return;
|
||
}
|
||
if (isgroup() && !strcmp(A.quipu_name, "mail") && (Entry.attrs[attr_to_index("member")].number_of_values == 0)) {
|
||
A.output_string = "Members";
|
||
i = OUT_LABEL_LEN - strlen(A.output_string);
|
||
padding = (char *) Malloc((unsigned) (i + 1));
|
||
(void) memset(padding, ' ', i);
|
||
*(padding + i) = '\0';
|
||
sprintf(out_buf, "%s:%s", A.output_string, padding);
|
||
}
|
||
else if (!(isgroup() && !strcmp(A.quipu_name, "mail") && (Entry.attrs[attr_to_index("member")].number_of_values > 0))) {
|
||
padding = (char *) Malloc((unsigned) (i + 1));
|
||
(void) memset(padding, ' ', i);
|
||
*(padding + i) = '\0';
|
||
sprintf(out_buf, "%s:%s", A.output_string, padding);
|
||
}
|
||
/*
|
||
* If this happens to be a group, then do not print the output
|
||
* string if we have already printed out some members.
|
||
*/
|
||
else if (isgroup() && !strcmp(A.quipu_name, "mail") && (Entry.attrs[attr_to_index("member")].number_of_values > 0)) {
|
||
padding = (char *) Malloc((unsigned) (OUT_LABEL_LEN + 2));
|
||
(void) memset(padding, ' ', OUT_LABEL_LEN + 1);
|
||
*(padding + OUT_LABEL_LEN + 1) = '\0';
|
||
sprintf(out_buf, "%s", padding);
|
||
}
|
||
lead = strlen(out_buf) + 2;
|
||
|
||
printf(" %s", out_buf);
|
||
for (i = 0; *vp != NULL; i++, vp++) {
|
||
if (i > 0) {
|
||
if (!strncmp(A.output_string, "Home a", 6) || !strncmp(A.output_string, "Business a", 10)) {
|
||
printf(" %s", out_buf);
|
||
}
|
||
else {
|
||
for (k = lead; k > 0; k--)
|
||
putchar(' ');
|
||
}
|
||
}
|
||
for (cp = *vp; *cp != '\0'; cp++) {
|
||
switch (*cp) {
|
||
case '$' :
|
||
if (!strncmp(A.output_string, "Home a", 6) || !strncmp(A.output_string, "Business a", 10) || !strcmp(A.quipu_name, "multiLineDescription")) {
|
||
putchar('\n');
|
||
for (k = lead; k > 0; k--)
|
||
putchar(' ');
|
||
while (isspace(*(cp + 1)))
|
||
cp++;
|
||
}
|
||
else
|
||
putchar(*cp);
|
||
break;
|
||
case '\n' :
|
||
putchar('%');
|
||
putchar('\n');
|
||
break;
|
||
default:
|
||
putchar(*cp);
|
||
}
|
||
}
|
||
putchar('\n');
|
||
}
|
||
if (padding != NULL)
|
||
Free(padding);
|
||
return;
|
||
}
|
||
|
||
/* prints the DN's associated with an attribute */
|
||
void
|
||
print_DN( struct attribute A )
|
||
{
|
||
int i, lead;
|
||
register char **vp;
|
||
char out_buf[MED_BUF_SIZE], *padding = NULL;
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_TRACE)
|
||
printf("->print_DN(%x)\n", A);
|
||
#endif
|
||
if (A.number_of_values == 0)
|
||
return;
|
||
/*
|
||
* Pad out the output string label so that it fills the
|
||
* whole field of length OUT_LABEL_LEN.
|
||
*/
|
||
i = OUT_LABEL_LEN - strlen(A.output_string);
|
||
if (i > 0) {
|
||
padding = (char *) Malloc((unsigned) (i + 1));
|
||
(void) memset(padding, ' ', i);
|
||
*(padding + i) = '\0';
|
||
sprintf(out_buf, "%s:%s", A.output_string, padding);
|
||
(void) Free(padding);
|
||
}
|
||
lead = strlen(out_buf) + 2;
|
||
|
||
vp = A.values;
|
||
format2((char *) my_ldap_dn2ufn(*vp), out_buf, (char *) NULL, 2, lead + 1, col_size);
|
||
for (vp++; *vp != NULL; vp++) {
|
||
format2((char *) my_ldap_dn2ufn(*vp), (char *) NULL, (char *) NULL, lead,
|
||
lead + 1, col_size);
|
||
}
|
||
return;
|
||
}
|
||
|
||
void
|
||
clear_entry( void )
|
||
{
|
||
register int i;
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_TRACE)
|
||
printf("->clear_entry()\n");
|
||
if ((debug & D_PRINT) && (Entry.name != NULL))
|
||
printf(" Clearing entry \"%s\"\n", Entry.name);
|
||
#endif
|
||
if (Entry.DN != NULL)
|
||
Free(Entry.DN);
|
||
if (Entry.name != NULL)
|
||
Free(Entry.name);
|
||
Entry.may_join = FALSE;
|
||
Entry.subscriber_count = -1;
|
||
Entry.DN = Entry.name = NULL;
|
||
|
||
/* clear all of the values associated with all attributes */
|
||
for (i = 0; attrlist[i].quipu_name != NULL; i++) {
|
||
#ifdef DEBUG
|
||
if (debug & D_PRINT)
|
||
printf(" Clearing attribute \"%s\" -- ",
|
||
Entry.attrs[i].quipu_name);
|
||
#endif
|
||
if (Entry.attrs[i].values == NULL) {
|
||
#ifdef DEBUG
|
||
if (debug & D_PRINT)
|
||
printf(" no values, skipping\n");
|
||
#endif
|
||
continue;
|
||
}
|
||
#ifdef DEBUG
|
||
if (debug & D_PRINT)
|
||
printf(" freeing %d values\n",
|
||
Entry.attrs[i].number_of_values);
|
||
#endif
|
||
Entry.attrs[i].number_of_values = 0;
|
||
ldap_value_free(Entry.attrs[i].values);
|
||
Entry.attrs[i].values = (char **) NULL;
|
||
|
||
/*
|
||
* Note: We do not clear either of the char * fields
|
||
* since they will always be applicable.
|
||
*/
|
||
}
|
||
}
|
||
|
||
int
|
||
attr_to_index( char *s )
|
||
{
|
||
register int i;
|
||
|
||
for (i = 0; attrlist[i].quipu_name != NULL; i++)
|
||
if (!strcasecmp(s, attrlist[i].quipu_name))
|
||
return(i);
|
||
return(-1);
|
||
}
|
||
|
||
void
|
||
initialize_attribute_strings( void )
|
||
{
|
||
register int i;
|
||
|
||
for (i = 0; attrlist[i].quipu_name != NULL; i++)
|
||
Entry.attrs[i].quipu_name = attrlist[i].quipu_name;
|
||
for (i = 0; attrlist[i].quipu_name != NULL; i++)
|
||
Entry.attrs[i].output_string = attrlist[i].output_string;
|
||
}
|
||
|
||
/* prints the URL/label pairs associated with an attribute */
|
||
void
|
||
print_URL( struct attribute A )
|
||
{
|
||
int i, lead;
|
||
register char **vp;
|
||
char out_buf[MED_BUF_SIZE], *padding = NULL;
|
||
|
||
#ifdef DEBUG
|
||
if (debug & D_TRACE)
|
||
printf("->print_URL(%x)\n", A);
|
||
#endif
|
||
if (A.number_of_values == 0)
|
||
return;
|
||
/*
|
||
* Pad out the output string label so that it fills the
|
||
* whole field of length OUT_LABEL_LEN.
|
||
*/
|
||
i = OUT_LABEL_LEN - strlen(A.output_string);
|
||
if (i > 0) {
|
||
padding = (char *) Malloc((unsigned) (i + 1));
|
||
(void) memset(padding, ' ', i);
|
||
*(padding + i) = '\0';
|
||
sprintf(out_buf, "%s:%s", A.output_string, padding);
|
||
}
|
||
lead = strlen(out_buf) + 2;
|
||
|
||
vp = A.values;
|
||
print_one_URL(*vp, 2, out_buf, lead);
|
||
for (vp++; *vp != NULL; vp++)
|
||
print_one_URL(*vp, lead, (char *) NULL, lead);
|
||
if (padding != NULL)
|
||
Free(padding);
|
||
return;
|
||
}
|
||
|
||
void
|
||
print_one_URL( char *s, int label_lead, char *tag, int url_lead )
|
||
{
|
||
register int i;
|
||
char c, *cp, *url;
|
||
|
||
for (cp = s; !isspace(*cp) && (*cp != '\0'); cp++)
|
||
;
|
||
c = *cp;
|
||
*cp = '\0';
|
||
url = strdup(s);
|
||
*cp = c;
|
||
if (*cp != '\0') {
|
||
for (cp++; isspace(*cp); cp++)
|
||
;
|
||
}
|
||
else
|
||
cp = "(no description available)";
|
||
format2(cp, tag, (char *) NULL, label_lead, label_lead + 1, col_size);
|
||
for (i = url_lead + 2; i > 0; i--)
|
||
printf(" ");
|
||
printf("%s\n", url);
|
||
Free(url);
|
||
}
|
||
|
||
|
||
#define GET2BYTENUM( p ) (( *p - '0' ) * 10 + ( *(p+1) - '0' ))
|
||
|
||
static char *
|
||
time2text( char *ldtimestr, int dateonly )
|
||
{
|
||
struct tm t;
|
||
char *p, *timestr, zone, *fmterr = "badly formatted time";
|
||
time_t gmttime;
|
||
|
||
memset( (char *)&t, 0, sizeof( struct tm ));
|
||
if ( strlen( ldtimestr ) < 13 ) {
|
||
return( fmterr );
|
||
}
|
||
|
||
for ( p = ldtimestr; p - ldtimestr < 12; ++p ) {
|
||
if ( !isdigit( *p )) {
|
||
return( fmterr );
|
||
}
|
||
}
|
||
|
||
p = ldtimestr;
|
||
t.tm_year = GET2BYTENUM( p ); p += 2;
|
||
t.tm_mon = GET2BYTENUM( p ) - 1; p += 2;
|
||
t.tm_mday = GET2BYTENUM( p ); p += 2;
|
||
t.tm_hour = GET2BYTENUM( p ); p += 2;
|
||
t.tm_min = GET2BYTENUM( p ); p += 2;
|
||
t.tm_sec = GET2BYTENUM( p ); p += 2;
|
||
|
||
if (( zone = *p ) == 'Z' ) { /* GMT */
|
||
zone = '\0'; /* no need to indicate on screen, so we make it null */
|
||
}
|
||
|
||
gmttime = gtime( &t );
|
||
timestr = ctime( &gmttime );
|
||
|
||
timestr[ strlen( timestr ) - 1 ] = zone; /* replace trailing newline */
|
||
if ( dateonly ) {
|
||
SAFEMEMCPY( timestr + 11, timestr + 20, strlen( timestr + 20 ) + 1 );
|
||
}
|
||
|
||
Free ( ldtimestr );
|
||
return( strdup( timestr ) );
|
||
}
|
||
|
||
|
||
/* gtime.c - inverse gmtime */
|
||
|
||
#include <ac/time.h>
|
||
|
||
/* gtime(): the inverse of localtime().
|
||
This routine was supplied by Mike Accetta at CMU many years ago.
|
||
*/
|
||
|
||
int dmsize[] = {
|
||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||
};
|
||
|
||
#define dysize(y) \
|
||
(((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
|
||
|
||
#define YEAR(y) ((y) >= 100 ? (y) : (y) + 1900)
|
||
|
||
/* */
|
||
|
||
static long
|
||
gtime( struct tm *tm )
|
||
{
|
||
register int i,
|
||
sec,
|
||
mins,
|
||
hour,
|
||
mday,
|
||
mon,
|
||
year;
|
||
register long result;
|
||
|
||
if ((sec = tm -> tm_sec) < 0 || sec > 59
|
||
|| (mins = tm -> tm_min) < 0 || mins > 59
|
||
|| (hour = tm -> tm_hour) < 0 || hour > 24
|
||
|| (mday = tm -> tm_mday) < 1 || mday > 31
|
||
|| (mon = tm -> tm_mon + 1) < 1 || mon > 12)
|
||
return ((long) -1);
|
||
if (hour == 24) {
|
||
hour = 0;
|
||
mday++;
|
||
}
|
||
year = YEAR (tm -> tm_year);
|
||
|
||
result = 0L;
|
||
for (i = 1970; i < year; i++)
|
||
result += dysize (i);
|
||
if (dysize (year) == 366 && mon >= 3)
|
||
result++;
|
||
while (--mon)
|
||
result += dmsize[mon - 1];
|
||
result += mday - 1;
|
||
result = 24 * result + hour;
|
||
result = 60 * result + mins;
|
||
result = 60 * result + sec;
|
||
|
||
return result;
|
||
}
|