]> git.sur5r.net Git - openldap/blob - clients/ud/print.c
Round one of LDAP_F() macro changes. In this round we rename
[openldap] / clients / ud / print.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * Copyright (c) 1991, 1993 
8  * Regents of the University of Michigan.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that this notice is preserved and that due credit is given
12  * to the University of Michigan at Ann Arbor. The name of the University
13  * may not be used to endorse or promote products derived from this
14  * software without specific prior written permission. This software
15  * is provided ``as is'' without express or implied warranty.
16  */
17
18 #include "portable.h"
19
20 #include <stdio.h>
21
22 #include <ac/ctype.h>
23 #include <ac/string.h>
24 #include <ac/time.h>
25
26 #include <lber.h>
27 #include <ldap.h>
28
29 #include "ud.h"
30
31 struct entry Entry;
32
33 static char *time2text(char *ldtimestr, int dateonly);
34 static long             gtime(struct tm *tm);
35
36 /*
37  *  When displaying entries, display only these attributes, and in this
38  *  order.
39  */
40 static char *person_attr_print_order[] = {
41         "cn",
42         "mail",
43         "telephoneNumber",
44         "facsimileTelephoneNumber",
45         "pager",
46         "postalAddress",
47         "title",
48         "uid",
49         "multiLineDescription",
50         "homePhone",
51         "homePostalAddress",
52         "drink",
53         "labeledURL",
54         "onVacation",
55         "vacationMessage",
56         "memberOfGroup",
57         "lastModifiedBy",
58         "lastModifiedTime",
59         "modifiersname",
60         "modifytimestamp",
61         NULL
62 };
63
64 static char *group_attr_print_order[] = {
65         "cn",
66         "facsimileTelephoneNumber",
67         "telephoneNumber",
68         "postalAddress",
69         "multiLineDescription",
70         "joinable",
71         "associatedDomain",
72         "owner",
73         "moderator",
74         "ErrorsTo",
75         "rfc822ErrorsTo",
76         "RequestsTo",
77         "rfc822RequestsTo",
78         "member",
79         "mail",
80         "labeledURL",
81         "lastModifiedBy",
82         "lastModifiedTime",
83         "modifiersname",
84         "modifytimestamp",
85         "creatorsname",
86         "createtimestamp",
87         NULL
88 };
89
90
91 void
92 parse_answer( LDAPMessage *s )
93 {
94         int idx;
95         char **rdns;
96         register LDAPMessage *ep;
97         register char *ap;
98
99 #ifdef DEBUG
100         if (debug & D_TRACE)
101                 printf("->parse_answer(%x)\n", s);
102 #endif
103
104         clear_entry();
105
106 #ifdef DEBUG
107         if (debug & D_PARSE)
108                 printf(" Done clearing entry\n");
109 #endif
110         for (ep = ldap_first_entry(ld, s); ep != NULL; ep = ldap_next_entry(ld, ep)) {
111                 BerElement *cookie = NULL;
112 #ifdef DEBUG
113                 if (debug & D_PARSE)
114                         printf(" Determining DN and name\n");
115 #endif
116                 Entry.DN = ldap_get_dn(ld, ep);
117 #ifdef DEBUG
118                 if (debug & D_PARSE)
119                         printf(" DN = %s\n", Entry.DN);
120 #endif
121                 rdns = ldap_explode_dn(Entry.DN, TRUE);
122 #ifdef DEBUG
123                 if (debug & D_PARSE)
124                         printf(" Name = %s\n", *rdns);
125 #endif
126                 Entry.name = strdup(*rdns);
127                 ldap_value_free(rdns);
128                 for (ap = ldap_first_attribute(ld, ep, &cookie); ap != NULL; ap = ldap_next_attribute(ld, ep, cookie)) {
129
130 #ifdef DEBUG
131                         if (debug & D_PARSE)
132                                 printf("parsing ap = %s\n", ap);
133 #endif
134                         if ((idx = attr_to_index(ap)) < 0) {
135                                 printf("  Unknown attribute \"%s\"\n", ap);
136                                 continue;
137                         }
138                         add_value(&(Entry.attrs[idx]), ep, ap);
139                 }
140
141                 if( cookie != NULL ) {
142                         ber_free( cookie, 0 );
143                 }
144         }
145 #ifdef DEBUG
146         if (debug & D_PARSE)
147                 printf(" Done parsing entry\n");
148 #endif
149 }
150
151 void
152 add_value( struct attribute *attr, LDAPMessage *ep, char *ap )
153 {
154         register int i = 0;
155         char **vp, **tp, **avp;
156
157 #ifdef DEBUG
158         if (debug & D_TRACE)
159                 printf("->add_value(%x, %x, %s)\n", attr, ep, ap);
160 #endif
161         vp = (char **) ldap_get_values(ld, ep, ap);
162
163         /*
164          *  Fill in the attribute structure for this attribute.  This
165          *  stores away the values (using strdup()) and the count.  Terminate
166          *  the list with a NULL pointer.
167          *
168          *  attr->quipu_name has already been set during initialization.
169          */
170         if ((attr->number_of_values = ldap_count_values(vp)) > 0) {
171                 attr->values = (char **) Malloc((unsigned) ((attr->number_of_values + 1) * sizeof(char *)));
172                 avp = attr->values;
173
174                 for (i = 1, tp = vp; *tp != NULL; i++, tp++) {
175 #ifdef DEBUG
176                         if (debug & D_PARSE)
177                                 printf("  value #%d  %s\n", i, *tp);
178 #endif
179                         /*
180                          *  The 'name' field of the Entry structure already has
181                          *  has the first part of the DN copied into it.  Thus,
182                          *  we don't need to save it away here again.  Also, by
183                          *  tossing it away here, we make printing this info out
184                          *  a bit easier later.
185                          */
186                         if (!strcmp(ap, "cn") && !strcmp(*tp, Entry.name)) {
187                                 attr->number_of_values--;
188                                 continue;
189                         }
190                         *avp++ = strdup(*tp);
191                 }
192                 *avp = NULL;
193         }
194         ldap_value_free(vp);
195 }
196
197 void
198 print_an_entry( void )
199 {
200         int n = 0, i, idx;
201         char is_a_group, **order;
202         char *sub_list[MAX_VALUES], buf[SMALL_BUF_SIZE];
203
204 #ifdef DEBUG
205         if (debug & D_TRACE)
206                 printf("->print_an_entry()\n");
207 #endif
208
209         if( Entry.name == NULL ) {
210                 printf(" No Entry found.\n");
211                 return;
212         }
213
214         printf(" \"%s\"\n", Entry.name);
215         
216         /*
217          *  If the entry is a group, find all of the subscribers to that
218          *  group.  A subscriber is an entry that *points* to a group entry,
219          *  and a member is an entry that is included as part of a group
220          *  entry.
221          *
222          *  We also need to select the appropriate output format here.
223          */
224         is_a_group = isgroup();
225         if (is_a_group) {
226                 order = (char **) group_attr_print_order;
227                 n = find_all_subscribers(sub_list, Entry.DN);
228 #ifdef DEBUG
229                 if (debug & D_PRINT)
230                         printf(" Group \"%s\" has %d subscribers\n", 
231                                                                 Entry.name, n);
232 #endif
233         }
234         else
235                 order = (char **) person_attr_print_order;
236
237         for (i = 0; order[i] != NULL; i++) {
238                 idx = attr_to_index(order[i]);
239 #ifdef DEBUG
240                 if (debug & D_PRINT) {
241                         printf("  ATTR #%2d = %s [%s] (%d values)\n", i + 1,
242                                 Entry.attrs[idx].output_string,
243                                 Entry.attrs[idx].quipu_name,
244                                 Entry.attrs[idx].number_of_values);
245                 }
246 #endif
247                 if (idx < 0)
248                         continue;
249                 if (Entry.attrs[idx].number_of_values == 0)
250                         continue;
251                 if (isadn(order[i]))
252                         print_DN(Entry.attrs[idx]);
253                 else if (isaurl(order[i]))
254                         print_URL(Entry.attrs[idx]);
255                 else if (isadate(order[i])) {
256                         /* fix time and date, then call usual routine */
257                         Entry.attrs[idx].values[0] = 
258                                 time2text(Entry.attrs[idx].values[0], FALSE);
259                         print_values(Entry.attrs[idx]);
260                 }
261                 else
262                         print_values(Entry.attrs[idx]);
263         }
264
265         /*
266          *  If it is a group, then we should print the subscriber list (if
267          *  there are any).  If there are a lot of them, prompt the user
268          *  before printing them.
269          */
270         if (is_a_group && (n > 0)) {
271                 char *label = "Subscribers:         ";
272
273                 if (n > TOO_MANY_TO_PRINT) {
274                         printf("  There are %d subscribers.  Print them? ", n);
275                         fflush(stdout);
276                         fetch_buffer(buf, sizeof(buf), stdin);
277                         if (!((buf[0] == 'y') || (buf[0] == 'Y')))
278                                 return;
279                 }
280                 format2((char *) my_ldap_dn2ufn(sub_list[n - 1]), label, (char *) NULL, 2, 
281                                                 2 + strlen(label) + 1, col_size); 
282                 for (n--; n > 0; n--)
283                         format2((char *) my_ldap_dn2ufn(sub_list[n - 1]), (char *) NULL, 
284                                 (char *) NULL, 2 + strlen(label), 
285                                 2 + strlen(label) + 2, col_size); 
286         }
287
288         return;
289 }
290
291 #define OUT_LABEL_LEN   20
292
293 /* prints the values associated with an attribute */
294 void
295 print_values( struct attribute A )
296 {
297         register int i, k;
298         register char *cp, **vp;
299         char out_buf[MED_BUF_SIZE], *padding = NULL;
300         int lead;
301
302 #ifdef DEBUG
303         if (debug & D_TRACE)
304                 printf("->print_values(%x)\n", A);
305 #endif
306         if (A.number_of_values == 0)
307                 return;
308         if ((vp = A.values) == NULL)
309                 return;
310
311         /*
312          *  Pad out the output string label so that it fills the
313          *  whole field of length OUT_LABEL_LEN.
314          */
315         out_buf[0] = '\0';
316         i = OUT_LABEL_LEN - strlen(A.output_string);
317         if (i < 0) {
318                 printf("Output string for \"%s\" is too long.  Maximum length is %d characters\n", A.quipu_name, OUT_LABEL_LEN);
319                 return;
320         }
321         if (isgroup() && !strcmp(A.quipu_name, "mail") && (Entry.attrs[attr_to_index("member")].number_of_values == 0)) {
322                 A.output_string = "Members";
323                 i = OUT_LABEL_LEN - strlen(A.output_string);
324                 padding = (char *) Malloc((unsigned) (i + 1));
325                 (void) memset(padding, ' ', i);
326                 *(padding + i) = '\0';
327                 sprintf(out_buf, "%s:%s", A.output_string, padding);
328         }
329         else if (!(isgroup() && !strcmp(A.quipu_name, "mail") && (Entry.attrs[attr_to_index("member")].number_of_values > 0))) {
330                 padding = (char *) Malloc((unsigned) (i + 1));
331                 (void) memset(padding, ' ', i);
332                 *(padding + i) = '\0';
333                 sprintf(out_buf, "%s:%s", A.output_string, padding);
334         }
335         /*
336          *  If this happens to be a group, then do not print the output
337          *  string if we have already printed out some members.
338          */
339         else if (isgroup() && !strcmp(A.quipu_name, "mail") && (Entry.attrs[attr_to_index("member")].number_of_values > 0)) {
340                 padding = (char *) Malloc((unsigned) (OUT_LABEL_LEN + 2));
341                 (void) memset(padding, ' ', OUT_LABEL_LEN + 1);
342                 *(padding + OUT_LABEL_LEN + 1) = '\0';
343                 sprintf(out_buf, "%s", padding);
344         }
345         lead = strlen(out_buf) + 2;
346
347         printf("  %s", out_buf);
348         for (i = 0; *vp != NULL; i++, vp++) {
349                 if (i > 0) {
350                         if (!strncmp(A.output_string, "Home a", 6) || !strncmp(A.output_string, "Business a", 10)) {
351                                 printf("  %s", out_buf);
352                         }
353                         else {
354                                 for (k = lead; k > 0; k--)
355                                         putchar(' ');
356                         }
357                 }
358                 for (cp = *vp; *cp != '\0'; cp++) {
359                         switch (*cp) {
360                         case '$' :
361                                 if (!strncmp(A.output_string, "Home a", 6) || !strncmp(A.output_string, "Business a", 10) || !strcmp(A.quipu_name, "multiLineDescription")) {
362                                         putchar('\n');
363                                         for (k = lead; k > 0; k--)
364                                                 putchar(' ');
365                                         while (isspace((unsigned char) cp[1]))
366                                                 cp++;
367                                 }
368                                 else
369                                         putchar(*cp);
370                                 break;
371                         case '\n' :
372                                 putchar('%');
373                                 putchar('\n');
374                                 break;
375                         default:
376                                 putchar(*cp);
377                         }
378                 }
379                 putchar('\n');
380         }
381         if (padding != NULL)
382                 Free(padding);
383         return;
384 }
385
386 /* prints the DN's associated with an attribute */
387 void
388 print_DN( struct attribute A )
389 {
390         int i, lead;
391         register char **vp;
392         char out_buf[MED_BUF_SIZE], *padding = NULL;
393
394 #ifdef DEBUG
395         if (debug & D_TRACE)
396                 printf("->print_DN(%x)\n", A);
397 #endif
398         if (A.number_of_values == 0)
399                 return;
400         /*
401          *  Pad out the output string label so that it fills the
402          *  whole field of length OUT_LABEL_LEN.
403          */
404         i = OUT_LABEL_LEN - strlen(A.output_string);
405         if (i > 0) {
406                 padding = (char *) Malloc((unsigned) (i + 1));
407                 (void) memset(padding, ' ', i);
408                 *(padding + i) = '\0';
409                 sprintf(out_buf, "%s:%s", A.output_string, padding);
410                 (void) Free(padding);
411         }
412         lead = strlen(out_buf) + 2;
413
414         vp = A.values;
415         format2((char *) my_ldap_dn2ufn(*vp), out_buf, (char *) NULL, 2, lead + 1, col_size); 
416         for (vp++; *vp != NULL; vp++) {
417                 format2((char *) my_ldap_dn2ufn(*vp), (char *) NULL, (char *) NULL, lead, 
418                         lead + 1, col_size); 
419         }
420         return;
421 }
422
423 void
424 clear_entry( void )
425 {
426         register int i;
427
428 #ifdef DEBUG
429         if (debug & D_TRACE)
430                 printf("->clear_entry()\n");
431         if ((debug & D_PRINT) && (Entry.name != NULL))
432                 printf(" Clearing entry \"%s\"\n", Entry.name);
433 #endif
434         if (Entry.DN != NULL)
435                 ldap_memfree(Entry.DN);
436         if (Entry.name != NULL)
437                 Free(Entry.name);
438         Entry.may_join = FALSE;
439         Entry.subscriber_count = -1;
440         Entry.DN = Entry.name = NULL;
441
442         /*  clear all of the values associated with all attributes */
443         for (i = 0; attrlist[i].quipu_name != NULL; i++) {
444 #ifdef DEBUG
445                 if (debug & D_PRINT)
446                         printf(" Clearing attribute \"%s\" -- ", 
447                                 Entry.attrs[i].quipu_name);
448 #endif
449                 if (Entry.attrs[i].values == NULL) {
450 #ifdef DEBUG
451                         if (debug & D_PRINT)
452                                 printf(" no values, skipping\n");
453 #endif
454                         continue;
455                 }
456 #ifdef DEBUG
457                 if (debug & D_PRINT)
458                         printf(" freeing %d values\n", 
459                                         Entry.attrs[i].number_of_values);
460 #endif
461                 Entry.attrs[i].number_of_values = 0;
462                 ldap_value_free(Entry.attrs[i].values);
463                 Entry.attrs[i].values = (char **) NULL;
464
465                 /*
466                  *  Note:  We do not clear either of the char * fields
467                  *  since they will always be applicable.
468                  */
469         }
470 }
471
472 int
473 attr_to_index( char *s )
474 {
475         register int i;
476
477         for (i = 0; attrlist[i].quipu_name != NULL; i++)
478                 if (!strcasecmp(s, attrlist[i].quipu_name))
479                         return(i);
480         return(-1);
481 }
482
483 void
484 initialize_attribute_strings( void )
485 {
486         register int i;
487
488         for (i = 0; attrlist[i].quipu_name != NULL; i++)
489                 Entry.attrs[i].quipu_name = attrlist[i].quipu_name;
490         for (i = 0; attrlist[i].quipu_name != NULL; i++)
491                 Entry.attrs[i].output_string = attrlist[i].output_string;
492 }
493
494 /* prints the URL/label pairs associated with an attribute */
495 void
496 print_URL( struct attribute A )
497 {
498         int i, lead;
499         register char **vp;
500         char out_buf[MED_BUF_SIZE], *padding = NULL;
501
502 #ifdef DEBUG
503         if (debug & D_TRACE)
504                 printf("->print_URL(%x)\n", A);
505 #endif
506         if (A.number_of_values == 0)
507                 return;
508         /*
509          *  Pad out the output string label so that it fills the
510          *  whole field of length OUT_LABEL_LEN.
511          */
512         i = OUT_LABEL_LEN - strlen(A.output_string);
513         if (i > 0) {
514                 padding = (char *) Malloc((unsigned) (i + 1));
515                 (void) memset(padding, ' ', i);
516                 *(padding + i) = '\0';
517                 sprintf(out_buf, "%s:%s", A.output_string, padding);
518         }
519         lead = strlen(out_buf) + 2;
520
521         vp = A.values;
522         print_one_URL(*vp, 2, out_buf, lead);
523         for (vp++; *vp != NULL; vp++)
524                 print_one_URL(*vp, lead, (char *) NULL, lead);
525         if (padding != NULL)
526                 Free(padding);
527         return;
528 }
529
530 void
531 print_one_URL( char *s, int label_lead, char *tag, int url_lead )
532 {
533         register int i;
534         char c, *cp, *url;
535
536         for (cp = s; !isspace((unsigned char)*cp) && (*cp != '\0'); cp++)
537                 ;
538         c = *cp;
539         *cp = '\0';
540         url = strdup(s);
541         *cp = c;
542         if (*cp != '\0') {
543                 for (cp++; isspace((unsigned char)*cp); cp++)
544                         ;
545         }
546         else
547                 cp = "(no description available)";
548         format2(cp, tag, (char *) NULL, label_lead, label_lead + 1, col_size);
549         for (i = url_lead + 2; i > 0; i--)
550                 printf(" ");
551         printf("%s\n", url);
552         Free(url);
553 }
554
555
556 #define GET2BYTENUM( p )        (( *(p) - '0' ) * 10 + ( *((p)+1) - '0' ))
557
558 static char *
559 time2text( char *ldtimestr, int dateonly )
560 {
561     struct tm           t;
562     char                *p, *timestr, zone, *fmterr = "badly formatted time";
563     time_t              gmttime;
564         int ndigits;
565
566         if (strlen( ldtimestr ) < 12 ) {
567                 return( fmterr );
568         }
569
570     for ( ndigits=0; isdigit((unsigned char) ldtimestr[ndigits]); ndigits++) {
571                 ; /* EMPTY */
572     }
573
574         if ( ndigits != 12 && ndigits != 14) {
575             return( fmterr );
576         }
577         
578     memset( (char *)&t, '\0', sizeof( struct tm ));
579
580     p = ldtimestr;
581
582         if( ndigits == 14) {
583                 /* came with a century */
584                 /* POSIX says tm_year should be year - 1900 */
585         t.tm_year = 100 * GET2BYTENUM( p ) - 1900;
586                 p += 2;
587         } else {
588         t.tm_year = 0;
589         }
590     t.tm_year += GET2BYTENUM( p ); p += 2;
591
592     t.tm_mon = GET2BYTENUM( p ) - 1; p += 2;
593     t.tm_mday = GET2BYTENUM( p ); p += 2;
594     t.tm_hour = GET2BYTENUM( p ); p += 2;
595     t.tm_min = GET2BYTENUM( p ); p += 2;
596     t.tm_sec = GET2BYTENUM( p ); p += 2;
597
598     if (( zone = *p ) == 'Z' ) {        /* GMT */
599         zone = '\0';    /* no need to indicate on screen, so we make it null */
600     }
601
602     gmttime = gtime( &t );
603     timestr = ctime( &gmttime );
604
605     timestr[ strlen( timestr ) - 1 ] = zone;    /* replace trailing newline */
606     if ( dateonly ) {
607         SAFEMEMCPY( timestr + 11, timestr + 20, strlen( timestr + 20 ) + 1 );
608     }
609
610     return( strdup( timestr ) );
611 }
612
613
614 /* gtime.c - inverse gmtime */
615
616 #include <ac/time.h>
617
618 /* gtime(): the inverse of localtime().
619         This routine was supplied by Mike Accetta at CMU many years ago.
620  */
621
622 int     dmsize[] = {
623     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
624 };
625
626 #define dysize(y)       \
627         (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
628
629 /*
630  * Y2K YEAR
631  */
632         /* per STDC & POSIX tm_year *should* be offset by 1900 */
633 #define YEAR_POSIX(y)           ((y) + 1900)
634
635         /*
636          * year is < 1900, year is offset by 1900
637          */
638 #define YEAR_CAREFUL(y)         ((y) < 1900 ? (y) + 1900 : (y))
639
640 #define YEAR(y) YEAR_CAREFUL(y)
641
642
643 /* \f */
644
645 static long
646 gtime( struct tm *tm )
647 {
648     register int    i,
649                     sec,
650                     mins,
651                     hour,
652                     mday,
653                     mon,
654                     year;
655     register long   result;
656
657     if ((sec = tm -> tm_sec) < 0 || sec > 59
658             || (mins = tm -> tm_min) < 0 || mins > 59
659             || (hour = tm -> tm_hour) < 0 || hour > 24
660             || (mday = tm -> tm_mday) < 1 || mday > 31
661             || (mon = tm -> tm_mon + 1) < 1 || mon > 12)
662         return ((long) -1);
663     if (hour == 24) {
664         hour = 0;
665         mday++;
666     }
667     year = YEAR (tm -> tm_year);
668
669     result = 0L;
670     for (i = 1970; i < year; i++)
671         result += dysize (i);
672     if (dysize (year) == 366 && mon >= 3)
673         result++;
674     while (--mon)
675         result += dmsize[mon - 1];
676     result += mday - 1;
677     result = 24 * result + hour;
678     result = 60 * result + mins;
679     result = 60 * result + sec;
680
681     return result;
682 }