bugfix: field splitting
[confetti] / pdiread.sh
1 #!/bin/zsh
2
3 # Copyright 2014 - 2018 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 # This is a parsing library for the Personal Data Interchange format (PDI)
21 # PDI is the format for encoding VCard (.vcf) and iCalendar (.ics) files
22
23 [ -n "$include_pdi" ] && return 0
24 include_pdi="$0"
25
26 BR='
27 '
28
29 pdi_load() {
30   sed -r ':X;N;$!bX; s;\r\n[ \t];;g; s;\r\n;\n;g;' "$1" \
31   | sed -r '
32     # === turn property names to upper case, strip group names ===
33     h; s;^([^;:]+);;;
34     x; s;^([^;:\.]+\.)?([^;:]+).*$;\2;;
35     y;abcdefghijklmnopqrstuvwxyz;ABCDEFGHIJKLMNOPQRSTUVWXYZ;
36     G; s;\n;;;
37
38     # === strip trailing CR (but keep CRs in property value) ===
39     # s;\r$;;;  # already done in in previous filter
40
41     # === Normalise various known vendor properties ===
42                 s;^X-MS-CARDPICTURE(\;|:);PHOTO\1;;
43                         s;^X-GENDER(\;|:);GENDER\1;;
44                    s;^X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
45          s;^X-EVOLUTION-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
46     s;^X-KADDRESSBOOK-X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
47             s;^X-EVOLUTION-BLOG-URL(\;|:);URL\1;;
48                            s;^AGENT(\;|:);RELATED\;VALUE=text\;TYPE=agent\1;;
49                      s;^X-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
50            s;^X-EVOLUTION-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
51  s;^X-KADDRESSBOOK-X-ASSISTANTSNAME(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
52                        s;^X-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
53              s;^X-EVOLUTION-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
54    s;^X-KADDRESSBOOK-X-MANAGERSNAME(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
55                         s;^X-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
56               s;^X-EVOLUTION-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
57      s;^X-KADDRESSBOOK-X-SPOUSENAME(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
58
59     # === Normalise obsolete vendor IM properties ===
60             s;^X-AIM((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
61             s;^X-ICQ((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
62     s;^X-GOOGLE-TALK((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
63          s;^X-JABBER((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
64             s;^X-MSN((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:msn:;;
65           s;^X-YAHOO((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:ymsgr:;;
66             s;^X-SIP((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(sip:)?;IMPP\1:sip:;;
67
68     # === Update obsolete LABEL property ===
69     s;^LABEL((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$;ADR\1\;LABEL="\5":;;
70
71     # === Insert empty attribute fields where no attributes are present ===
72     s;^([^;:]+):;\1\;:;;
73
74     # unscramble aggregated fields
75     :disag
76     s;^([^:]+:)((.*[^\])?(\\\\)*),;\1\2\n\1;;
77     t disag;
78     '
79 }
80
81 pdi_count(){
82   local card="$1" name="$2" rc='' cnt=0
83   while rc="${card#*${BR}${name};}"; do
84     [ "${rc}" != "${card}" ] || break
85     card="$rc"
86     cnt=$(($cnt + 1))
87   done
88   printf %i\\n $cnt
89 }
90
91 pdi_attrib(){
92   local card=":$1" name="$2" cnt="${3:-1}"
93   while [ $cnt -gt 0 ]; do
94     card="${card#*${BR}${name};}"
95     cnt=$((cnt - 1))
96   done
97   printf %s\\n "${card%%:*}"
98 }
99
100 pdi_value(){
101   local card="${BR}$1" name="$2" cnt="${3:-1}"
102   while [ $cnt -gt 0 ]; do
103     card="${card#*${BR}${name};*:}"
104     cnt=$((cnt - 1))
105   done
106   printf %s\\n "${card%%${BR}*}"
107 }