Make assoldap compatible with multi-value fields.
[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 and pure-mksh base64 decoder, unless already done
28 mydir=$(realpath "$(dirname "$0")")
29 [[ -n $ASSO_VAL ]] || PATH="$mydir:$mydir/..:$PATH" . assockit.ksh
30 typeset -f Lb64decode >/dev/null || PATH="$mydir:$mydir/..:$PATH" . base64
31
32 # Syntax: asso_setldap arrayname index ... -- ldapsearch-options
33 function asso_setldap {
34         # parse options
35         local arrpath ldapopts x i=0 T dn line value
36         set -A arrpath
37         while (( $# )); do
38                 [[ $1 = -- ]] && break
39                 arrpath[i++]=$1
40                 shift
41         done
42         if [[ $1 != -- ]]; then
43                 print -u2 'assoldap.ksh: syntax: asso_setldap arraypath -- ldappath'
44                 return 255
45         fi
46         shift
47         set -A ldapopts -- "$@"
48
49         # just in case, unset the target array and create it as associative
50         asso__lookup 1 "${arrpath[@]}"
51         asso__r_free
52         asso__r_setf $ASSO_AASS
53
54         # call ldapsearch with decent output format
55         if ! T=$(mktemp -d /tmp/assoldap.XXXXXXXXXX); then
56                 print -u2 'assoldap.ksh: could not create temporary directory'
57                 return 255
58         fi
59         (ldapsearch -xLLL "${ldapopts[@]}"; echo $? >"$T/err") | \
60             tr '\n' $'\a' | sed -e $'s/\a //g' >"$T/out"
61         i=$(<"$T/err")
62         if (( i )); then
63                 print -u2 'assoldap.ksh: ldapsearch returned error'
64                 rm -rf "$T"
65                 return $i
66         fi
67         if [[ ! -s $T/out ]]; then
68                 # empty output
69                 rm -rf "$T"
70                 return 0
71         fi
72
73         # parse LDIF (without linewraps)
74         while IFS= read -d $'\a' -r line; do
75                 if [[ -z $line ]]; then
76                         dn=
77                         continue
78                 fi
79                 value=${line##+([!:]):?(:)*( )}
80                 if [[ $value = "$line" ]]; then
81                         print -ru2 "assoldap.ksh: malformed line: $line"
82                         rm -rf "$T"
83                         return 255
84                 fi
85                 x=${line%%*( )"$value"}
86                 if [[ $x = "$line" ]]; then
87                         print -ru2 "assoldap.ksh: malformed line: $line"
88                         rm -rf "$T"
89                         return 255
90                 fi
91                 [[ $x = *:: && $x != jpegPhoto:: ]] && \
92                     value=$(Lb64decode "$value")
93                 x=${x%%+(:)}
94                 if [[ -z $dn ]]; then
95                         if [[ $x = dn ]]; then
96                                 dn=$value
97                         else
98                                 print -ru2 "assoldap.ksh: not dn: $line"
99                                 rm -rf "$T"
100                                 return 255
101                         fi
102                 elif [[ $x = dn ]]; then
103                         print -ru2 "assoldap.ksh: unexpected dn ($dn): $line"
104                         rm -rf "$T"
105                         return 255
106                 fi
107
108                 c=$(asso_getv "${arrpath[@]}" "$dn" "$x" count)
109                 asso_sets "$value" "${arrpath[@]}" "$dn" "$x" $((c))
110                 asso_seti $((++c)) "${arrpath[@]}" "$dn" "$x" count
111         done <"$T/out"
112         rm -rf "$T"
113         if [[ -n $dn ]]; then
114                 print -u2 'assoldap.ksh: missing empty line at EOT'
115                 return 255
116         fi
117         return 0
118 }
119
120 :||\
121 {
122         # for testing
123         LDAPTLS_CACERT=/etc/ssl/certs/dc.lan.tarent.de.cer \
124             asso_setldap users -- \
125             -H ldaps://dc.lan.tarent.de -b cn=users,dc=tarent,dc=de -s one \
126             isJabberAccount=1 cn uid
127         if (( $? )); then
128                 print -u2 An error occurred: $?
129                 exit 1
130         fi
131         print "uid (dn) = cn"
132         asso_loadk users
133         for user_dn in "${asso_y[@]}"; do
134                 print -r -- "$(asso_getv users "$user_dn" uid)" \
135                     "($user_dn) = $(asso_getv users "$user_dn" cn)"
136         done | sort
137 }