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