]> git.plutz.net Git - confetti/blob - pages/cards.sh
improved vcf parser (speed, security)
[confetti] / pages / cards.sh
1 #!/bin/zsh
2
3 # Copyright 2014 - 2016 Paul Hänsch
4 #
5 # This file is part of Confetti.
6
7 # Confetti is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # Confetti is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with Confetti.  If not, see <http://www.gnu.org/licenses/>. 
19
20 BR='
21 '
22
23 case $PROFILE in
24 medical)
25   view_card="$_EXEC/templates/view_client.sh"
26   edit_card="$_EXEC/templates/edit_client.sh"
27   _GET[order]="${_GET[order]:-lastname}"
28   _GET[filtertype]="${_GET[filtertype]:-name}"
29   profile_medical=x
30 ;;
31 circus)
32   view_card="$_EXEC/templates/view_attendee.sh"
33   edit_card="$_EXEC/templates/edit_attendee.sh"
34   _GET[order]="${_GET[order]:-firstname}"
35   _GET[filtertype]="${_GET[filtertype]:-any}"
36   profile_circus=x
37 ;;
38 esac
39
40 edit="${_GET[edit]}"
41 [ \! -f "vcard/$edit" -a \! -f "temp/$edit" ] && edit=''
42
43 listcourses() {
44   ls -1 ${_DATA}/ical/*ics |while read file; do
45     icstime="$(sed -rn 's:^DTSTART\:(TZID=.*\:)?([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z?\r$:\2-\3-\4 \5\:\6\:\7:p' "$file")"
46     echo "$(date -d "$icstime" "+%u %H%M%S")\t$file"
47   done |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
48 }
49
50 list_hi_companies(){
51   sed -rn 's;^X-HEALTH-INSURANCE:([^\;]+)\;.*$;\1;p' ${_DATA}/vcard/*vcf \
52   | sort -u
53 }
54
55 listcards() {
56   case "${_GET[filtertype]}" in
57     any)
58        grep -il "${_GET[filter]}" ${_DATA}/vcard/*vcf
59       ;;
60     name)
61        egrep -xil "(FN|NICKNAME|N)(;.+)*:.*${_GET[filter]}.*" ${_DATA}/vcard/*vcf
62       ;;
63     street)
64        egrep -xil "(ADR)(;.+)*:([^;]*;){2}${_GET[filter]}.*" ${_DATA}/vcard/*vcf
65       ;;
66     zip)
67        egrep -xil "(ADR)(;.+)*:([^;]*;){5}${_GET[filter]}.*" ${_DATA}/vcard/*vcf
68       ;;
69     telephone)
70        egrep -xil "(TEL)(;.+)*:.*${_GET[filter]}.*" ${_DATA}/vcard/*vcf
71       ;;
72     birth)
73        egrep -xil "(BDAY)(;.+)*:${_GET[filter]}.*" ${_DATA}/vcard/*vcf
74       ;;
75     course)
76       ;;
77     *) ls -1 ${_DATA}/vcard/*vcf 2>/dev/null
78       ;;
79   esac |case "${_GET[order]}" in
80     firstname)
81       while read file; do
82         fn=$(sed -rn 's:^N(;.+)*\:([^;]*;){1} *([^;]*).*$:\3:p' "$file")
83         echo "$fn\t$file"
84       done
85       ;;
86     lastname)
87       while read file; do
88         ln=$(sed -rn 's:^N(;.+)*\:([^;]*;){0} *([^;]*).*$:\3:p' "$file")
89         echo "$ln\t$file"
90       done
91       ;;
92     bdate)
93       while read file; do
94         bd=$(sed -rn 's:^BDAY(;.+)*\:(.*)$:\2:p' "$file")
95         echo "$bd\t$file"
96       done
97       ;;
98     *)
99       sed -r 's:^.*$:x\t&:'
100       ;;
101   esac |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
102 }
103
104 vcf_parse() {
105   sed -r ':X;N;$!bX; s;\r\n[ \t];;g; s;\r\n;\n;g;' "$1" \
106   | sed -rn '
107     # === turn property names to upper case ===
108     h; s;^([^\;:]+);;;
109     x; s;^([^\;:]+).*$;\1;;
110     y;abcdefghijklmnopqrstuvwxyz;ABCDEFGHIJKLMNOPQRSTUVWXYZ;
111     G; s;\n;;;
112
113     # === Normalise various known vendor properties ===
114                 s;^X-MS-CARDPICTURE(\;|:);PHOTO\1;;
115                         s;^X-GENDER(\;|:);GENDER\1;;
116                    s;^X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
117          s;^X-EVOLUTION-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
118     s;^X-KADDRESSBOOK-X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
119             s;^X-EVOLUTION-BLOG-URL(\;|:);URL\1;;
120                               s;^AGENT(\;|:);RELATED\;VALUE=text\;TYPE=agent\1;;
121                         s;^X-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
122               s;^X-EVOLUTION-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
123     s;^X-KADDRESSBOOK-X-ASSISTANTSNAME(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
124                           s;^X-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
125                 s;^X-EVOLUTION-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
126       s;^X-KADDRESSBOOK-X-MANAGERSNAME(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
127                            s;^X-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
128                  s;^X-EVOLUTION-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
129         s;^X-KADDRESSBOOK-X-SPOUSENAME(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
130
131     # === Normalise obsolete vendor IM properties ===
132             s;^X-AIM((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
133             s;^X-ICQ((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
134     s;^X-GOOGLE-TALK((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
135          s;^X-JABBER((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
136             s;^X-MSN((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):;IMPP\1:msn:;;
137           s;^X-YAHOO((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):;IMPP\1:ymsgr:;;
138             s;^X-SIP((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):(sip:)?;IMPP\1:sip:;;
139
140     # === Update obsolete LABEL property ===
141     s;^LABEL((\;[a-zA-Z0-9-]+|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):(.*)$;ADR\1\;LABEL="\5":;;
142
143     /^([a-zA-Z0-9-]+)((\;[a-zA-Z0-9-]+=?|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):(.*)$/{
144       h;
145       s;^([a-zA-Z0-9-]+)((\;[a-zA-Z0-9-]+=?|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):(.*)$;key='\''\1'\'';;
146       H; g;
147       s;^([a-zA-Z0-9-]+)((\;[a-zA-Z0-9-]+=?|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\6;;
148       s;'\'';'\'\\\\\'\'';g
149       s;^.*$;value='\''&'\'';
150       H; g;
151       s;^([a-zA-Z0-9-]+)((\;[a-zA-Z0-9-]+=?|\;[a-zA-Z0-9-]+=([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\2;;
152       s;'\'';'\'\\\\\'\'';g
153       s;\;([a-zA-Z0-9-]+)(=|=(([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*))?;\ntag[\1]='\''\3'\'';g
154       H; g;
155       s;^[^\n]*[\n ]+;;
156
157       s;\n[\n ]+;\n;g; s;\n$;;;
158
159       p;
160     }
161     '
162 }
163
164 view_card() {  #Parameter: Cardfile
165   id="$1"
166   cardfile="$_DATA/vcard/${id}"
167   cachefile="$_DATA/cache/${id}.cache"
168   unset key
169   if [ "$cachefile" -nt "$cardfile" ]; then
170     cat "$cachefile"
171   else
172     declare -A tags
173     declare -A values
174     vcf_parse "$cardfile" |while read -r line; do
175       declare -A tag
176       case "$line" in
177         value*) eval "$line";;
178         tag*)   eval "$line";;
179         key*)
180           if [ -z "$key" ]; then
181             eval "$line"
182           else
183             values[$key]="${value//\\r\\n/$BR}"
184             for t in ${(k)tag}; do
185               tags[${key}_$t]="$tag[$t]"
186             done
187             eval "$line"
188             if [ -n "$values[$key]" ]; then
189               n=0
190               while [ -n "$values[$key$n]" ]; do n=$(($n + 1)); done
191               key=$key$n
192             fi
193             unset value
194             unset tag
195           fi
196         ;;
197       esac
198     done
199     . $view_card |tee "$cachefile"
200   fi
201 }
202
203 edit_card() {  #Parameter: Cardfile
204   id="$1"
205   cardfile="$_DATA/vcard/$id"
206   tempfile="$_DATA/temp/$id"
207   [ -f "$tempfile" ] && cardfile="$tempfile"
208   unset key
209
210   declare -A tags
211   declare -A values
212   vcf_parse "$cardfile" |while read -r line; do
213     declare -A tag
214     case "$line" in
215       value*) eval "$line";;
216       tag*)   eval "$line";;
217       key*)
218         if [ -z "$key" ]; then
219           eval "$line"
220         else
221           [ -n "$value" ] && values[$key]="${value//\\r\\n/$BR}" || values[$key]='\r'
222           for t in ${(k)tag}; do
223             tags[${key}_$t]="$tag[$t]"
224           done
225           eval "$line"
226           if [ -n "$values[$key]" ]; then
227             n=0
228             while [ -n "$values[${key}${n}]" ]; do n=$(($n + 1)); done
229             key=$key$n
230           fi
231             unset value
232             unset tag
233         fi
234       ;;
235     esac
236   done
237   . $edit_card
238 }