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