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