common functions for pdi (vcf/ics) parsing
authorPaul Hänsch <paul@plutz.net>
Mon, 22 Oct 2018 16:21:09 +0000 (18:21 +0200)
committerPaul Hänsch <paul@plutz.net>
Mon, 22 Oct 2018 16:21:09 +0000 (18:21 +0200)
pdiread.sh [new file with mode: 0755]

diff --git a/pdiread.sh b/pdiread.sh
new file mode 100755 (executable)
index 0000000..e01c71c
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/zsh
+
+# Copyright 2014 - 2018 Paul Hänsch
+#
+# This file is part of Confetti.
+# 
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti.  If not, see <http://www.gnu.org/licenses/>. 
+
+# This is a parsing library for the Personal Data Interchange format (PDI)
+# PDI is the format for encoding VCard (.vcf) and iCalendar (.ics) files
+
+[ -n "$include_pdi" ] && return 0
+include_pdi="$0"
+
+BR='
+'
+
+pdi_load() {
+  sed -r ':X;N;$!bX; s;\r\n[ \t];;g; s;\r\n;\n;g;' "$1" \
+  | sed -r '
+    # === turn property names to upper case, strip group names ===
+    h; s;^([^;:]+);;;
+    x; s;^([^;:\.]+\.)?([^;:]+).*$;\2;;
+    y;abcdefghijklmnopqrstuvwxyz;ABCDEFGHIJKLMNOPQRSTUVWXYZ;
+    G; s;\n;;;
+
+    # === strip trailing CR (but keep CRs in property value) ===
+    # s;\r$;;;  # already done in in previous filter
+
+    # unscramble aggregated fields
+    :disag
+    s;^([^:]+:)((.*[^\])?(\\\\)*),;\1\2\n\1;;
+    t disag;
+
+    # === Normalise various known vendor properties ===
+                s;^X-MS-CARDPICTURE(\;|:);PHOTO\1;;
+                        s;^X-GENDER(\;|:);GENDER\1;;
+                   s;^X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
+         s;^X-EVOLUTION-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
+    s;^X-KADDRESSBOOK-X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
+            s;^X-EVOLUTION-BLOG-URL(\;|:);URL\1;;
+                           s;^AGENT(\;|:);RELATED\;VALUE=text\;TYPE=agent\1;;
+                     s;^X-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
+           s;^X-EVOLUTION-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
+ s;^X-KADDRESSBOOK-X-ASSISTANTSNAME(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
+                       s;^X-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
+             s;^X-EVOLUTION-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
+   s;^X-KADDRESSBOOK-X-MANAGERSNAME(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
+                        s;^X-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
+              s;^X-EVOLUTION-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
+     s;^X-KADDRESSBOOK-X-SPOUSENAME(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
+
+    # === Normalise obsolete vendor IM properties ===
+            s;^X-AIM((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
+            s;^X-ICQ((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
+    s;^X-GOOGLE-TALK((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
+         s;^X-JABBER((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
+            s;^X-MSN((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:msn:;;
+          s;^X-YAHOO((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:ymsgr:;;
+            s;^X-SIP((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(sip:)?;IMPP\1:sip:;;
+
+    # === Update obsolete LABEL property ===
+    s;^LABEL((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$;ADR\1\;LABEL="\5":;;
+
+    # === Insert empty attribute fields where no attributes are present ===
+    s;^([^;:]+):;\1\;:;;
+    '
+}
+
+pdi_count(){
+  local card="$1" name="$2" rc='' cnt=0
+  while rc="${card#*${BR}${name};}"; do
+    [ "${rc}" != "${card}" ] || break
+    card="$rc"
+    cnt=$(($cnt + 1))
+  done
+  printf %i\\n $cnt
+}
+
+pdi_attrib(){
+  local card=":$1" name="$2" cnt="${3:-1}"
+  while [ $cnt -gt 0 ]; do
+    card="${card#*${BR}${name};}"
+    cnt=$((cnt - 1))
+  done
+  printf %s\\n "${card%%:*}"
+}
+
+pdi_value(){
+  local card="${BR}$1" name="$2" cnt="${3:-1}"
+  while [ $cnt -gt 0 ]; do
+    card="${card#*${BR}${name};*:}"
+    cnt=$((cnt - 1))
+  done
+  printf %s\\n "${card%%${BR}*}"
+}