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