305 grep uid=mahlon |
311 grep uid=mahlon |
306 grep uid=mahlon ou=People |
312 grep uid=mahlon ou=People |
307 grep -r (&(uid=mahlon)(objectClass=*)) |
313 grep -r (&(uid=mahlon)(objectClass=*)) |
308 |
314 |
309 aliased to: search |
315 aliased to: search |
|
316 |
|
317 =item B<inspect> |
|
318 |
|
319 View schema information about a given entry, or a list of arbitrary |
|
320 objectClasses, along with the most common flags for the objectClass |
|
321 attributes. |
|
322 |
|
323 inspect uid=mahlon |
|
324 inspect posixAccount organizationalUnit |
|
325 inspect _schema |
|
326 |
|
327 The output is a list of found objectClasses, their schema heirarchy |
|
328 (up to 'top'), whether or not they are a structural class, and then |
|
329 a merged list of all valid attributes for the given objectClasses. |
|
330 Attributes are marked as either required or optional, and whether |
|
331 they allow multiple values or not. |
|
332 |
|
333 If you ask for the special "_schema" object, the raw server schema |
|
334 is dumped to screen. |
310 |
335 |
311 =item B<list> |
336 =item B<list> |
312 |
337 |
313 List entries for the current basedn. Globbing is supported. |
338 List entries for the current basedn. Globbing is supported. |
314 |
339 |
580 sub ldif |
605 sub ldif |
581 { |
606 { |
582 my $self = shift; |
607 my $self = shift; |
583 my $use_temp = shift; |
608 my $use_temp = shift; |
584 |
609 |
585 # find the terminal width |
610 # create tmpfile and link ldif object with it |
586 # |
611 # |
|
612 if ( $use_temp ) { |
|
613 my ( undef, $fname ) = |
|
614 File::Temp::tempfile( 'shelldap_XXXXXXXX', DIR => '/tmp', UNLINK => 1 ); |
|
615 $self->{'ldif'} = Net::LDAP::LDIF->new( $fname, 'w', sort => 1, wrap => $self->wrapsize ); |
|
616 $self->{'ldif_fname'} = $fname; |
|
617 } |
|
618 |
|
619 # ldif -> stdout |
|
620 else { |
|
621 $self->{'ldif'} = Net::LDAP::LDIF->new( \*STDOUT, 'w', sort => 1, wrap => $self->wrapsize ); |
|
622 } |
|
623 |
|
624 return $self->{'ldif'}; |
|
625 } |
|
626 |
|
627 |
|
628 ### Return an Entry object from an LDIF filename, or undef if there was an error. |
|
629 ### |
|
630 sub load_ldif |
|
631 { |
|
632 my $self = shift; |
|
633 my $ldif = Net::LDAP::LDIF->new( shift(), 'r' ); |
|
634 |
|
635 return unless $ldif; |
|
636 |
|
637 my $e; |
|
638 eval { $e = $ldif->read_entry(); }; |
|
639 |
|
640 return if $@; |
|
641 return $e; |
|
642 } |
|
643 |
|
644 |
|
645 ### Given a filename, return an md5 checksum. |
|
646 ### |
|
647 sub chksum |
|
648 { |
|
649 my $self = shift; |
|
650 my $file = shift or return; |
|
651 |
|
652 my $md5 = Digest::MD5->new(); |
|
653 open F, $file or die "Unable to read file: $!\n"; |
|
654 my $hash = $md5->addfile( *F )->hexdigest(); |
|
655 close F; |
|
656 |
|
657 return $hash; |
|
658 } |
|
659 |
|
660 |
|
661 ### Find and return the current terminal width. |
|
662 ### |
|
663 sub wrapsize |
|
664 { |
|
665 my $self = shift; |
|
666 |
587 my $wrap = $conf->{'wrap'}; |
667 my $wrap = $conf->{'wrap'}; |
588 eval { |
668 eval { |
589 my $rows; |
669 my $rows; |
590 my $term = Term::ReadLine->new( 1 ); |
670 my $term = Term::ReadLine->new( 1 ); |
591 ( $rows, $wrap ) = $term->get_screen_size() unless $wrap; |
671 ( $rows, $wrap ) = $term->get_screen_size() unless $wrap; |
592 }; |
672 }; |
|
673 |
593 $wrap ||= 78; |
674 $wrap ||= 78; |
594 |
675 return $wrap; |
595 # create tmpfile and link ldif object with it |
|
596 # |
|
597 if ( $use_temp ) { |
|
598 my ( undef, $fname ) = |
|
599 File::Temp::tempfile( 'shelldap_XXXXXXXX', DIR => '/tmp', UNLINK => 1 ); |
|
600 $self->{'ldif'} = Net::LDAP::LDIF->new( $fname, 'w', sort => 1, wrap => $wrap ); |
|
601 $self->{'ldif_fname'} = $fname; |
|
602 } |
|
603 |
|
604 # ldif -> stdout |
|
605 else { |
|
606 $self->{'ldif'} = Net::LDAP::LDIF->new( \*STDOUT, 'w', sort => 1, wrap => $wrap ); |
|
607 } |
|
608 |
|
609 return $self->{'ldif'}; |
|
610 } |
|
611 |
|
612 |
|
613 ### Return an Entry object from an LDIF filename, or undef if there was an error. |
|
614 ### |
|
615 sub load_ldif |
|
616 { |
|
617 my $self = shift; |
|
618 my $ldif = Net::LDAP::LDIF->new( shift(), 'r' ); |
|
619 |
|
620 return unless $ldif; |
|
621 |
|
622 my $e; |
|
623 eval { $e = $ldif->read_entry(); }; |
|
624 |
|
625 return if $@; |
|
626 return $e; |
|
627 } |
|
628 |
|
629 |
|
630 ### Given a filename, return an md5 checksum. |
|
631 ### |
|
632 sub chksum |
|
633 { |
|
634 my $self = shift; |
|
635 my $file = shift or return; |
|
636 |
|
637 my $md5 = Digest::MD5->new(); |
|
638 open F, $file or die "Unable to read file: $!\n"; |
|
639 my $hash = $md5->addfile( *F )->hexdigest(); |
|
640 close F; |
|
641 |
|
642 return $hash; |
|
643 } |
676 } |
644 |
677 |
645 |
678 |
646 ### Used by Term::Shell to generate the prompt. |
679 ### Used by Term::Shell to generate the prompt. |
647 ### |
680 ### |
1035 foreach ( %cmd_map ) { |
1076 foreach ( %cmd_map ) { |
1036 next unless $_; |
1077 next unless $_; |
1037 my $sub = "comp_$_"; |
1078 my $sub = "comp_$_"; |
1038 *$sub = \&autocomplete_cwd; |
1079 *$sub = \&autocomplete_cwd; |
1039 } |
1080 } |
1040 *comp_touch = \&comp_create; |
1081 *comp_touch = \&comp_create; |
1041 *comp_export = \&comp_setenv; |
1082 *comp_export = \&comp_setenv; |
1042 |
1083 |
1043 # setup alias subs |
1084 # setup alias subs |
1044 # |
1085 # |
1045 # Term::Shell has an alias_* feature, but |
1086 # Term::Shell has an alias_* feature, but |
1046 # it seems to work about 90% of the time. |
1087 # it seems to work about 90% of the time. |
1983 print "\n"; |
2024 print "\n"; |
1984 return; |
2025 return; |
1985 } |
2026 } |
1986 |
2027 |
1987 |
2028 |
|
2029 ### Show basic information for an entry (DN) or list of objectClasses. |
|
2030 ### |
|
2031 ### structural/auxillary classes |
|
2032 ### required attributes |
|
2033 ### optional attributes |
|
2034 ### |
|
2035 sub run_inspect |
|
2036 { |
|
2037 my $self = shift; |
|
2038 my @ocs = @_; |
|
2039 my $dn = $ocs[0]; |
|
2040 my ( $must_attr, $may_attr ); |
|
2041 |
|
2042 unless ( $dn ) { |
|
2043 print "No DN or objectClass(es) provided.\n"; |
|
2044 return; |
|
2045 } |
|
2046 |
|
2047 # "Magic" argument that dumps all raw schema information. |
|
2048 # |
|
2049 if ( $dn eq '_schema' ) { |
|
2050 $self->{'schema'}->dump(); |
|
2051 return; |
|
2052 } |
|
2053 |
|
2054 # one argument -- if it successfully resolves to a valid DN, fetch |
|
2055 # the objectClass list from it. |
|
2056 # |
|
2057 if ( scalar @ocs == 1 ) { |
|
2058 $dn = $self->base() if $dn eq '.'; |
|
2059 $dn = $self->path_to_dn( $dn ); |
|
2060 |
|
2061 my $s = $self->search({ base => $dn, vals => 1, attrs => ['objectClass'] }); |
|
2062 if ( $s->{'code'} == LDAP_SUCCESS ) { |
|
2063 my $e = ${ $s->{'entries'} }[0]; |
|
2064 @ocs = $e->get_value('objectClass'); |
|
2065 } |
|
2066 } |
|
2067 |
|
2068 # get the complete attributes list. |
|
2069 # |
|
2070 ( $must_attr, $may_attr ) = $self->fetch_attributes( \@ocs ); |
|
2071 my %must = map { $_ => 1 } @{$must_attr}; |
|
2072 |
|
2073 # Output objectClass chains and flags. |
|
2074 # |
|
2075 print "ObjectClasses:\n"; |
|
2076 foreach my $oc ( sort @ocs ) { |
|
2077 my @sups = $self->findall_supers( $oc ); |
|
2078 |
|
2079 my @oc_chain = ( $oc, @sups ); |
|
2080 my @oc_out; |
|
2081 |
|
2082 foreach my $oc ( @oc_chain ) { |
|
2083 my $oc_obj = $self->{'schema'}->objectclass( $oc ); |
|
2084 next unless $oc_obj; |
|
2085 |
|
2086 $oc = $oc . ' (' . 'structural' . ')' if $oc_obj->{'structural'}; |
|
2087 push( @oc_out, $oc ); |
|
2088 } |
|
2089 |
|
2090 print " " . join( ' --> ', @oc_out ) . "\n" if scalar @oc_out; |
|
2091 } |
|
2092 |
|
2093 # Output attributes and flags. |
|
2094 # |
|
2095 print "\nAttributes:\n"; |
|
2096 foreach my $attr ( sort (@{$must_attr}, @{$may_attr}) ) { |
|
2097 my @flaglist; |
|
2098 if ( $self->{'schema'}->attribute( $attr )->{'single-value'} ) { |
|
2099 push ( @flaglist, 'single-value' ); |
|
2100 } |
|
2101 else { |
|
2102 push ( @flaglist, 'multivalue' ); |
|
2103 } |
|
2104 |
|
2105 push ( @flaglist, $must{$attr} ? 'required' : 'optional' ); |
|
2106 |
|
2107 my $flags = ''; |
|
2108 $flags = (' (' . join( ', ', sort @flaglist ) . ')') if scalar @flaglist > 0; |
|
2109 |
|
2110 printf( " %s%s\n", $attr, $flags ); |
|
2111 } |
|
2112 |
|
2113 print "\n"; |
|
2114 return; |
|
2115 } |
|
2116 |
|
2117 |
|
2118 ### Recursively walk an objectClass heirarchy, returning an array |
|
2119 ### of inheritence. |
|
2120 ### |
|
2121 sub findall_supers |
|
2122 { |
|
2123 my $self = shift; |
|
2124 my $oc = shift or return; |
|
2125 my @found; |
|
2126 |
|
2127 foreach my $sup ( $self->{'schema'}->superclass($oc) ) { |
|
2128 push( @found, $sup ); |
|
2129 push( @found, $self->findall_supers( $sup ) ); |
|
2130 } |
|
2131 |
|
2132 return @found; |
|
2133 } |
|
2134 |
|
2135 |
|
2136 |
1988 ######################################################################## |
2137 ######################################################################## |
1989 ### M A I N |
2138 ### M A I N |
1990 ######################################################################## |
2139 ######################################################################## |
1991 |
2140 |
1992 package main; |
2141 package main; |
1993 use strict; |
2142 use strict; |
1994 use warnings; |
2143 use warnings; |
1995 |
2144 |
1996 $0 = 'shelldap'; |
2145 $0 = 'shelldap'; |
1997 my $VERSION = '0.9.0'; |
2146 my $VERSION = '1.0.0'; |
1998 |
2147 |
1999 use Getopt::Long; |
2148 use Getopt::Long; |
2000 use YAML::Syck; |
2149 use YAML::Syck; |
2001 use Pod::Usage; |
2150 use Pod::Usage; |
2002 eval 'use Term::ReadLine::Gnu'; |
2151 eval 'use Term::ReadLine::Gnu'; |