82a57f6d60519a6d278346baff90c82bbac92105
[shellsnippets/shellsnippets.git] / mksh / assoldap.ksh
1 # -*- mode: sh -*-
2 #-
3 # Copyright © 2013
4 #       mirabilos <t.glaser@tarent.de>
5 # Copyright © 2014, 2015
6 #       Dominik George <dominik.george@teckids.org>
7 # Copyright © 2014, 2015
8 #       mirabilos <thorsten.glaser@teckids.org>
9 #
10 # Provided that these terms and disclaimer and all copyright notices
11 # are retained or reproduced in an accompanying document, permission
12 # is granted to deal in this work without restriction, including un‐
13 # limited rights to use, publicly perform, distribute, sell, modify,
14 # merge, give away, or sublicence.
15 #
16 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
17 # the utmost extent permitted by applicable law, neither express nor
18 # implied; without malicious intent or gross negligence. In no event
19 # may a licensor, author or contributor be held liable for indirect,
20 # direct, other damage, loss, or other issues arising in any way out
21 # of dealing in the work, even if advised of the possibility of such
22 # damage or existence of a defect, except proven that it results out
23 # of said person’s immediate fault when using the work as intended.
24 #-
25 # Generic LDAP (LDIF) parser into associative arrays.
26
27 # include assockit, unless already done
28 mydir=$(realpath "$(dirname "$0")")
29 [[ -n $ASSO_VAL ]] || PATH="$mydir:$mydir/..:$PATH" . assockit.ksh
30
31 # not NUL-safe
32 set -A Tb64decode_tbl -- \
33     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
34     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
35     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 62 -1 -1 -1 63 \
36     52 53 54 55 56 57 58 59 60 61 -1 -1 -1 -1 -1 -1 \
37     -1  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 \
38     15 16 17 18 19 20 21 22 23 24 25 -1 -1 -1 -1 -1 \
39     -1 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
40     41 42 43 44 45 46 47 48 49 50 51 -1 -1 -1 -1 -1 \
41     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
42     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
43     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
44     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
45     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
46     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
47     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 \
48     -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
49 function Tb64decode {
50         [[ -o utf8-mode ]]; local u=$? s
51         set +U
52         read -raN-1 s <<<"$*"
53         local -i i=0 n=${#s[*]} v x
54         unset s[--n]
55         local -i1 o
56
57         while (( i < n )); do
58                 (( (x = Tb64decode_tbl[s[i++]]) == -1 )) && continue
59                 while (( (v = Tb64decode_tbl[s[i++]]) == -1 )); do
60                         if (( i > n )); then
61                                 (( u )) || set -U
62                                 return 0
63                         fi
64                 done
65                 (( o = ((x = (x << 6) | v) >> 4) & 255 ))
66                 REPLY+=${o#1#}
67                 while (( (v = Tb64decode_tbl[s[i++]]) == -1 )); do
68                         if (( i > n )); then
69                                 (( u )) || set -U
70                                 return 0
71                         fi
72                 done
73                 (( o = ((x = (x << 6) | v) >> 2) & 255 ))
74                 REPLY+=${o#1#}
75                 while (( (v = Tb64decode_tbl[s[i++]]) == -1 )); do
76                         if (( i > n )); then
77                                 (( u )) || set -U
78                                 return 0
79                         fi
80                 done
81                 (( o = ((x << 6) | v) & 255 ))
82                 REPLY+=${o#1#}
83         done
84         (( u )) || set -U
85 }
86
87 # Syntax: asso_setldap arrayname index ... -- ldapsearch-options
88 function asso_setldap_plain {
89         local opts x n=0 found=0
90
91         for x in "$@"; do
92                 opts[n++]=$x
93                 if [[ $x = -[-+] ]]; then
94                         opts[n++]=-x
95                         found=1
96                 fi
97         done
98         if (( !found )); then
99                 opts[n++]=--
100                 opts[n++]=-x
101         fi
102         asso_setldap_internal "${opts[@]}"
103 }
104 function asso_setldap_sasl {
105         local opts x n=0 found=0
106
107         for x in "$@"; do
108                 opts[n++]=$x
109                 if [[ $x = -[-+] ]]; then
110                         opts[n++]=-Q
111                         found=1
112                 fi
113         done
114         if (( !found )); then
115                 opts[n++]=--
116                 opts[n++]=-Q
117         fi
118         asso_setldap_internal "${opts[@]}"
119 }
120 function asso_setldap_internal {
121         # parse options
122         local arrpath ldapopts x i=0 T dn line value found=0
123         set -A arrpath
124         while (( $# )); do
125                 [[ $1 = -- || $1 = -+ ]] && break
126                 arrpath[i++]=$1
127                 shift
128         done
129         if [[ $1 != -- && $1 != -+ ]]; then
130                 print -u2 'assoldap.ksh: syntax: asso_setldap arraypath -- ldappath'
131                 return 255
132         fi
133         [[ $1 = -+ ]]; do_free=$?
134         shift
135         set -A ldapopts -- "$@"
136
137         # Add default host URI if none is given
138         for x in "${ldapopts[@]}"; do
139                 if [[ $x = -H ]]; then
140                         found=1
141                         break
142                 fi
143         done
144         if (( !found )); then
145                 set -A ldapopts+ -- -H ldapi://
146         fi
147
148         if (( do_free )); then
149                 # just in case, unset the target array and create it as associative
150                 asso__lookup 1 "${arrpath[@]}"
151                 asso__r_free
152                 asso__r_setf $ASSO_AASS
153         fi
154
155         # call ldapsearch with decent output format
156         if ! T=$(mktemp /tmp/assoldap.XXXXXXXXXX); then
157                 print -u2 'assoldap.ksh: could not create temporary file'
158                 return 255
159         fi
160         if ! ldapsearch -LLL "${ldapopts[@]}" >"$T"; then
161                 print -ru2 "assoldap.ksh: error from: ldapsearch -LLL ${ldapopts[*]}"
162                 rm -f "$T"
163                 return $i
164         fi
165         if [[ ! -s $T ]]; then
166                 # empty output
167                 rm -f "$T"
168                 return 0
169         fi
170
171         # parse LDIF
172         { IFS= read -r line && while :; do
173                 if [[ -z $line ]]; then
174                         dn=
175                         IFS= read -r line || break
176                         continue
177                 fi
178                 if [[ $line = ' '* ]]; then
179                         value+=${line# }
180                 else
181                         x=${line%%: *}
182                         value=${line: ${#x}+2}
183                 fi
184                 IFS= read -r line || break
185                 [[ $line = ' '* ]] && continue
186                 if [[ $x = *: ]]; then
187                         x=${x%:}
188                         [[ $x = jpegPhoto ]] || value=${|Tb64decode "$value";}
189                 fi
190                 [[ $x = dn ]] && dn=$value
191
192                 c=$(asso_getv "${arrpath[@]}" "$dn" "$x" count)
193                 asso_sets "$value" "${arrpath[@]}" "$dn" "$x" $((c))
194                 asso_seti $((++c)) "${arrpath[@]}" "$dn" "$x" count
195         done; } <"$T"
196         rm -f "$T"
197         return 0
198 }
199
200 :||\
201 {
202         # for testing
203         LDAPTLS_CACERT=/etc/ssl/certs/dc.lan.tarent.de.cer \
204             asso_setldap_plain users -- \
205             -H ldaps://dc.lan.tarent.de -b cn=users,dc=tarent,dc=de -s one \
206             isJabberAccount=1 cn uid
207         if (( $? )); then
208                 print -u2 An error occurred: $?
209                 exit 1
210         fi
211         print "uid (dn) = cn"
212         asso_loadk users
213         for user_dn in "${asso_y[@]}"; do
214                 print -r -- "$(asso_getv users "$user_dn" uid)" \
215                     "($user_dn) = $(asso_getv users "$user_dn" cn)"
216         done | sort
217 }