Description: Upstream changes introduced in version 2.36-1
 This patch has been created by dpkg-source during the package build.
 Here's the last changelog entry, hopefully it gives details on why
 those changes were made:
 .
 bbdb (2.36-1) unstable; urgency=low
 .
   * New "upstream" release.
   * Look for upstream in github repo (debian/watch)
 .
 The person named in the Author field signed this changelog entry.
Author: Barak A. Pearlmutter <bap@debian.org>

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: http://bugs.debian.org/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: <YYYY-MM-DD>

--- /dev/null
+++ bbdb-2.36/extern/bbdb-vcard/bbdb-vcard.el
@@ -0,0 +1,1104 @@
+;;; bbdb-vcard.el --- vCard import/export for BBDB
+
+;; Copyright (c) 2010 Bert Burgemeister
+
+;; Author: Bert Burgemeister <trebbu@googlemail.com>
+;; Keywords: data calendar mail news
+;; URL: http://github.com/trebb/bbdb-vcard
+;; Version: 0.2
+
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2, or (at
+;; your option) any later version.
+;;
+;; This program 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
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; The exporter functionality is based on code from
+;; bbdb-vcard-export.el by Jim Hourihan and Alex Schroeder.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+;;; Commentary:
+;; 
+;; Purpose
+;; =======
+;; 
+;; Import and export of vCards as defined in RFC 2425 and RFC 2426
+;; to/from The Insidious Big Brother Database (BBDB).
+;; 
+;;
+;; Usage
+;; =====
+;;
+;; vCard Import
+;; ------------
+;;
+;; On a file, a buffer or a region containing one or more vCards, use
+;; `bbdb-vcard-import-file', `bbdb-vcard-import-buffer', or
+;; `bbdb-vcard-import-region' respectively to import them into BBDB.
+;;
+;; Preferred input format is vCard version 3.0.  Version 2.1 vCards
+;; are converted to version 3.0 on import.
+;;
+;;
+;; vCard Export
+;; ------------
+;;
+;; In buffer *BBDB*, press v to export the record under point.  Press
+;; * v to export all records in buffer into one vCard file.  Press *
+;; C-u v to export them into one file each.
+;;
+;; To put one or all vCard(s) into the kill ring, press V or * V
+;; respectively.
+;;
+;; Exported vCards are always version 3.0.  They can be re-imported
+;; without data loss with one exception: North American phone numbers
+;; lose their structure and are stored as flat strings.
+;;
+;;
+;; There are a few customization variables grouped under `bbdb-vcard'.
+;;
+;;
+;; Installation
+;; ============
+;;
+;; Put this file and file vcard.el into your `load-path' and add the
+;; following line to your Emacs initialization file:
+;;
+;;   (require 'bbdb-vcard)
+;;
+;;
+;; Implementation
+;; ==============
+;;
+;; vCard Import
+;; ------------
+;;
+;; For conversion of v2.1 vCards into v3.0 on import, Noah Friedman's
+;; vcard.el is needed.
+;;
+;; An existing BBDB record is extended by new information from a vCard
+;; 
+;;   (a) if name and company and an email address match
+;;   (b) or if name and company match
+;;   (c) or if name and an email address match
+;;   (d) or if name and birthday match
+;;   (e) or if name and a phone number match.
+;;
+;; Otherwise, a fresh BBDB record is created.
+;;
+;; When `bbdb-vcard-try-merge' is set to nil, there is always a fresh
+;; record created.
+;;
+;; In cases (c), (d), and (e), if the vCard has ORG defined, this ORG
+;; would overwrite an existing Company in BBDB.
+;;
+;; Phone numbers are always imported as strings.
+;;
+;; For vCard types that have more or less direct counterparts in BBDB,
+;; labels and parameters are translated and structured values
+;; (lastname; firstname; additional names; prefixes etc.) are
+;; converted appropriately with the risk of some (hopefully
+;; unessential) information loss.  For labels of the vCard types ADR
+;; and TEL, parameter translation is defined in
+;; `bbdb-vcard-import-translation-table'.
+;; 
+;; If there is a REV element, it is stored in BBDB's creation-date in
+;; newly created BBDB records, or discarded for existing ones.
+;;
+;; VCard type prefixes (A.ADR:..., B.ADR:... etc.) are stripped off
+;; and discarded from the following types: N, FN, NICKNAME, ORG (first
+;; occurrence), ADR, TEL, EMAIL, URL, BDAY (first occurrence), NOTE.
+;;
+;; VCard types that are prefixed `X-BBDB-' are stored in BBDB without
+;; the prefix.
+;;
+;; VCard type X-BBDB-ANNIVERSARY may contain (previously exported)
+;; newline-separated non-birthday anniversaries that are meant to be
+;; read by org-mode.
+;;
+;; All remaining vCard types that don't match the regexp in
+;; `bbdb-vcard-skip-on-import' and that have a non-empty value are
+;; stored unaltered in the BBDB Notes alist where, for instance,
+;; `TZ;VALUE=text:-05:00' is stored as `(tz\;value=text . "-05:00")'.
+;; From the BBDB data fields AKA, Phones, Addresses, Net Addresses,
+;; and Notes, duplicates are removed, respectively.
+;;
+;; VCards found inside other vCards (as values of type AGENT) are
+;; imported as well.
+;;
+;;
+;; Handling of the individual types defined in RFC2426 during import
+;; (assuming default label translation and no vCard type exclusion):
+;; "
+;; |----------------------+----------------------------------------|
+;; | VCARD TYPE;          | STORAGE IN BBDB                        |
+;; | PARAMETERS           |                                        |
+;; |----------------------+----------------------------------------|
+;; | VERSION              | -                                      |
+;; |----------------------+----------------------------------------|
+;; | N                    | First occurrence:                      |
+;; |                      | Firstname                              |
+;; |                      | Lastname                               |
+;; |                      |                                        |
+;; |                      | Rest:                                  |
+;; |                      | AKAs (append)                          |
+;; |----------------------+----------------------------------------|
+;; | FN                   | AKAs (append)                          |
+;; | NICKNAME             | AKAs (append)                          |
+;; |----------------------+----------------------------------------|
+;; | ORG                  | First occurrence:                      |
+;; |                      | Company                                |
+;; |                      |                                        |
+;; |                      | Rest:                                  |
+;; |                      | Notes<org                              |
+;; |                      | (repeatedly)                           |
+;; |----------------------+----------------------------------------|
+;; | ADR;TYPE=x,HOME,y    | Addresses<Home                         |
+;; | ADR;TYPE=x;TYPE=HOME | Addresses<Home                         |
+;; | ADR;TYPE=x,WORK,y    | Addresses<Office                       |
+;; | ADR;TYPE=x;TYPE=WORK | Addresses<Office                       |
+;; | ADR;TYPE=x,y,z       | Addresses<x,y,z                        |
+;; | ADR;TYPE=x;TYPE=y    | Addresses<x,y                          |
+;; | ADR                  | Addresses<Office                       |
+;; |----------------------+----------------------------------------|
+;; | TEL;TYPE=x,HOME,y    | Phones<Home (append)                   |
+;; | TEL;TYPE=x;TYPE=HOME | Phones<Home (append)                   |
+;; | TEL;TYPE=x,WORK,y    | Phones<Office (append)                 |
+;; | TEL;TYPE=x;TYPE=WORK | Phones<Office (append)                 |
+;; | TEL;TYPE=x,CELL,y    | Phones<Mobile (append)                 |
+;; | TEL;TYPE=x;TYPE=CELL | Phones<Mobile (append)                 |
+;; | TEL;TYPE=x,y,z       | Phones<x,y,z (append)                  |
+;; | TEL;TYPE=x;TYPE=y    | Phones<x,y (append)                    |
+;; | TEL                  | Phones<Office (append)                 |
+;; |----------------------+----------------------------------------|
+;; | EMAIL;TYPE=x,y,z     | Net-Addresses (append)                 |
+;; | URL                  | Notes<www                              |
+;; |----------------------+----------------------------------------|
+;; | BDAY                 | Notes<anniversary (append as birthday) |
+;; | X-BBDB-ANNIVERSARY   | Notes<anniversary (append)             |
+;; |----------------------+----------------------------------------|
+;; | NOTE                 | Notes<notes (append)                   |
+;; | REV                  | Notes<creation-date                    |
+;; | CATEGORIES           | Notes<mail-alias (append)              |
+;; | SORT-STRING          | Notes<sort-string                      |
+;; | KEY                  | Notes<key                              |
+;; | GEO                  | Notes<geo                              |
+;; | TZ                   | Notes<tz                               |
+;; | PHOTO                | Notes<photo                            |
+;; | LABEL                | Notes<label                            |
+;; | LOGO                 | Notes<logo                             |
+;; | SOUND                | Notes<sound                            |
+;; | TITLE                | Notes<title                            |
+;; | ROLE                 | Notes<role                             |
+;; | AGENT                | Notes<agent                            |
+;; | MAILER               | Notes<mailer                           |
+;; | UID                  | Notes<uid                              |
+;; | PRODID               | Notes<prodid                           |
+;; | CLASS                | Notes<class                            |
+;; | X-foo                | Notes<x-foo                            |
+;; | X-BBDB-bar           | Notes<bar                              |
+;; |----------------------+----------------------------------------|
+;; | anyJunK;a=x;b=y      | Notes<anyjunk;a=x;b=y                  |
+;; |----------------------+----------------------------------------|
+;; "
+;;
+;; vCard Export
+;; ------------
+;;
+;; VCard types N (only fields lastname, firstname) and FN both come
+;; from BBDB's Name.
+;;
+;; Members of BBDB field AKA are stored comma-separated under the
+;; vCard type NICKNAME.
+;;
+;; Labels of Addresses and Phones are translated as defined in
+;; `bbdb-vcard-export-translation-table' into type parameters of
+;; vCard types ADR and TEL, respectively.
+;;
+;; In vCard type ADR, fields postbox and extended address are always
+;; empty.  Newlines which subdivide BBDB Address fields are converted
+;; into commas subdividing vCard ADR fields.
+;;
+;; The value of 'anniversary in Notes is supposed to be subdivided by
+;; newlines.  The birthday part (either just a date or a date followed
+;; by \"birthday\") is stored under vCard type BDAY. The rest is
+;; stored newline-separated in the non-standard vCard type
+;; X-BBDB-ANNIVERSARY.
+;;
+;; Field names listed in `bbdb-vcard-x-bbdb-candidates' are in the
+;; exported vCard prepended by `X-BBDB-'.
+;;
+;; The creation-date of the BBDB record is stored as vCard type REV.
+;;
+;; Remaining members of BBDB Notes are exported to the vCard without
+;; change.
+;;
+
+
+;;; History:
+;; 
+
+
+;;; Code:
+
+(require 'cl)
+(require 'bbdb)
+(require 'vcard)
+
+(defconst bbdb-vcard-version "0.2"
+  "Version of the vCard importer/exporter.
+The major part increases on user-visible changes.")
+
+
+
+;;;; User Variables
+
+(defgroup bbdb-vcard nil
+  "Customizations for vCards"
+  :group 'bbdb)
+
+(defcustom bbdb-vcard-skip-on-import "X-GSM-"
+  "Regexp describing vCard elements that are to be discarded during import.
+Example: `X-GSM-\\|X-MS-'."
+  :group 'bbdb-vcard
+  :type 'regexp)
+
+(defcustom bbdb-vcard-skip-valueless t
+  "Skip vCard element types with an empty value.
+Nil means insert empty types into BBDB."
+  :group 'bbdb-vcard
+  :type 'boolean)
+
+(defcustom bbdb-vcard-import-translation-table
+  '(("CELL\\|CAR" . "Mobile")
+    ("WORK" . "Office")
+    ("HOME" . "Home")  ; translates e.g. "dom,home,postal,parcel" to "Home"
+    ("^$" . "Office")) ; acts as a default for parameterless ADR or TEL
+  "Label translation on vCard import.
+Alist with translations of location labels for addresses and phone
+numbers.  Cells are (VCARD-LABEL-REGEXP . BBDB-LABEL).  One entry
+should map a default BBDB label to the empty string (`\"^$\"') which
+corresponds to unlabelled vCard elements."
+  :group 'bbdb-vcard
+  :type '(alist :key-type
+                (choice regexp (const :tag "Empty (as default)" "^$"))
+                :value-type string))
+
+(defcustom bbdb-vcard-try-merge t
+  "Try to merge vCards into existing BBDB records.
+Nil means create a fresh bbdb record each time a vCard is read."
+  :group 'bbdb-vcard
+  :type 'boolean)
+
+(defcustom bbdb-vcard-type-canonicalizer 'upcase
+  "Function to apply to vCard type names on export.
+Most reasonable choices are `upcase' and `downcase'."
+  :group 'bbdb-vcard
+  :type 'function)
+
+(defcustom bbdb-vcard-x-bbdb-candidates
+  '(attribution
+    finger-host
+    gnus-score
+    mark-char
+    mail-name
+    face
+    tex-name
+    aka)                                ; not sure what this is for
+  "List of translatable BBDB user field names.
+On export to a vCard, they are transformed into vCard-compliant
+extended types by prepending `X-BBDB-'.  On (re-)import, this prefix
+is removed again."
+  :group 'bbdb-vcard
+  :type '(repeat symbol))
+
+(defcustom bbdb-vcard-export-translation-table
+  '(("Mobile" . "CELL")
+    ("Office" . "WORK"))
+  "Label translation on vCard export.
+Alist with translations of location labels for addresses and phone
+numbers.  Cells are (BBDB-LABEL-REGEXP . VCARD-LABEL)."
+  :group 'bbdb-vcard
+  :type '(alist :key-type
+                (choice regexp (const :tag "Empty (as default)" "^$"))
+                :value-type string))
+
+(defcustom bbdb-vcard-export-coding-system
+  'utf-8-dos        ; dos line endings mandatory according to RFC 2426
+  "Coding system to use when writing vCard files."
+  :group 'bbdb-vcard
+  :type 'symbol)
+
+(defcustom bbdb-vcard-default-dir "~/exported-vcards/"
+  "Default storage directory for exported vCards.
+Nil means current directory."
+  :group 'bbdb-vcard
+  :type '(choice directory (const :tag "Current directory" nil)))
+
+
+
+;;;; User Functions
+
+;;;###autoload
+(defun bbdb-vcard-import-region (begin end)
+  "Import the vCards between BEGIN and END into BBDB.
+Existing BBDB records may be altered."
+  (interactive "r")
+  (bbdb-vcard-iterate-vcards 'bbdb-vcard-import-vcard
+                             (buffer-substring-no-properties begin end)))
+
+;;;###autoload
+(defun bbdb-vcard-import-buffer (vcard-buffer)
+  "Import vCards from VCARD-BUFFER into BBDB.
+Existing BBDB records may be altered."
+  (interactive (list (current-buffer)))
+  (set-buffer vcard-buffer)
+  (bbdb-vcard-import-region (point-min) (point-max)))
+
+;;;###autoload
+(defun bbdb-vcard-import-file (vcard-file)
+  "Import vCards from VCARD-FILE into BBDB.
+If VCARD-FILE is a wildcard, import each matching file.  Existing BBDB
+records may be altered."
+  (interactive "FvCard file (or wildcard): ")
+  (dolist (vcard-file (file-expand-wildcards vcard-file))
+    (with-temp-buffer
+      (insert-file-contents vcard-file)
+      (bbdb-vcard-import-region (point-min) (point-max)))))
+
+;;;###autoload
+(defun bbdb-vcard-export
+  (filename-or-directory all-records-p one-file-per-record-p)
+  "From Buffer *BBDB*, write one or more record(s) as vCard(s) to file(s).
+\\<bbdb-mode-map>\
+If \"\\[bbdb-apply-next-command-to-all-records]\\[bbdb-vcard-export]\"\
+is used instead of simply \"\\[bbdb-vcard-export]\", then export all \
+records currently
+in the *BBDB* buffer.  If used with prefix argument, store records
+in individual files."
+  (interactive
+   (let ((default-filename              ; argument filename-or-directory
+           (bbdb-vcard-make-file-name (bbdb-current-record nil)))
+         (all-records-p (bbdb-do-all-records-p)))
+     (list
+      (if all-records-p
+          (if current-prefix-arg
+              (read-directory-name "Write vCard files to directory: "
+                                   bbdb-vcard-default-dir nil 42)
+            (read-file-name
+             "Write vCards to file: "
+             bbdb-vcard-default-dir
+             nil nil
+             (format-time-string "%Y-%m-%dT%H:%M.vcf" (current-time))))
+        (read-file-name "Write current record to vCard file: "
+                        bbdb-vcard-default-dir nil nil default-filename))
+      all-records-p           ; argument all-records-p
+      current-prefix-arg)))   ; argument one-file-per-record-p
+  (if all-records-p
+      (let ((records (progn (set-buffer bbdb-buffer-name)
+                            (mapcar 'car bbdb-records)))
+            used-up-basenames)          ; keep them unique
+        (if one-file-per-record-p
+            (progn
+              (dolist (record records)
+                (with-temp-buffer
+                  (let ((basename
+                         (bbdb-vcard-make-file-name record
+                                                    used-up-basenames)))
+                    (insert (bbdb-vcard-from record))
+                    (bbdb-vcard-write-buffer
+                     (concat filename-or-directory basename))
+                    (push basename used-up-basenames))))
+              (message "Wrote %d vCards to %s"
+                       (length used-up-basenames) filename-or-directory))
+          (with-temp-buffer     ; all visible BBDB records in one file
+            (dolist (record records)
+              (insert (bbdb-vcard-from record)))
+            (bbdb-vcard-write-buffer filename-or-directory))))
+    (let ((vcard (bbdb-vcard-from (bbdb-current-record nil)))) ; current record
+      (with-temp-buffer
+        (insert vcard)
+        (bbdb-vcard-write-buffer filename-or-directory)))))
+
+;;;###autoload
+(defun bbdb-vcard-export-to-kill-ring (all-records-p)
+  "From Buffer *BBDB*, copy one or more record(s) as vCard(s) to the kill ring.
+\\<bbdb-mode-map>\
+If \"\\[bbdb-apply-next-command-to-all-records]\
+\\[bbdb-vcard-export-to-kill-ring]\"\
+is used instead of simply \"\\[bbdb-vcard-export-to-kill-ring]\", \
+then export all records currently in
+the *BBDB* buffer."
+  (interactive (let ((all-records-p (bbdb-do-all-records-p)))
+                 (list all-records-p)))
+  (if all-records-p
+      (let ((records (progn (set-buffer bbdb-buffer-name)
+                            (mapcar 'car bbdb-records))))
+        (kill-new "")
+        (dolist (record records)
+          (kill-append (bbdb-vcard-from record) nil))
+        (message "Saved %d records as vCards" (length records)))
+    (kill-new (bbdb-vcard-from (bbdb-current-record nil)))
+    (message "Saved record as vCard")))
+
+;;;###autoload (define-key bbdb-mode-map [(v)] 'bbdb-vcard-export)
+(define-key bbdb-mode-map [(v)] 'bbdb-vcard-export)
+;;;###autoload (define-key bbdb-mode-map [(V)] 'bbdb-vcard-export-to-kill-ring)
+(define-key bbdb-mode-map [(V)] 'bbdb-vcard-export-to-kill-ring)
+
+
+
+(defun bbdb-vcard-iterate-vcards (vcard-processor vcards)
+  "Apply VCARD-PROCESSOR successively to each vCard in string VCARDS.
+When VCARDS is nil, return nil.  Otherwise, return t."
+  (with-temp-buffer
+    (insert vcards)
+    (goto-char (point-min))
+    ;; Change CR into CRLF if necessary, dealing with inconsistent line
+    ;; endings.
+    (while (re-search-forward "[^\r]\\(\n\\)" nil t)
+      (replace-match "\r\n" nil nil nil 1))
+    (setf (buffer-string) (bbdb-vcard-unfold-lines (buffer-string)))
+    (goto-char (point-min))
+    (while (re-search-forward
+            "^\\([[:alnum:]-]*\\.\\)?*BEGIN:VCARD[\r\n[:print:][:cntrl:]]*?\\(^\\([[:alnum:]-]*\\.\\)?END:VCARD\\)"
+            nil t)
+      (let ((vcard (match-string 0)))
+        (if (string= "3.0" (bbdb-vcard-version-of vcard))
+            (funcall vcard-processor vcard)
+          (funcall vcard-processor      ; probably a v2.1 vcard
+                   (bbdb-vcard-unfold-lines
+                    (bbdb-vcard-convert-to-3.0 vcard))))))))
+
+(defun bbdb-vcard-version-of (vcard)
+  "Return version number string of VCARD."
+  (with-temp-buffer
+    (insert vcard)
+    (car (bbdb-vcard-values-of-type "version" "value"))))
+
+(defun bbdb-vcard-import-vcard (vcard)
+  "Store VCARD (version 3.0) in BBDB.
+Extend existing BBDB records where possible."
+  (with-temp-buffer
+    (insert vcard)
+    (let* ((raw-name (car (bbdb-vcard-values-of-type "N" "value" t t)))
+           ;; Name suitable for storing in BBDB:
+           (name (bbdb-vcard-unescape-strings
+                  (bbdb-vcard-unvcardize-name raw-name)))
+           ;; Name to search for in BBDB now:
+           (name-to-search-for
+            (when raw-name (if (stringp raw-name)
+                               raw-name
+                             (concat (nth 1 raw-name) ;given name
+                                     " .*"
+                                     (nth 0 raw-name))))) ; family name
+           ;; Additional names from prefixed types like A.N, B.N, etc.:
+           (other-names
+            (mapcar
+             (lambda (n)
+               (bbdb-join (bbdb-vcard-unvcardize-name (cdr (assoc "value" n)))
+                          " "))
+             (bbdb-vcard-elements-of-type "N" nil t)))
+           (vcard-formatted-names (bbdb-vcard-unescape-strings
+                                   (bbdb-vcard-values-of-type "FN" "value")))
+           (vcard-nicknames
+            (bbdb-vcard-unescape-strings
+             (bbdb-vcard-split-structured-text
+              (car (bbdb-vcard-values-of-type "NICKNAME" "value"))
+              "," t)))
+           ;; Company suitable for storing in BBDB:
+           (vcard-org
+            (bbdb-vcard-unescape-strings
+             (bbdb-vcard-unvcardize-org
+              (car (bbdb-vcard-values-of-type "ORG" "value" t t)))))
+           ;; Company to search for in BBDB now:
+           (org-to-search-for vcard-org) ; sorry
+           ;; Email suitable for storing in BBDB:
+           (vcard-email (bbdb-vcard-values-of-type "EMAIL" "value"))
+           ;; Email to search for in BBDB now:
+           (email-to-search-for
+            (when vcard-email
+              (concat "\\(" (bbdb-join vcard-email "\\)\\|\\(") "\\)")))
+           ;; Phone numbers suitable for storing in BBDB:
+           (vcard-tels
+            (mapcar (lambda (tel)
+                      (vector (bbdb-vcard-translate
+                               (or (cdr (assoc "type" tel)) ""))
+                              (cdr (assoc "value" tel))))
+                    (bbdb-vcard-elements-of-type "TEL")))
+           ;; Phone numbers to search for in BBDB now:
+           (tel-to-search-for
+            (when vcard-tels
+              (concat "\\("
+                      (mapconcat (lambda (x) (elt x 1))
+                                 vcard-tels "\\)\\|\\(")
+                      "\\)")))
+           ;; Addresses
+           (vcard-adrs
+            (mapcar 'bbdb-vcard-unvcardize-adr
+                    (bbdb-vcard-elements-of-type "ADR" nil t)))
+           (vcard-url (car (bbdb-vcard-values-of-type "URL" "value" t)))
+           (vcard-notes (bbdb-vcard-values-of-type "NOTE" "value"))
+           (raw-bday (car (bbdb-vcard-values-of-type "BDAY" "value" t)))
+           ;; Birthday suitable for storing in BBDB (usable by org-mode):
+           (vcard-bday (when raw-bday (concat raw-bday " birthday")))
+           ;; Birthday to search for in BBDB now:
+           (bday-to-search-for vcard-bday)
+           ;; Non-birthday anniversaries, probably exported by ourselves:
+           (vcard-x-bbdb-anniversaries
+            (bbdb-vcard-split-structured-text
+             (car (bbdb-vcard-values-of-type "X-BBDB-ANNIVERSARY" "value"))
+             "\\\\n" t))
+           (vcard-rev (car (bbdb-vcard-values-of-type "REV" "value")))
+           (vcard-categories (bbdb-vcard-values-of-type "CATEGORIES" "value"))
+           ;; The BBDB record to change:
+           (record-freshness-info "BBDB record changed:") ; default user info
+           (bbdb-record
+            (or
+             ;; Try to find an existing one ...
+             ;; (a) try company and net and name:
+             (car (and bbdb-vcard-try-merge
+                       (bbdb-vcard-search-intersection
+                        (bbdb-records)
+                        name-to-search-for
+                        org-to-search-for email-to-search-for)))
+             ;; (b) try company and name:
+             (car (and bbdb-vcard-try-merge
+                       (bbdb-vcard-search-intersection
+                        (bbdb-records) name-to-search-for org-to-search-for)))
+             ;; (c) try net and name; we may change company here:
+             (car (and bbdb-vcard-try-merge
+                       (bbdb-vcard-search-intersection
+                        (bbdb-records)
+                        name-to-search-for nil email-to-search-for)))
+             ;; (d) try birthday and name; we may change company here:
+             (car (and bbdb-vcard-try-merge
+                       (bbdb-vcard-search-intersection
+                        (bbdb-records)
+                        name-to-search-for nil nil bday-to-search-for)))
+             ;; (e) try phone and name; we may change company here:
+             (car (and bbdb-vcard-try-merge
+                       (bbdb-vcard-search-intersection
+                        (bbdb-records)
+                        name-to-search-for nil nil nil tel-to-search-for)))
+             ;; No existing record found; make a fresh one:
+             (let ((fresh-record (make-vector bbdb-record-length nil)))
+               (bbdb-record-set-cache fresh-record
+                                      (make-vector bbdb-cache-length nil))
+               (if vcard-rev            ; For fresh records,
+                   (bbdb-record-putprop ; set creation-date from vcard-rev
+                    fresh-record 'creation-date
+                    (replace-regexp-in-string
+                     "\\([0-9]\\{4\\}-[01][0-9]-[0-3][0-9]\\).*" "\\1"
+                     vcard-rev))
+                 (bbdb-invoke-hook 'bbdb-create-hook fresh-record))
+               (setq record-freshness-info "BBDB record added:") ; user info
+               fresh-record)))
+           (bbdb-akas (bbdb-record-aka bbdb-record))
+           (bbdb-addresses (bbdb-record-addresses bbdb-record))
+           (bbdb-phones (bbdb-record-phones bbdb-record))
+           (bbdb-nets (bbdb-record-net bbdb-record))
+           (bbdb-raw-notes (bbdb-record-raw-notes bbdb-record))
+           notes
+           other-vcard-type)
+      (bbdb-vcard-elements-of-type "BEGIN")   ; get rid of delimiter
+      (bbdb-vcard-elements-of-type "END")     ; get rid of delimiter
+      (bbdb-vcard-elements-of-type "VERSION") ; get rid of this too
+      (when name ; which should be the case as N is mandatory in vCard
+        (bbdb-record-set-firstname bbdb-record (car name))
+        (bbdb-record-set-lastname bbdb-record (cadr name)))
+      (bbdb-record-set-aka
+       bbdb-record
+       (remove (concat (bbdb-record-firstname bbdb-record)
+                       " " (bbdb-record-lastname bbdb-record))
+               (reduce (lambda (x y) (union x y :test 'string=))
+                       (list vcard-nicknames
+                             other-names
+                             vcard-formatted-names
+                             bbdb-akas))))
+      (when vcard-org (bbdb-record-set-company bbdb-record vcard-org))
+      (bbdb-record-set-net
+       bbdb-record (union vcard-email bbdb-nets :test 'string=))
+      (bbdb-record-set-addresses
+       bbdb-record (union vcard-adrs bbdb-addresses :test 'equal))
+      (bbdb-record-set-phones bbdb-record
+                              (union vcard-tels bbdb-phones :test 'equal))
+      ;; prepare bbdb's notes:
+      (when vcard-url (push (cons 'www vcard-url) bbdb-raw-notes))
+      (when vcard-notes
+        ;; Put vCard NOTEs under key 'notes (append if necessary).
+        (unless (assq 'notes bbdb-raw-notes)
+          (push (cons 'notes "") bbdb-raw-notes))
+        (setf (cdr (assq 'notes bbdb-raw-notes))
+              (bbdb-vcard-merge-strings
+               (cdr (assq 'notes bbdb-raw-notes))
+               (bbdb-vcard-unescape-strings vcard-notes)
+               ";\n")))
+      (when (or vcard-bday vcard-x-bbdb-anniversaries)
+        ;; Put vCard BDAY and vCard X-BBDB-ANNIVERSARY's under key
+        ;; 'anniversary (append if necessary) where org-mode can find it.
+        (when vcard-bday (push vcard-bday vcard-x-bbdb-anniversaries))
+        (unless (assq 'anniversary bbdb-raw-notes)
+          (push (cons 'anniversary "") bbdb-raw-notes))
+        (setf (cdr (assq 'anniversary bbdb-raw-notes))
+              (bbdb-vcard-merge-strings
+               (cdr (assq 'anniversary bbdb-raw-notes))
+               (bbdb-vcard-unescape-strings vcard-x-bbdb-anniversaries)
+               "\n")))
+      (when vcard-categories
+        ;; Put vCard CATEGORIES under key 'mail-alias (append if necessary).
+        (unless (assq 'mail-alias bbdb-raw-notes)
+          (push (cons 'mail-alias "") bbdb-raw-notes))
+        (setf (cdr (assq 'mail-alias bbdb-raw-notes))
+              (bbdb-vcard-merge-strings
+               (cdr (assq 'mail-alias bbdb-raw-notes))
+               vcard-categories
+               ",")))
+      (while (setq other-vcard-type (bbdb-vcard-other-element))
+        (when (string-match "^\\([[:alnum:]-]*\\.\\)?AGENT"
+                            (symbol-name (car other-vcard-type)))
+          ;; Notice other vCards inside the current one.
+          (bbdb-vcard-iterate-vcards
+           'bbdb-vcard-import-vcard    ; needed for inner v2.1 vCards:
+           (replace-regexp-in-string "\\\\" "" (cdr other-vcard-type))))
+        (unless (or (and bbdb-vcard-skip-on-import
+                         (string-match bbdb-vcard-skip-on-import
+                                       (symbol-name (car other-vcard-type))))
+                    (and bbdb-vcard-skip-valueless
+                         (zerop (length (cdr other-vcard-type)))))
+          (push (bbdb-vcard-remove-x-bbdb other-vcard-type) bbdb-raw-notes)))
+      (bbdb-record-set-raw-notes
+       bbdb-record
+       (remove-duplicates bbdb-raw-notes :test 'equal :from-end t))
+      (bbdb-change-record bbdb-record t)
+      ;; Tell the user what we've done.
+      (message "%s %s %s -- %s"
+               record-freshness-info
+               (bbdb-record-firstname bbdb-record)
+               (bbdb-record-lastname bbdb-record)
+               (replace-regexp-in-string
+                "\n" "; " (or (bbdb-record-company bbdb-record) "-"))))))
+
+(defun bbdb-vcard-from (record)
+  "Return BBDB RECORD as a vCard."
+  (with-temp-buffer
+    (let* ((name (bbdb-record-name record))
+           (first-name (bbdb-record-firstname record))
+           (last-name (bbdb-record-lastname record))
+           (aka (bbdb-record-aka record))
+           (company (bbdb-record-company record))
+           (net (bbdb-record-net record))
+           (phones (bbdb-record-phones record))
+           (addresses (bbdb-record-addresses record))
+           (www (bbdb-get-field record 'www))
+           (notes
+            (bbdb-vcard-split-structured-text (bbdb-record-notes record)
+                                              ";\n" t))
+           (raw-anniversaries (bbdb-vcard-split-structured-text
+                               (bbdb-get-field record 'anniversary) "\n" t))
+           (birthday-regexp
+            "\\([0-9]\\{4\\}-[01][0-9]-[0-3][0-9]\\)\\([[:blank:]]+birthday\\)?\\'")
+           (birthday
+            (car (bbdb-vcard-split-structured-text
+                  (find-if (lambda (x) (string-match birthday-regexp x))
+                           raw-anniversaries)
+                  " " t)))
+           (other-anniversaries
+            (remove-if (lambda (x) (string-match birthday-regexp x))
+                       raw-anniversaries :count 1))
+           (creation-date (bbdb-get-field record 'creation-date))
+           (mail-aliases (bbdb-record-getprop record
+                                              bbdb-define-all-aliases-field))
+           (raw-notes (copy-alist (bbdb-record-raw-notes record))))
+      (bbdb-vcard-insert-vcard-element "BEGIN" "VCARD")
+      (bbdb-vcard-insert-vcard-element "VERSION" "3.0")
+      (bbdb-vcard-insert-vcard-element "FN" (bbdb-vcard-escape-strings name))
+      (bbdb-vcard-insert-vcard-element
+       "N" (bbdb-vcard-escape-strings last-name)
+       ";" (bbdb-vcard-escape-strings first-name)
+       ";;;") ; Additional Names, Honorific Prefixes, Honorific Suffixes
+      (bbdb-vcard-insert-vcard-element
+       "NICKNAME" (bbdb-join (bbdb-vcard-escape-strings aka) ","))
+      (bbdb-vcard-insert-vcard-element
+       "ORG" (bbdb-vcard-escape-strings company))
+      (dolist (mail net)
+        (bbdb-vcard-insert-vcard-element
+         "EMAIL;TYPE=INTERNET" (bbdb-vcard-escape-strings mail)))
+      (dolist (phone phones)
+        (bbdb-vcard-insert-vcard-element
+         (concat
+          "TEL;TYPE="
+          (bbdb-vcard-escape-strings
+           (bbdb-vcard-translate (bbdb-phone-location phone) t)))
+         (bbdb-vcard-escape-strings (bbdb-phone-string phone))))
+      (dolist (address addresses)
+        (bbdb-vcard-insert-vcard-element
+         (concat
+          "ADR;TYPE="
+          (bbdb-vcard-escape-strings
+           (bbdb-vcard-translate (bbdb-address-location address) t)))
+         ";;"                           ; no Postbox, no Extended
+         (bbdb-join (bbdb-vcard-escape-strings (bbdb-address-streets address))
+                    ",")
+         ";" (bbdb-vcard-vcardize-address-element
+              (bbdb-vcard-escape-strings (bbdb-address-city address)))
+         ";" (bbdb-vcard-vcardize-address-element
+              (bbdb-vcard-escape-strings (bbdb-address-state address)))
+         ";" (bbdb-vcard-vcardize-address-element
+              (bbdb-vcard-escape-strings (bbdb-address-zip address)))
+         ";" (bbdb-vcard-vcardize-address-element
+              (bbdb-vcard-escape-strings (bbdb-address-country address)))))
+      (bbdb-vcard-insert-vcard-element "URL" www)
+      (dolist (note notes)
+        (bbdb-vcard-insert-vcard-element
+         "NOTE" (bbdb-vcard-escape-strings note)))
+      (bbdb-vcard-insert-vcard-element "BDAY" birthday)
+      (bbdb-vcard-insert-vcard-element  ; non-birthday anniversaries
+       "X-BBDB-ANNIVERSARY" (bbdb-join other-anniversaries "\\n"))
+      (bbdb-vcard-insert-vcard-element "REV" creation-date)
+      (bbdb-vcard-insert-vcard-element
+       "CATEGORIES"
+       (bbdb-join (bbdb-vcard-escape-strings
+                   (bbdb-vcard-split-structured-text mail-aliases "," t)) ","))
+      ;; prune raw-notes...
+      (dolist (key '(www notes anniversary mail-alias creation-date timestamp))
+        (setq raw-notes (assq-delete-all key raw-notes)))
+      ;; ... and output what's left
+      (dolist (raw-note raw-notes)
+        (bbdb-vcard-insert-vcard-element
+         (symbol-name (bbdb-vcard-prepend-x-bbdb-maybe (car raw-note)))
+         (bbdb-vcard-escape-strings (cdr raw-note))))
+      (bbdb-vcard-insert-vcard-element "END" "VCARD")
+      (bbdb-vcard-insert-vcard-element nil)) ; newline
+    (buffer-string)))
+
+
+
+(defun bbdb-vcard-convert-to-3.0 (vcard)
+  "Convert VCARD from v2.1 to v3.0.
+Return a version 3.0 vCard as a string.  Don't bother about the vCard
+v3.0 mandatory elements N and FN."
+  (with-temp-buffer
+    (bbdb-vcard-insert-vcard-element "BEGIN" "VCARD")
+    (bbdb-vcard-insert-vcard-element "VERSION" "3.0")
+    (dolist (element (remove*
+                      "VERSION" (vcard-parse-string vcard)
+                      :key (lambda (x) (upcase (caar x))) :test 'string=))
+      (bbdb-vcard-insert-vcard-element
+       (concat (caar element)
+               (mapconcat 'bbdb-vcard-parameter-pair (cdar element) ""))
+       (bbdb-join (bbdb-vcard-escape-strings (cdr element)) ";")))
+    (bbdb-vcard-insert-vcard-element "END" "VCARD")
+    (bbdb-vcard-insert-vcard-element nil)
+    (replace-regexp-in-string "\n" "\r\n" (buffer-string))))
+
+(defun bbdb-vcard-parameter-pair (input)
+  "Return \"parameter=value\" made from INPUT.
+INPUT is its representation in vcard.el.  Return empty string if INPUT
+is nil."
+  (cond ((consp input) (concat ";" (car input) "=" (cdr input)))
+        ((stringp input) (concat ";TYPE=" input))
+        ((null input) "")))
+
+
+
+(defun bbdb-vcard-values-of-type
+  (type parameter &optional one-is-enough-p split-value-at-semi-colon-p)
+  "Return in a list the values of PARAMETER of vCard element of TYPE.
+The VCard element is read and deleted from current buffer which is
+supposed to contain a single vCard.  If ONE-IS-ENOUGH-P is non-nil,
+read and delete only the first element of TYPE.  If PARAMETER is
+\"value\" and SPLIT-VALUE-AT-SEMI-COLON-P is non-nil, split the value
+at semi-colons into a list."
+  (mapcar (lambda (x) (cdr (assoc parameter x)))
+          (bbdb-vcard-elements-of-type
+           type one-is-enough-p split-value-at-semi-colon-p)))
+
+(defun bbdb-vcard-elements-of-type
+  (type &optional one-is-enough-p split-value-at-semi-colon-p)
+  "From current buffer read and delete the vCard elements of TYPE.
+The current buffer is supposed to contain a single vCard.  If
+ONE-IS-ENOUGH-P is non-nil, read and delete only the first element of
+TYPE.  Return a list of alists, one per element.  Each alist has a
+cell with key \"value\" containing the element's value, and may have
+other elements of the form \(parameter-name . parameter-value).  If
+SPLIT-VALUE-AT-SEMI-COLON-P is non-nil, split the value at key
+\"value\" at semi-colons into a list."
+  (goto-char (point-min))
+  (let (values parameters read-enough)
+    (while
+        (and
+         (not read-enough)
+         (re-search-forward
+          (concat
+           "^\\([[:alnum:]-]*\\.\\)?\\(" type "\\)\\(;.*\\)?:\\(.*\\)\r$")
+          nil t))
+      (goto-char (match-end 2))
+      (setq parameters nil)
+      (push (cons "value" (if split-value-at-semi-colon-p
+                              (bbdb-vcard-split-structured-text
+                               (match-string 4) ";")
+                            (match-string 4)))
+            parameters)
+      (while (re-search-forward "\\([^;:=]+\\)=\\([^;:]+\\)"
+                                (line-end-position) t)
+        (let* ((parameter-key (downcase (match-string 1)))
+               (parameter-value (downcase (match-string 2)))
+               (parameter-sibling (assoc parameter-key parameters)))
+          (if parameter-sibling         ; i.e., pair with equal key
+              ;; collect vCard parameter list `;a=x;a=y;a=z'
+              ;; into vCard value list `;a=x,y,z'; becoming ("a" . "x,y,z")
+              (setf (cdr parameter-sibling)
+                    (concat (cdr parameter-sibling) "," parameter-value))
+            ;; vCard parameter pair `;key=value;' with new key
+            (push (cons parameter-key parameter-value) parameters))))
+      (push parameters values)
+      (delete-region (line-end-position 0) (line-end-position))
+      (when one-is-enough-p (setq read-enough t)))
+    (nreverse values)))
+
+(defun bbdb-vcard-other-element ()
+  "From current buffer read and delete the topmost vCard element.
+Buffer is supposed to contain a single vCard.  Return (TYPE . VALUE)."
+  (goto-char (point-min))
+  (when (re-search-forward "^\\([[:graph:]]*?\\):\\(.*\\)\r$" nil t)
+    (let ((type (match-string 1))
+          (value (match-string 2)))
+      (delete-region (match-beginning 0) (match-end 0))
+      (cons (intern (downcase type)) (bbdb-vcard-unescape-strings value)))))
+
+(defun bbdb-vcard-insert-vcard-element (type &rest values)
+  "Insert a vCard element comprising TYPE, `:', VALUES into current buffer.
+Take care of TYPE canonicalization, line folding, and closing newline.
+Do nothing if TYPE is non-nil and VALUES are empty.  Insert just a
+newline if TYPE is nil."
+  (if type
+      (let ((value (bbdb-join values "")))
+        (unless (zerop (length value))
+          (insert (bbdb-vcard-fold-line
+                   (concat (bbdb-vcard-canonicalize-vcard-type type)
+                           ":" value)))))
+    (insert (bbdb-vcard-fold-line ""))))
+
+
+
+(defun bbdb-vcard-unfold-lines (vcards)
+  "Return folded vCard lines from VCARDS unfolded."
+  (replace-regexp-in-string  "\r\n\\( \\|\t\\)" "" vcards))
+
+(defun bbdb-vcard-fold-line (long-line)
+  "Insert after every 75th position in LONG-LINE a newline and a space."
+  (with-temp-buffer (insert long-line)
+                    (goto-char (point-min))
+                    (while (< (goto-char (+ (point) 75))
+                              (point-max))
+                      (insert "\n "))
+                    (insert "\n")
+                    (buffer-string)))
+
+(defun bbdb-vcard-unescape-strings (escaped-strings)
+  "Unescape escaped `;', `,', `\\', and newlines in ESCAPED-STRINGS.
+ESCAPED-STRINGS may be a string or a sequence of strings."
+  (flet ((unescape (x) (replace-regexp-in-string
+                        "\\([\\\\]\\)\\([,;\\]\\)" ""
+                        (replace-regexp-in-string "\\\\n" "\n" x)
+                        nil nil 1)))
+    (bbdb-vcard-process-strings 'unescape escaped-strings)))
+
+(defun bbdb-vcard-escape-strings (unescaped-strings )
+  "Escape `;', `,', `\\', and newlines in UNESCAPED-STRINGS.
+UNESCAPED-STRINGS may be a string or a sequence of strings."
+  (flet ((escape (x) (replace-regexp-in-string
+                      "\r" "" (replace-regexp-in-string ; from 2.1 conversion
+                               "\n" "\\\\n" (replace-regexp-in-string
+                                             "\\(\\)[,;\\]" "\\\\" (or x "")
+                                             nil nil 1)))))
+    (bbdb-vcard-process-strings 'escape unescaped-strings)))
+
+(defun bbdb-vcard-process-strings (string-processor strings)
+  "Apply STRING-PROCESSOR to STRINGS.
+STRINGS may be a string or a sequence of strings."
+  (if (stringp strings)
+      (funcall string-processor strings)
+    (mapcar string-processor strings)))
+
+
+
+(defun bbdb-vcard-remove-x-bbdb (vcard-element)
+  "Remove the `X-BBDB-' prefix from the type part of VCARD-ELEMENT if any."
+  (cons (intern (replace-regexp-in-string
+                 "^X-BBDB-" "" (symbol-name (car vcard-element))))
+        (cdr vcard-element)))
+
+(defun bbdb-vcard-prepend-x-bbdb-maybe (bbdb-fieldname)
+  "If BBDB-FIELDNAME is in `bbdb-vcard-x-bbdb-candidates', prepend `X-BBDB'."
+  (if (member bbdb-fieldname bbdb-vcard-x-bbdb-candidates)
+      (intern (concat "x-bbdb-" (symbol-name bbdb-fieldname)))
+    bbdb-fieldname))                  ; lowercase more consistent here
+
+(defun bbdb-vcard-unvcardize-name (vcard-name)
+  "Convert VCARD-NAME (type N) into (FIRSTNAME LASTNAME)."
+  (if (stringp vcard-name)              ; unstructured N
+      (bbdb-divide-name vcard-name)
+    (let ((vcard-name
+           (mapcar (lambda (x)
+                     (bbdb-join (bbdb-vcard-split-structured-text x "," t)
+                                " "))
+                   vcard-name))) ; flatten comma-separated substructure
+      (list (concat (nth 3 vcard-name)  ; honorific prefixes
+                    (unless (zerop (length (nth 3 vcard-name))) " ")
+                    (nth 1 vcard-name)  ; given name
+                    (unless (zerop (length (nth 2 vcard-name))) " ")
+                    (nth 2 vcard-name)) ; additional names
+            (concat (nth 0 vcard-name)  ; family name
+                    (unless (zerop (length (nth 4 vcard-name))) " ")
+                    (nth 4 vcard-name)))))) ; honorific suffixes
+
+(defun bbdb-vcard-unvcardize-org (vcard-org)
+  "Convert VCARD-ORG (type ORG), which may be a list, into a string."
+  (if (or (null vcard-org)
+          (stringp vcard-org)) ; unstructured, probably non-standard ORG
+      vcard-org                ; Company, unit 1, unit 2...
+    (bbdb-join vcard-org "\n")))
+
+(defun bbdb-vcard-unvcardize-adr (vcard-adr)
+  "Convert VCARD-ADR into BBDB format.
+Turn a vCard element of type ADR into (TYPE STREETS CITY STATE ZIP
+COUNTRY)."
+  (let ((adr-type (or (cdr (assoc "type" vcard-adr)) ""))
+        (streets         ; all comma-separated sub-elements of
+         (remove         ; Postbox, Extended, Streets go into one list
+          "" (reduce 'append
+                     (mapcar (lambda (x)
+                               (bbdb-vcard-split-structured-text x "," t))
+                             (subseq (cdr (assoc "value" vcard-adr))
+                                     0 3)))))
+        (non-streets          ; turn comma-separated substructure into
+         (mapcar              ; newline-separated text
+          (lambda (x) (bbdb-join
+                       (bbdb-vcard-split-structured-text x "," t)
+                       "\n"))
+          (subseq (cdr (assoc "value" vcard-adr))
+                  3 nil))))
+    (vector (bbdb-vcard-translate adr-type)
+            streets
+            (or (elt non-streets 0) "")    ; City
+            (or (elt non-streets 1) "")    ; State
+            (or (elt non-streets 2) "")    ; Zip
+            (or (elt non-streets 3) "")))) ; Country
+
+(defun bbdb-vcard-vcardize-address-element (address-element)
+  "Replace escaped newlines in ADDRESS-ELEMENT by commas."
+  (replace-regexp-in-string "\\\\n" "," address-element))
+
+(defun bbdb-vcard-translate (label &optional exportp)
+  "Translate LABEL from vCard to BBDB or, if EXPORTP is non-nil, vice versa.
+Translations are defined in `bbdb-vcard-import-translation-table' and
+`bbdb-vcard-export-translation-table' respectively."
+  (when label
+    (capitalize
+     (or (assoc-default label
+                        (if exportp
+                            bbdb-vcard-export-translation-table
+                          bbdb-vcard-import-translation-table) 'string-match)
+         label))))
+
+(defun bbdb-vcard-merge-strings (old-string new-strings separator)
+  "Merge strings successively from list NEW-STRINGS into OLD-STRING.
+If an element of NEW-STRINGS is already in OLD-STRING, leave
+OLD-STRING unchanged.  Otherwise append SEPARATOR and NEW-STRING."
+  (with-temp-buffer
+    (insert old-string)
+    (dolist (new-string new-strings)
+      (unless (prog1 (search-backward new-string nil t)
+                (goto-char (point-max)))
+        (unless (zerop (buffer-size)) (insert separator))
+        (insert new-string)))
+    (buffer-string)))
+
+(defun bbdb-vcard-split-structured-text
+  (text separator &optional return-always-list-p)
+  "Split TEXT at unescaped occurrences of SEPARATOR; return parts in a list.
+Return text unchanged if there aren't any separators and RETURN-ALWAYS-LIST-P
+is nil."
+  (when (stringp text)
+    (let ((string-elements
+           (split-string
+            (replace-regexp-in-string
+             (concat "\\\\\r" separator) (concat "\\\\" separator)
+             (replace-regexp-in-string separator (concat "\r" separator) text))
+            (concat "\r" separator))))
+      (if (and (null return-always-list-p)
+               (= 1 (length string-elements)))
+          (car string-elements)
+        string-elements))))
+
+(defun bbdb-vcard-canonicalize-vcard-type (&rest strings)
+  "Concatenate STRINGS and apply `bbdb-vcard-type-canonicalizer' to them."
+  (funcall bbdb-vcard-type-canonicalizer (bbdb-join strings "")))
+
+(defun bbdb-vcard-write-buffer (vcard-file-name)
+  "Write current buffer to VCARD-FILE-NAME.
+Create directories where necessary."
+  (make-directory (file-name-directory vcard-file-name) t)
+  (let ((buffer-file-coding-system bbdb-vcard-export-coding-system))
+    (write-region nil nil vcard-file-name nil nil nil t)))
+
+(defun bbdb-vcard-make-file-name (bbdb-record &optional used-up-basenames)
+  "Come up with a vCard filename given a BBDB-RECORD.
+Make it unique against the list USED-UP-BASENAMES."
+  (let ((name (bbdb-record-name bbdb-record))
+        (aka (car (bbdb-record-aka bbdb-record)))
+        (unique-number 0)
+        filename)
+    (while (member
+            (setq filename
+                  (concat
+                   (replace-regexp-in-string
+                    "[[:blank:]]+" "_"
+                    (or (unless (zerop (length name)) name)
+                        (unless (zerop (length aka)) aka)
+                        "bbdb-record"))
+                   (unless (zerop unique-number)
+                     (concat "-" (number-to-string unique-number)))
+                   ".vcf"))
+            used-up-basenames)
+      (incf unique-number))
+    filename))
+
+(defmacro bbdb-vcard-search-intersection
+  (records &optional name company net notes phone)
+  "Search RECORDS for records that match each non-nil argument."
+  (let*
+      ((phone-search
+        (if phone `(when ,phone (bbdb-search ,records nil nil nil nil ,phone))
+          records))
+       (notes-search
+        (if notes `(when ,notes (bbdb-search ,phone-search nil nil nil ,notes))
+          phone-search))
+       (net-search
+        (if net `(when ,net (bbdb-search ,notes-search nil nil ,net))
+          notes-search))
+       (company-search
+        (if company `(when ,company (bbdb-search ,net-search nil ,company))
+          net-search))
+       (name-search
+        (if name `(when ,name (bbdb-search ,company-search ,name))
+          company-search)))
+    name-search))
+
+
+
+(provide 'bbdb-vcard)
+
+;;; bbdb-vcard.el ends here
--- /dev/null
+++ bbdb-2.36/extern/bbdb-vcard/vcard.el
@@ -0,0 +1,704 @@
+;;; vcard.el --- vcard parsing and display routines
+
+;; Copyright (C) 1997, 1999, 2000 Noah S. Friedman
+
+;; Author: Noah Friedman <friedman@splode.com>
+;; Maintainer: friedman@splode.com
+;; Keywords: vcard, mail, news
+;; Created: 1997-09-27
+
+;; $Id: vcard.el,v 1.11 2000/06/29 17:07:55 friedman Exp $
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+;;
+;; This program 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 General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, you can either send email to this
+;; program's maintainer or write to: The Free Software Foundation,
+;; Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Unformatted vcards are just plain ugly.  But if you live in the MIME
+;; world, they are a better way of exchanging contact information than
+;; freeform signatures since the former can be automatically parsed and
+;; stored in a searchable index.
+;;
+;; This library of routines provides the back end necessary for parsing
+;; vcards so that they can eventually go into an address book like BBDB
+;; (although this library does not implement that itself).  Also included
+;; is a sample pretty-printer which MUAs can use which do not provide their
+;; own vcard formatters.
+
+;; This library does not interface directly with any mail user agents.  For
+;; an example of bindings for the VM MUA, see vm-vcard.el available from
+;;
+;;    http://www.splode.com/~friedman/software/emacs-lisp/index.html#mail
+;;
+;; Updates to vcard.el should be available there too.
+
+;; The main entry point to this package is `vcard-pretty-print' although
+;; any documented variable or function is considered part of the API for
+;; operating on vcard data.
+
+;; The vcard 2.1 format is defined by the versit consortium.
+;; See http://www.imc.org/pdi/vcard-21.ps
+;;
+;; RFC 2426 defines the vcard 3.0 format.
+;; See ftp://ftp.rfc-editor.org/in-notes/rfc2426.txt
+
+;; A parsed vcard is a list of attributes of the form
+;;
+;;     (proplist value1 value2 ...)
+;;
+;; Where proplist is a list of property names and parameters, e.g.
+;;
+;;     (property1 (property2 . parameter2) ...)
+;;
+;; Each property has an associated implicit or explicit parameter value
+;; (not to be confused with attribute values; in general this API uses
+;; `parameter' to refer to property values and `value' to refer to attribute
+;; values to avoid confusion).  If a property has no explicit parameter value,
+;; the parameter value is considered to be `t'.  Any property which does not
+;; exist for an attribute is considered to have a nil parameter.
+
+;; TODO:
+;;   * Finish supporting the 3.0 extensions.
+;;     Currently, only the 2.1 standard is supported.
+;;   * Handle nested vcards and grouped attributes?
+;;     (I've never actually seen one of these in use.)
+;;   * Handle multibyte charsets.
+;;   * Inverse of vcard-parse-string: write .VCF files from alist
+;;   * Implement a vcard address book?  Or is using BBDB preferable?
+;;   * Improve the sample formatter.
+
+;;; Code:
+
+(defgroup vcard nil
+  "Support for the vCard electronic business card format."
+  :group 'vcard
+  :group 'mail
+  :group 'news)
+
+;;;###autoload
+(defcustom vcard-pretty-print-function 'vcard-format-sample-box
+  "*Formatting function used by `vcard-pretty-print'."
+  :type 'function
+  :group 'vcard)
+
+;;;###autoload
+(defcustom vcard-standard-filters
+  '(vcard-filter-html
+    vcard-filter-adr-newlines
+    vcard-filter-tel-normalize
+    vcard-filter-textprop-cr)
+  "*Standard list of filters to apply to parsed vcard data.
+These filters are applied sequentially to vcard attributes when
+the function `vcard-standard-filter' is supplied as the second argument to
+`vcard-parse'."
+  :type 'hook
+  :group 'vcard)
+
+
+;;; No user-settable options below.
+
+;; XEmacs 21 ints and chars are disjoint types.
+;; For all else, treat them as the same.
+(defalias 'vcard-char-to-int
+  (if (fboundp 'char-to-int) 'char-to-int 'identity))
+
+;; This is just the version number for this package; it does not refer to
+;; the vcard format specification.  Currently, this package does not yet
+;; support the full vcard 3.0 specification.
+;;
+;; Whenever any part of the API defined in this package change in a way
+;; that is not backward-compatible, the major version number here should be
+;; incremented.  Backward-compatible additions to the API should be
+;; indicated by increasing the minor version number.
+(defconst vcard-api-version "2.0")
+
+;; The vcard standards allow specifying the encoding for an attribute using
+;; these values as immediate property names, rather than parameters of the
+;; `encoding' property.  If these are encountered while parsing, associate
+;; them as parameters of the `encoding' property in the returned structure.
+(defvar vcard-encoding-tags
+  '("quoted-printable" "base64" "8bit" "7bit"))
+
+;; The vcard parser will auto-decode these encodings when they are
+;; encountered.  These methods are invoked via vcard-parse-region-value.
+(defvar vcard-region-decoder-methods
+  '(("quoted-printable" . vcard-region-decode-quoted-printable)
+    ("base64"           . vcard-region-decode-base64)))
+
+;; This is used by vcard-region-decode-base64
+(defvar vcard-region-decode-base64-table
+  (let* ((a "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
+         (len (length a))
+         (tbl (make-vector 123 nil))
+         (i 0))
+    (while (< i len)
+      (aset tbl (vcard-char-to-int (aref a i)) i)
+      (setq i (1+ i)))
+    tbl))
+
+
+;;; This function can be used generically by applications to obtain
+;;; a printable representation of a vcard.
+
+;;;###autoload
+(defun vcard-pretty-print (vcard)
+  "Format VCARD into a string suitable for display to user.
+VCARD can be an unparsed string containing raw VCF vcard data
+or a parsed vcard alist as returned by `vcard-parse-string'.
+
+The result is a string with formatted vcard information suitable for
+insertion into a mime presentation buffer.
+
+The function specified by the variable `vcard-pretty-print-function'
+actually performs the formatting.  That function will always receive a
+parsed vcard alist."
+  (and (stringp vcard)
+       (setq vcard (vcard-parse-string vcard)))
+  (funcall vcard-pretty-print-function vcard))
+
+
+;;; Parsing routines
+
+;;;###autoload
+(defun vcard-parse-string (raw &optional filter)
+  "Parse RAW vcard data as a string, and return an alist representing data.
+
+If the optional function FILTER is specified, apply that filter to each
+attribute.  If no filter is specified, `vcard-standard-filter' is used.
+
+Filters should accept two arguments: the property list and the value list.
+Modifying in place the property or value list will affect the resulting
+attribute in the vcard alist.
+
+Vcard data is normally in the form
+
+    begin:                        vcard
+    prop1a:                       value1a
+    prop2a;prop2b;prop2c=param2c: value2a
+    prop3a;prop3b:                value3a;value3b;value3c
+    end:                          vcard
+
+\(Whitespace around the `:' separating properties and values is optional.\)
+If supplied to this function an alist of the form
+
+    \(\(\(\"prop1a\"\) \"value1a\"\)
+     \(\(\"prop2a\" \"prop2b\" \(\"prop2c\" . \"param2c\"\)\) \"value2a\"\)
+     \(\(\"prop3a\" \"prop3b\"\) \"value3a\" \"value3b\" \"value3c\"\)\)
+
+would be returned."
+  (let ((vcard nil)
+        (buf (generate-new-buffer " *vcard parser work*")))
+    (unwind-protect
+        (save-excursion
+          (set-buffer buf)
+          ;; Make sure last line is newline-terminated.
+          ;; An extra trailing newline is harmless.
+          (insert raw "\n")
+          (setq vcard (vcard-parse-region (point-min) (point-max) filter)))
+      (kill-buffer buf))
+    vcard))
+
+;;;###autoload
+(defun vcard-parse-region (beg end &optional filter)
+  "Parse the raw vcard data in region, and return an alist representing data.
+This function is just like `vcard-parse-string' except that it operates on
+a region of the current buffer rather than taking a string as an argument.
+
+Note: this function modifies the buffer!"
+  (or filter
+      (setq filter 'vcard-standard-filter))
+  (let ((case-fold-search t)
+        (vcard-data nil)
+        (pos (make-marker))
+        (newpos (make-marker))
+        properties value)
+    (save-restriction
+      (narrow-to-region beg end)
+      (save-match-data
+        ;; Unfold folded lines and delete naked carriage returns
+        (goto-char (point-min))
+        (while (re-search-forward "\r$\\|\n[ \t]" nil t)
+          (goto-char (match-beginning 0))
+          (delete-char 1))
+
+        (goto-char (point-min))
+        (re-search-forward "^begin:[ \t]*vcard[ \t]*\n")
+        (set-marker pos (point))
+        (while (and (not (looking-at "^end[ \t]*:[ \t]*vcard[ \t]*$"))
+                    (re-search-forward ":[ \t]*" nil t))
+          (set-marker newpos (match-end 0))
+          (setq properties
+                (vcard-parse-region-properties pos (match-beginning 0)))
+          (set-marker pos (marker-position newpos))
+          (re-search-forward "[ \t]*\n")
+          (set-marker newpos (match-end 0))
+          (setq value
+                (vcard-parse-region-value properties pos (match-beginning 0)))
+          (set-marker pos (marker-position newpos))
+          (goto-char pos)
+          (funcall filter properties value)
+          (setq vcard-data (cons (cons properties value) vcard-data)))))
+    (nreverse vcard-data)))
+
+(defun vcard-parse-region-properties (beg end)
+  (downcase-region beg end)
+  (let* ((proplist (vcard-split-string (buffer-substring beg end) ";"))
+         (props proplist)
+         split)
+    (save-match-data
+      (while props
+        (cond ((string-match "=" (car props))
+               (setq split (vcard-split-string (car props) "=" 2))
+               (setcar props (cons (car split) (car (cdr split)))))
+              ((member (car props) vcard-encoding-tags)
+               (setcar props (cons "encoding" (car props)))))
+        (setq props (cdr props))))
+    proplist))
+
+(defun vcard-parse-region-value (proplist beg end)
+  (let* ((encoding (vcard-get-property proplist "encoding"))
+         (decoder (cdr (assoc encoding vcard-region-decoder-methods)))
+         result pos match-beg match-end)
+    (save-restriction
+      (narrow-to-region beg end)
+      (cond (decoder
+             ;; Each `;'-separated field needs to be decoded and saved
+             ;; separately; if the entire region were decoded at once, we
+             ;; would not be able to distinguish between the original `;'
+             ;; chars and those which were encoded in order to quote them
+             ;; against being treated as field separators.
+             (goto-char beg)
+             (setq pos (set-marker (make-marker) (point)))
+             (setq match-beg (make-marker))
+             (setq match-end (make-marker))
+             (save-match-data
+               (while (< pos (point-max))
+                 (cond ((search-forward ";" nil t)
+                        (set-marker match-beg (match-beginning 0))
+                        (set-marker match-end (match-end 0)))
+                       (t
+                        (set-marker match-beg (point-max))
+                        (set-marker match-end (point-max))))
+                 (funcall decoder pos match-beg)
+                 (setq result (cons (buffer-substring pos match-beg) result))
+                 (set-marker pos (marker-position match-end))))
+             (setq result (nreverse result))
+             (vcard-set-property proplist "encoding" nil))
+            (t
+             (setq result (vcard-split-string (buffer-string) ";")))))
+    (goto-char (point-max))
+    result))
+
+
+;;; Functions for retrieving property or value information from parsed
+;;; vcard attributes.
+
+(defun vcard-values (vcard have-props &optional non-props limit)
+  "Return the values in VCARD.
+This function is like `vcard-ref' and takes the same arguments, but return
+only the values, not the associated property lists."
+  (mapcar 'cdr (vcard-ref vcard have-props non-props limit)))
+
+(defun vcard-ref (vcard have-props &optional non-props limit)
+  "Return the attributes in VCARD with HAVE-PROPS properties.
+Optional arg NON-PROPS is a list of properties which candidate attributes
+must not have.
+Optional arg LIMIT means return no more than that many attributes.
+
+The attributes in VCARD which have all properties specified by HAVE-PROPS
+but not having any specified by NON-PROPS are returned.  The first element
+of each attribute is the actual property list; the remaining elements are
+the values.
+
+If a specific property has an associated parameter \(e.g. an encoding\),
+use the syntax \(\"property\" . \"parameter\"\) to specify it.  If property
+parameter is not important or it has no specific parameter, just specify
+the property name as a string."
+  (let ((attrs vcard)
+        (result nil)
+        (count 0))
+    (while (and attrs (or (null limit) (< count limit)))
+      (and (vcard-proplist-all-properties (car (car attrs)) have-props)
+           (not (vcard-proplist-any-properties (car (car attrs)) non-props))
+           (setq result (cons (car attrs) result)
+                 count (1+ count)))
+      (setq attrs (cdr attrs)))
+    (nreverse result)))
+
+(defun vcard-proplist-all-properties (proplist props)
+  "Returns nil unless PROPLIST contains all properties specified in PROPS."
+  (let ((result t))
+    (while (and result props)
+      (or (vcard-get-property proplist (car props))
+          (setq result nil))
+      (setq props (cdr props)))
+    result))
+
+(defun vcard-proplist-any-properties (proplist props)
+  "Returns `t' if PROPLIST contains any of the properties specified in PROPS."
+  (let ((result nil))
+    (while (and (not result) props)
+      (and (vcard-get-property proplist (car props))
+           (setq result t))
+      (setq props (cdr props)))
+    result))
+
+(defun vcard-get-property (proplist property)
+  "Return the value from PROPLIST of PROPERTY.
+PROPLIST is a vcard attribute property list, which is normally the first
+element of each attribute entry in a vcard."
+  (or (and (member property proplist) t)
+      (cdr (assoc property proplist))))
+
+(defun vcard-set-property (proplist property value)
+  "In PROPLIST, set PROPERTY to VALUE.
+PROPLIST is a vcard attribute property list.
+If VALUE is nil, PROPERTY is deleted."
+  (let (elt)
+    (cond ((null value)
+           (vcard-delete-property proplist property))
+          ((setq elt (member property proplist))
+           (and value (not (eq value t))
+                (setcar elt (cons property value))))
+          ((setq elt (assoc property proplist))
+           (cond ((eq value t)
+                  (setq elt (memq elt proplist))
+                  (setcar elt property))
+                 (t
+                  (setcdr elt value))))
+          ((eq value t)
+           (nconc proplist (cons property nil)))
+          (t
+           (nconc proplist (cons (cons property value) nil))))))
+
+(defun vcard-delete-property (proplist property)
+  "Delete from PROPLIST the specified property PROPERTY.
+This will not succeed in deleting the first member of the proplist, but
+that element should never be deleted since it is the primary key."
+  (let (elt)
+    (cond ((setq elt (member property proplist))
+           (delq (car elt) proplist))
+          ((setq elt (assoc property proplist))
+           (delq (car (memq elt proplist)) proplist)))))
+
+
+;;; Vcard data filters.
+;;;
+;;; Filters receive both the property list and value list and may modify
+;;; either in-place.  The return value from the filters are ignored.
+;;;
+;;; These filters can be used for purposes such as removing HTML tags or
+;;; normalizing phone numbers into a standard form.
+
+(defun vcard-standard-filter (proplist values)
+  "Apply filters in `vcard-standard-filters' to attributes."
+  (vcard-filter-apply-filter-list vcard-standard-filters proplist values))
+
+;; This function could be used to dispatch other filter lists.
+(defun vcard-filter-apply-filter-list (filter-list proplist values)
+  (while filter-list
+    (funcall (car filter-list) proplist values)
+    (setq filter-list (cdr filter-list))))
+
+;; Some lusers put HTML (or even javascript!) in their vcards under the
+;; misguided notion that it's a standard feature of vcards just because
+;; Netscape supports this feature.  That is wrong; the vcard specification
+;; does not define any html content semantics and most MUAs cannot do
+;; anything with html text except display them unparsed, which is ugly.
+;;
+;; Thank Netscape for abusing the standard and damned near rendering it
+;; useless for interoperability between MUAs.
+;;
+;; This filter does a very rudimentary job.
+(defun vcard-filter-html (proplist values)
+  "Remove HTML tags from attribute values."
+  (save-match-data
+    (while values
+      (while (string-match "<[^<>\n]+>" (car values))
+        (setcar values (replace-match "" t t (car values))))
+      (setq values (cdr values)))))
+
+(defun vcard-filter-adr-newlines (proplist values)
+  "Replace newlines with \"; \" in `adr' values."
+  (and (vcard-get-property proplist "adr")
+       (save-match-data
+         (while values
+           (while (string-match "[\r\n]+" (car values))
+             (setcar values (replace-match "; " t t (car values))))
+           (setq values (cdr values))))))
+
+(defun vcard-filter-tel-normalize (proplist values)
+  "Normalize telephone numbers in `tel' values.
+Spaces and hyphens are replaced with `.'.
+US domestic telephone numbers are replaced with international format."
+  (and (vcard-get-property proplist "tel")
+       (save-match-data
+         (while values
+           (while (string-match "[\t._-]+" (car values))
+             (setcar values (replace-match " " t t (car values))))
+           (and (string-match "^(?\\(\\S-\\S-\\S-\\))? ?\
+\\(\\S-\\S-\\S- \\S-\\S-\\S-\\S-\\)"
+                              (car values))
+                (setcar values
+                        (replace-match "+1 \\1 \\2" t nil (car values))))
+           (setq values (cdr values))))))
+
+(defun vcard-filter-textprop-cr (proplist values)
+  "Strip carriage returns from text values."
+  (and (vcard-proplist-any-properties
+        proplist '("adr" "email" "fn" "label" "n" "org" "tel" "title" "url"))
+       (save-match-data
+         (while values
+           (while (string-match "\r+" (car values))
+             (setcar values (replace-match "" t t (car values))))
+           (setq values (cdr values))))))
+
+
+;;; Decoding methods.
+
+(defmacro vcard-hexstring-to-ascii (s)
+  (if (string-lessp emacs-version "20")
+      `(format "%c" (car (read-from-string (format "?\\x%s" ,s))))
+    `(format "%c" (string-to-number ,s 16))))
+
+(defun vcard-region-decode-quoted-printable (&optional beg end)
+  (save-excursion
+    (save-restriction
+      (save-match-data
+        (narrow-to-region (or beg (point-min)) (or end (point-max)))
+        (goto-char (point-min))
+        (while (re-search-forward "=\n" nil t)
+          (delete-region (match-beginning 0) (match-end 0)))
+        (goto-char (point-min))
+        (while (re-search-forward "=[0-9A-Za-z][0-9A-Za-z]" nil t)
+          (let ((s (buffer-substring (1+ (match-beginning 0)) (match-end 0))))
+            (replace-match (vcard-hexstring-to-ascii s) t t)))))))
+
+(defun vcard-region-decode-base64 (&optional beg end)
+  (save-restriction
+    (narrow-to-region (or beg (point-min)) (or end (point-max)))
+    (save-match-data
+      (goto-char (point-min))
+      (while (re-search-forward "[ \t\r\n]+" nil t)
+        (delete-region (match-beginning 0) (match-end 0))))
+    (goto-char (point-min))
+    (let ((count 0)
+          (n 0)
+          (c nil))
+      (while (not (eobp))
+        (setq c (char-after (point)))
+        (delete-char 1)
+        (cond ((char-equal c ?=)
+               (if (= count 2)
+                   (insert (lsh n -10))
+                 ;; count must be 3
+                 (insert (lsh n -16) (logand 255 (lsh n -8))))
+               (delete-region (point) (point-max)))
+              (t
+               (setq n (+ n (aref vcard-region-decode-base64-table
+                                  (vcard-char-to-int c))))
+               (setq count (1+ count))
+               (cond ((= count 4)
+                      (insert (logand 255 (lsh n -16))
+                              (logand 255 (lsh n -8))
+                              (logand 255 n))
+                      (setq n 0 count 0))
+                     (t
+                      (setq n (lsh n 6))))))))))
+
+
+(defun vcard-split-string (string &optional separator limit)
+  "Split STRING at occurences of SEPARATOR.  Return a list of substrings.
+Optional argument SEPARATOR can be any regexp, but anything matching the
+ separator will never appear in any of the returned substrings.
+ If not specified, SEPARATOR defaults to \"[ \\f\\t\\n\\r\\v]+\".
+If optional arg LIMIT is specified, split into no more than that many
+ fields \(though it may split into fewer\)."
+  (or separator (setq separator "[ \f\t\n\r\v]+"))
+  (let ((string-list nil)
+        (len (length string))
+        (pos 0)
+        (splits 0)
+        str)
+    (save-match-data
+      (while (<= pos len)
+        (setq splits (1+ splits))
+        (cond ((and limit
+                    (>= splits limit))
+               (setq str (substring string pos))
+               (setq pos (1+ len)))
+              ((string-match separator string pos)
+               (setq str (substring string pos (match-beginning 0)))
+               (setq pos (match-end 0)))
+              (t
+               (setq str (substring string pos))
+               (setq pos (1+ len))))
+        (setq string-list (cons str string-list))))
+    (nreverse string-list)))
+
+(defun vcard-copy-tree (tree)
+  "Make a deep copy of nested conses."
+  (cond
+   ((consp tree)
+    (cons (vcard-copy-tree (car tree))
+          (vcard-copy-tree (cdr tree))))
+   (t tree)))
+
+(defun vcard-flatten (l)
+  (if (consp l)
+      (apply 'nconc (mapcar 'vcard-flatten l))
+    (list l)))
+
+
+;;; Sample formatting routines.
+
+(defun vcard-format-sample-box (vcard)
+  "Like `vcard-format-sample-string', but put an ascii box around text."
+  (let* ((lines (vcard-format-sample-lines vcard))
+         (len (vcard-format-sample-max-length lines))
+         (edge (concat "\n+" (make-string (+ len 2) ?-) "+\n"))
+         (line-fmt (format "| %%-%ds |" len))
+         (formatted-lines
+          (mapconcat (function (lambda (s) (format line-fmt s))) lines "\n")))
+    (if (string= formatted-lines "")
+        formatted-lines
+      (concat edge formatted-lines edge))))
+
+(defun vcard-format-sample-string (vcard)
+  "Format VCARD into a string suitable for display to user.
+VCARD should be a parsed vcard alist.  The result is a string
+with formatted vcard information which can be inserted into a mime
+presentation buffer."
+  (mapconcat 'identity (vcard-format-sample-lines vcard) "\n"))
+
+(defun vcard-format-sample-lines (vcard)
+  (let* ((name  (vcard-format-sample-get-name vcard))
+         (title (vcard-format-sample-values-concat vcard '("title") 1 "; "))
+         (org   (vcard-format-sample-values-concat vcard '("org")   1 "; "))
+         (addr  (vcard-format-sample-get-address vcard))
+         (tel   (vcard-format-sample-get-telephone vcard))
+         (lines (delete nil (vcard-flatten (list name title org addr))))
+         (col-template (format "%%-%ds%%s"
+                               (vcard-format-sample-offset lines tel)))
+         (l lines))
+    (while tel
+      (setcar l (format col-template (car l) (car tel)))
+      ;; If we stripped away too many nil slots from l, add empty strings
+      ;; back in so setcar above will work on next iteration.
+      (and (cdr tel)
+           (null (cdr l))
+           (setcdr l (cons "" nil)))
+      (setq l (cdr l))
+      (setq tel (cdr tel)))
+    lines))
+
+(defun vcard-format-sample-get-name (vcard)
+  (let ((name (car (car (vcard-values vcard '("fn") nil 1))))
+        (email (car (vcard-format-sample-values
+                     vcard '((("email" "pref"))
+                             (("email" "internet"))
+                             (("email"))) 1))))
+    (cond ((and name email)
+           (format "%s <%s>" name email))
+          (email)
+          (name)
+          (""))))
+
+(defun vcard-format-sample-get-telephone (vcard)
+  (let ((fields '(("Work: "
+                   (("tel" "work" "pref")  . ("fax" "pager" "cell"))
+                   (("tel" "work" "voice") . ("fax" "pager" "cell"))
+                   (("tel" "work")         . ("fax" "pager" "cell")))
+                  ("Home: "
+                   (("tel" "home" "pref")  . ("fax" "pager" "cell"))
+                   (("tel" "home" "voice") . ("fax" "pager" "cell"))
+                   (("tel" "home")         . ("fax" "pager" "cell"))
+                   (("tel")                . ("fax" "pager" "cell" "work")))
+                  ("Cell: "
+                   (("tel" "cell" "pref"))
+                   (("tel" "cell")))
+                  ("Fax:  "
+                   (("tel" "pref" "fax"))
+                   (("tel" "work" "fax"))
+                   (("tel" "home" "fax"))
+                   (("tel" "fax")))))
+        (phones nil)
+        result)
+    (while fields
+      (setq result (vcard-format-sample-values vcard (cdr (car fields))))
+      (while result
+        (setq phones
+              (cons (concat (car (car fields)) (car (car result))) phones))
+        (setq result (cdr result)))
+      (setq fields (cdr fields)))
+    (nreverse phones)))
+
+(defun vcard-format-sample-get-address (vcard)
+  (let* ((addr (vcard-format-sample-values vcard '((("adr" "pref" "work"))
+                                                   (("adr" "pref"))
+                                                   (("adr" "work"))
+                                                   (("adr"))) 1))
+         (street (delete "" (list (nth 0 addr) (nth 1 addr) (nth 2 addr))))
+         (city-list (delete "" (nthcdr 3 addr)))
+         (city (cond ((null (car city-list)) nil)
+                     ((cdr city-list)
+                      (format "%s, %s"
+                              (car city-list)
+                              (mapconcat 'identity (cdr city-list) " ")))
+                     (t (car city-list)))))
+    (delete nil (if city
+                    (append street (list city))
+                  street))))
+
+(defun vcard-format-sample-values-concat (vcard have-props limit sep)
+  (let ((l (car (vcard-values vcard have-props nil limit))))
+    (and l (mapconcat 'identity (delete "" (vcard-copy-tree l)) sep))))
+
+(defun vcard-format-sample-values (vcard proplists &optional limit)
+  (let ((result (vcard-format-sample-ref vcard proplists limit)))
+    (if (equal limit 1)
+        (cdr result)
+      (mapcar 'cdr result))))
+
+(defun vcard-format-sample-ref (vcard proplists &optional limit)
+  (let ((result nil))
+    (while (and (null result) proplists)
+      (setq result (vcard-ref vcard
+                              (car (car proplists))
+                              (cdr (car proplists))
+                              limit))
+      (setq proplists (cdr proplists)))
+    (if (equal limit 1)
+        (vcard-copy-tree (car result))
+      (vcard-copy-tree result))))
+
+(defun vcard-format-sample-offset (row1 row2 &optional maxwidth)
+  (or maxwidth (setq maxwidth (frame-width)))
+  (let ((max1 (vcard-format-sample-max-length row1))
+        (max2 (vcard-format-sample-max-length row2)))
+    (if (zerop max1)
+        0
+      (+ max1 (min 5 (max 1 (- maxwidth (+ max1 max2))))))))
+
+(defun vcard-format-sample-max-length (strings)
+  (let ((maxlen 0))
+    (while strings
+      (setq maxlen (max maxlen (length (car strings))))
+      (setq strings (cdr strings)))
+    maxlen))
+
+(provide 'vcard)
+
+;;; vcard.el ends here.
--- /dev/null
+++ bbdb-2.36/extern/bbdb-vcard/test-bbdb-vcard.el
@@ -0,0 +1,1660 @@
+;;; Tests for bbdb-vcard.el
+;;
+;; Before proceeding, you should probably save your production bbdb file.
+;;
+;; To run the tests, eval this file.
+;; In case of failure, find test results in buffer `bbdb-vcard-test-result'.
+;;
+;; For the sake of minimality, not all test cases are rfc compliant.
+
+
+(require 'bbdb-vcard)
+
+(defun bbdb-vcard-import-test
+  (vcard bbdb-entry search-name
+         &optional search-company search-net check-creation-date-p)
+  "Import VCARD and search for it in bbdb by SEARCH-NAME,
+SEARCH-COMPANY, (perhaps later) SEARCH-NET.  If search result
+disagrees with BBDB-ENTRY, talk about it in buffer
+bbdb-vcard-test-result. timestamp and, if CHECK-CREATION-DATE-P is
+nil, creation-date are not taken into account."
+  (bbdb-vcard-iterate-vcards 'bbdb-vcard-import-vcard vcard)
+  (let* ((search-company (or search-company ""))
+         (bbdb-search-result
+          (car (bbdb-search (bbdb-search (bbdb-records) search-name)
+                            nil search-company))))
+    (setf (cdr (assoc 'timestamp (elt bbdb-search-result 7))) "2010-03-04"
+          (cdr (assoc 'timestamp (elt bbdb-entry 7))) "2010-03-04")
+    (unless check-creation-date-p
+      (setf (cdr (assoc 'creation-date (elt bbdb-search-result 7))) "2010-03-04"
+            (cdr (assoc 'creation-date (elt bbdb-entry 7))) "2010-03-04"))
+    (unless (equal (subseq bbdb-search-result 0 8)
+                   (subseq bbdb-entry 0 8))
+      (princ "\nTest failed:\n" (get-buffer-create "bbdb-vcard-test-result"))
+      (prin1 vcard (get-buffer-create "bbdb-vcard-test-result"))
+      (princ "\nwas stored as\n" (get-buffer-create "bbdb-vcard-test-result"))
+      (prin1 (subseq bbdb-search-result 0 8)
+             (get-buffer-create "bbdb-vcard-test-result"))
+      (princ "\nbut was expected as\n" (get-buffer-create "bbdb-vcard-test-result"))
+      (prin1 bbdb-entry (get-buffer-create "bbdb-vcard-test-result")))))
+
+(defun bbdb-vcard-normalize-notes (notes)
+  "Sort a BBDB NOTES field and delete the timestamps in order to make them
+comparable after re-import."
+  (let ((notes (remove-alist 'notes 'timestamp)))
+    (setq notes (remove-alist 'notes 'creation-date))
+    (sort
+     notes
+     '(lambda (x y) (if (string= (symbol-name (car x)) (symbol-name (car y)))
+                        (string< (cdr x) (cdr y))
+                      (string< (symbol-name (car x)) (symbol-name (car y))))))))
+
+(defun bbdb-vcard-normalize-record (record)
+  "Make BBDB RECORD comparable by deleting certain things and sorting others."
+  (setf (elt record 6) (bbdb-vcard-normalize-notes (elt record 7)))
+  (subseq record 0 7))
+
+(defun bbdb-vcard-compare-bbdbs (first-bbdb second-bbdb)
+  "Compare two BBDB record lists. Tell about mismatches in buffer
+`bbdb-vcard-test-result'."
+  (let ((i 0)
+        first-record second-record)
+    (while (or (nth i first-bbdb) (nth i second-bbdb))
+      (unless (equal (bbdb-vcard-normalize-record (nth i first-bbdb))
+                     (bbdb-vcard-normalize-record (nth i second-bbdb)))
+        (princ "\nRe-import: comparison of these records failed:"
+               (get-buffer-create "bbdb-vcard-test-result"))
+        (print (bbdb-vcard-normalize-record (nth i first-bbdb))
+               (get-buffer-create "bbdb-vcard-test-result"))
+        (prin1 (bbdb-vcard-normalize-record (nth i second-bbdb))
+               (get-buffer-create "bbdb-vcard-test-result")))
+      (incf i))))
+
+
+;;; Try not to mess up our real BBDB:
+(when bbdb-buffer
+  (save-buffer bbdb-buffer)
+  (kill-buffer bbdb-buffer))
+(when (get-buffer "test-bbdb") (kill-buffer "test-bbdb"))
+(setq bbdb-file "/tmp/test-bbdb")
+(when (file-exists-p bbdb-file) (delete-file bbdb-file))
+(when (get-buffer "bbdb-vcard-test-result") (kill-buffer "bbdb-vcard-test-result"))
+
+
+;;;; The Import Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bbdb-vcard-import-test
+ "
+** A vcard without any type parameters.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:First1 Last1
+N:Last1;First1
+NICKNAME:Firsty1
+PHOTO:The Alphabet:
+ abcdefghijklmnop
+ qrstuvwsyz
+BDAY:1999-12-05
+ADR:Box111;Room 111;First Street,First Corner;Cityone;First State;11111;Country
+LABEL:Label 1
+TEL:+11111111
+EMAIL:first1@provider1
+MAILER:Wanderlust1
+TZ:+01:00
+GEO:37.386013;-122.082932
+TITLE:Director\\, Research and Development
+ROLE:Programmer
+LOGO:encoded logo #1
+AGENT:CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
+ORG:Company1;Unit1;Subunit1
+CATEGORIES:category1
+NOTE:This vcard uses every type defined in rfc2426.
+PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN
+REV:1995-10-31T22:27:10Z
+SORT-STRING:aaa000
+SOUND:Audible1
+UID:111-111-111-111
+URL:http://first1.host1.org
+CLASS:CONFIDENTIAL
+KEY:The Key No 1
+X-foo:extended type 1
+END:VCARD
+"
+ ["First1" "Last1"
+  ("Firsty1")
+  "Company1
+Unit1
+Subunit1"
+  (["Office" "+11111111"])
+  (["Office"
+    ("Box111" "Room 111" "First Street" "First Corner")
+    "Cityone"
+    "First State"
+    "11111"
+    "Country"])
+  ("first1@provider1")
+  ((x-foo . "extended type 1")
+   (key . "The Key No 1")
+   (class . "CONFIDENTIAL")
+   (uid . "111-111-111-111")
+   (sound . "Audible1")
+   (sort-string . "aaa000")
+   (prodid . "-//ONLINE DIRECTORY//NONSGML Version 1//EN")
+   (agent . "CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com")
+   (logo . "encoded logo #1")
+   (role . "Programmer")
+   (title . "Director, Research and Development")
+   (geo . "37.386013;-122.082932")
+   (tz . "+01:00")
+   (mailer . "Wanderlust1")
+   (label . "Label 1")
+   (photo . "The Alphabet:abcdefghijklmnopqrstuvwsyz")
+   (mail-alias . "category1")
+   (anniversary . "1999-12-05 birthday")
+   (notes . "This vcard uses every type defined in rfc2426.")
+   (www . "http://first1.host1.org")
+   (creation-date . "1995-10-31") (timestamp . "2010-03-04"))]
+ "First1 Last1"
+ nil nil t)
+
+
+(bbdb-vcard-import-test
+ "
+** Bad vCard: semi-colons where they don't belong
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:First2; Last2
+N:Last2;First2
+NICKNAME:Firsty2,or; something
+PHOTO:The Alphabet:
+ abcdefghij;klmnop
+ qrstuvwsyz
+BDAY:1999-12-05
+ADR:Box111;Room 111;First Street,First Corner;Cityone;First State;11111;Country
+LABEL:Label 1;Label 2
+TEL:+11111111;+222222
+EMAIL:first1@provider1
+MAILER:Wanderlust1;Wanderlust2
+TZ:+01:00;Here
+GEO:37.386013;-122.082932
+TITLE:Director\\, Research; and Development
+ROLE:Programmer
+LOGO:encoded logo #1
+AGENT:CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
+ORG:Company1;Unit1;Subunit1
+CATEGORIES:category1
+NOTE:This isn't a decent vCard. It shouldn't render our bbdb unusable. We don't expect it to re-import unchanged, though.
+REV:1995-10-31T22:27:10Z
+SORT-STRING:aaa000
+SOUND:Audible1
+UID:111-111-111-111
+URL:http://first1.host1.org; My home
+CLASS:CONFIDENTIAL
+KEY:The Key No 1
+X-foo:extended type 1
+END:VCARD
+"
+ ["First2" "Last2"
+  ("First2; Last2" "Firsty2" "or; something")
+  "Company1
+Unit1
+Subunit1"
+  (["Office" "+11111111;+222222"])
+  (["Office" ("Box111" "Room 111" "First Street" "First Corner") "Cityone" "First State" "11111" "Country"])
+  ("first1@provider1")
+  ((x-foo . "extended type 1")
+   (key . "The Key No 1")
+   (class . "CONFIDENTIAL")
+   (uid . "111-111-111-111")
+   (sound . "Audible1")
+   (sort-string . "aaa000")
+   (agent . "CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com")
+   (logo . "encoded logo #1")
+   (role . "Programmer")
+   (title . "Director, Research; and Development")
+   (geo . "37.386013;-122.082932")
+   (tz . "+01:00;Here")
+   (mailer . "Wanderlust1;Wanderlust2")
+   (label . "Label 1;Label 2")
+   (photo . "The Alphabet:abcdefghij;klmnopqrstuvwsyz")
+   (mail-alias . "category1")
+   (anniversary . "1999-12-05 birthday")
+   (notes . "This isn't a decent vCard. It shouldn't render our bbdb unusable. We don't expect it to re-import unchanged, though.")
+   (www . "http://first1.host1.org; My home")
+   (creation-date . "1995-10-31") (timestamp . "2010-03-04"))]
+  "First2 Last2"
+ nil nil t)
+
+
+(bbdb-vcard-import-test
+ "
+** The following is made of examples from rfc2426.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:Mr. John Q. Public\\, Esq.
+N:Stevenson;John;Philip,Paul;Dr.;Jr.,M.D.,A.C.P.
+NICKNAME:Robbie
+PHOTO;VALUE=uri:http://www.abc.com/pub/photos
+ /jqpublic.gif
+BDAY:1996-04-15
+ADR;TYPE=dom,home,postal,parcel:;;123 Main
+  Street;Any Town;CA;91921-1234
+LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\\, Esq.\\n
+ Mail Drop: TNE QB\\n123 Main Street\\nAny Town\\, CA  91921-1234
+ \\nU.S.A.
+TEL;TYPE=work,voice,pref,msg:+1-213-555-1234
+EMAIL;TYPE=internet:jqpublic@xyz.dom1.com
+EMAIL;TYPE=internet:jdoe@isp.net
+MAILER:PigeonMail 2.1
+TZ:-05:00
+GEO:37.386013;-122.082932
+TITLE:Director\\, Research and Development
+ROLE:Programmer
+LOGO;ENCODING=b;TYPE=JPEG:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN
+ AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm
+ ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0
+AGENT;VALUE=uri:
+ CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
+ORG:ABC\\, Inc.;North American Division;Marketing
+CATEGORIES:TRAVEL AGENT
+NOTE:This fax number is operational 0800 to 1715
+  EST\\, Mon-Fri.
+PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN
+REV:1995-10-31T22:27:10Z
+SOUND;TYPE=BASIC;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN
+ AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm
+ ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0
+UID:19950401-080045-40000F192713-0052
+URL:http://www.swbyps.restaurant.french/~chezchic.html
+CLASS:PUBLIC
+KEY;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQA
+ wdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYX
+ Rpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0
+ ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNj
+ E5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYD
+ VQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAx
+ MPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRz
+ Y2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQ
+ EBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2
+ dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MB
+ EGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau
+ +hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIP
+ mx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//y
+ rZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7
+ UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==
+END:VCARD
+"
+ ["Dr. John Philip Paul" "Stevenson Jr. M.D. A.C.P."
+  ("Mr. John Q. Public, Esq." "Robbie")
+  "ABC, Inc.
+North American Division
+Marketing"
+  (["Office" "+1-213-555-1234"])
+  (["Home"
+    ("123 Main Street")
+    "Any Town"
+    "CA"
+    "91921-1234"
+    ""])
+  ("jqpublic@xyz.dom1.com" "jdoe@isp.net")
+  ((key\;encoding=b . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNjE5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYDVQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAxMPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRzY2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MBEGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau+hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIPmx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//yrZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==")
+   (class . "PUBLIC")
+   (uid . "19950401-080045-40000F192713-0052")
+   (sound\;type=basic\;encoding=b . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0")
+   (prodid . "-//ONLINE DIRECTORY//NONSGML Version 1//EN")
+   (agent\;value=uri . "CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com")
+   (logo\;encoding=b\;type=jpeg . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0")
+   (role . "Programmer")
+   (title . "Director, Research and Development")
+   (geo . "37.386013;-122.082932")
+   (tz . "-05:00")
+   (mailer . "PigeonMail 2.1")
+   (label\;type=dom\,home\,postal\,parcel . "Mr.John Q. Public, Esq.
+Mail Drop: TNE QB
+123 Main Street
+Any Town, CA  91921-1234
+U.S.A.")
+   (photo\;value=uri . "http://www.abc.com/pub/photos/jqpublic.gif")
+   (mail-alias . "TRAVEL AGENT")
+   (anniversary . "1996-04-15 birthday")
+   (notes . "This fax number is operational 0800 to 1715 EST, Mon-Fri.")
+   (www . "http://www.swbyps.restaurant.french/~chezchic.html")
+   (creation-date . "1995-10-31") (timestamp . "2010-03-04"))]
+ "John"
+ nil nil t)
+
+
+(bbdb-vcard-import-test
+ "
+** Exactly the same as before.
+   Re-reading it shouldn't duplicate anything.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:Mr. John Q. Public\\, Esq.
+N:Stevenson;John;Philip,Paul;Dr.;Jr.,M.D.,A.C.P.
+NICKNAME:Robbie
+PHOTO;VALUE=uri:http://www.abc.com/pub/photos
+ /jqpublic.gif
+BDAY:1996-04-15
+ADR;TYPE=dom,home,postal,parcel:;;123 Main
+  Street;Any Town;CA;91921-1234
+LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\\, Esq.\\n
+ Mail Drop: TNE QB\\n123 Main Street\\nAny Town\\, CA  91921-1234
+ \\nU.S.A.
+TEL;TYPE=work,voice,pref,msg:+1-213-555-1234
+EMAIL;TYPE=internet:jqpublic@xyz.dom1.com
+EMAIL;TYPE=internet:jdoe@isp.net
+MAILER:PigeonMail 2.1
+TZ:-05:00
+GEO:37.386013;-122.082932
+TITLE:Director\\, Research and Development
+ROLE:Programmer
+LOGO;ENCODING=b;TYPE=JPEG:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN
+ AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm
+ ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0
+AGENT;VALUE=uri:
+ CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
+ORG:ABC\\, Inc.;North American Division;Marketing
+CATEGORIES:TRAVEL AGENT
+NOTE:This fax number is operational 0800 to 1715
+  EST\\, Mon-Fri.
+PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN
+REV:1995-10-31T22:27:10Z
+SOUND;TYPE=BASIC;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN
+ AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm
+ ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0
+UID:19950401-080045-40000F192713-0052
+URL:http://www.swbyps.restaurant.french/~chezchic.html
+CLASS:PUBLIC
+KEY;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQA
+ wdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYX
+ Rpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0
+ ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNj
+ E5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYD
+ VQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAx
+ MPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRz
+ Y2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQ
+ EBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2
+ dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MB
+ EGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau
+ +hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIP
+ mx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//y
+ rZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7
+ UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==
+END:VCARD
+"
+["Dr. John Philip Paul" "Stevenson Jr. M.D. A.C.P."
+ ("Mr. John Q. Public, Esq." "Robbie")
+ "ABC, Inc.
+North American Division
+Marketing"
+ (["Office" "+1-213-555-1234"])
+ (["Home"
+   ("123 Main Street")
+   "Any Town"
+   "CA"
+   "91921-1234"
+   ""])
+ ("jqpublic@xyz.dom1.com" "jdoe@isp.net")
+ ((key\;encoding=b . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNjE5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYDVQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAxMPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRzY2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MBEGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau+hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIPmx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//yrZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==")
+  (class . "PUBLIC")
+  (uid . "19950401-080045-40000F192713-0052")
+  (sound\;type=basic\;encoding=b . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0")
+  (prodid . "-//ONLINE DIRECTORY//NONSGML Version 1//EN")
+  (agent\;value=uri . "CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com")
+  (logo\;encoding=b\;type=jpeg . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0")
+  (role . "Programmer")
+  (title . "Director, Research and Development")
+  (geo . "37.386013;-122.082932")
+  (tz . "-05:00")
+  (mailer . "PigeonMail 2.1")
+  (label\;type=dom\,home\,postal\,parcel . "Mr.John Q. Public, Esq.
+Mail Drop: TNE QB
+123 Main Street
+Any Town, CA  91921-1234
+U.S.A.")
+  (photo\;value=uri . "http://www.abc.com/pub/photos/jqpublic.gif")
+  (www . "http://www.swbyps.restaurant.french/~chezchic.html")
+  (mail-alias . "TRAVEL AGENT")
+  (anniversary . "1996-04-15 birthday")
+  (notes . "This fax number is operational 0800 to 1715 EST, Mon-Fri.")
+  (creation-date . "1995-10-31") (timestamp . "2010-03-04"))]
+ "John"
+ nil nil t)
+
+
+(bbdb-vcard-import-test 
+ "
+** Re-use of existing BBDB entries. 
+*** N, ORG, EMAIL
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyA;FirstA
+ORG:OrgA;UnitA
+EMAIL:userA@hostA.example.com
+END:vcard
+"
+ ["FirstA" "FamilyA"
+  nil
+  "OrgA
+UnitA"
+  nil
+  nil
+  ("userA@hostA.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstA FamilyA")
+
+
+(bbdb-vcard-import-test
+ "
+*** The same again; shouldn't change the previous one.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyA;FirstA
+ORG:OrgA;UnitA
+EMAIL:userA@hostA.example.com
+END:VCARD
+"
+ ["FirstA" "FamilyA"
+  nil
+  "OrgA
+UnitA"
+  nil
+  nil
+  ("userA@hostA.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstA FamilyA")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same N, same ORG, different EMAIL, which should be added to the previous
+    one.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyA;FirstA
+ORG:OrgA;UnitA
+EMAIL:personA@example.com
+END:VCARD
+"
+ ["FirstA" "FamilyA"
+  nil
+  "OrgA
+UnitA"
+  nil
+  nil
+  ("userA@hostA.example.com" "personA@example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstA FamilyA")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same N, same ORG, no EMAIL; shouldn't change anything.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyA;FirstA
+ORG:OrgA;UnitA
+END:VCARD
+"
+ ["FirstA" "FamilyA"
+  nil
+  "OrgA
+UnitA"
+  nil
+  nil
+  ("userA@hostA.example.com" "personA@example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstA FamilyA")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same N, same EMAIL, no ORG; shouldn't change anything.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyA;FirstA
+EMAIL:userA@hostA.example.com
+END:VCARD
+"
+ ["FirstA" "FamilyA"
+  nil
+  "OrgA
+UnitA"
+  nil
+  nil
+  ("userA@hostA.example.com" "personA@example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstA FamilyA")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same N, same EMAIL, different ORG by which Company of the previous one
+    should be replaced.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyA;FirstA
+ORG:OrgA;UnitB
+EMAIL:userA@hostA.example.com
+END:VCARD
+"
+ ["FirstA" "FamilyA"
+  nil
+  "OrgA
+UnitB"
+  nil
+  nil
+  ("userA@hostA.example.com" "personA@example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstA FamilyA")
+
+
+(bbdb-vcard-import-test
+ "
+*** Different N, same EMAIL, same ORG; should go into a fresh entry.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyA1;FirstA1
+ORG:OrgA;UnitB
+EMAIL:userA@hostA.example.com
+END:VCARD
+"
+ ["FirstA1" "FamilyA1"
+  nil
+  "OrgA
+UnitB"
+  nil
+  nil
+  ("userA@hostA.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstA1 FamilyA1")
+
+
+
+(bbdb-vcard-import-test
+ "
+** AKA has various sources; duplicates are being discarded.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyB;FirstB
+A.N:PseudonymB;FirstB
+FN:The FirstB of FamilyB
+A.FN:FirstB1 FamilyB1
+B.FN:FirstB2 FamilyB2
+C.FN:FirstB FamilyB
+NICKNAME:Bee,Effy Bee,FirstB FamilyB
+END:VCARD
+"
+ ["FirstB" "FamilyB"
+  ("FirstB2 FamilyB2"
+   "FirstB1 FamilyB1"
+   "The FirstB of FamilyB"
+   "FirstB PseudonymB"
+   "Bee"
+   "Effy Bee")
+  nil
+  nil
+  nil
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstB FamilyB")
+
+
+(bbdb-vcard-import-test
+ "
+** Additional ORGs go to Notes, org.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyC;FirstC
+ORG:OrgC1
+ORG:OrgC2
+END:vcard
+"
+ ["FirstC" "FamilyC"
+  nil
+  "OrgC1"
+  nil
+  nil
+  nil
+  ((org . "OrgC2")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstC FamilyC")
+
+
+(bbdb-vcard-import-test
+ "
+*** ... but only if they are unique
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyC;FirstC
+ORG:OrgC1
+ORG:OrgC2
+ORG:OrgC3
+ORG:OrgC3
+ORG:OrgC4
+END:VCARD
+"
+ ["FirstC" "FamilyC"
+  nil
+  "OrgC1"
+  nil
+  nil
+  nil
+  ((org . "OrgC4")
+   (org . "OrgC3")
+   (org . "OrgC2")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstC FamilyC")
+
+
+(bbdb-vcard-import-test
+ "
+** Prefixes are discarded.
+------------------------------------------------------------
+X.BEGIN:VCARD
+X.VERSION:3.0
+X.N:FamilyD;FirstD
+X.ORG:OrgD;UnitD
+X.EMAIL:userD@hostD.example.com
+X.END:VCARD
+"
+ ["FirstD" "FamilyD"
+  nil
+  "OrgD
+UnitD"
+  nil
+  nil
+  ("userD@hostD.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstD FamilyD")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same as before, don't change anything.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyD;FirstD
+ORG:OrgD;UnitD
+EMAIL:userD@hostD.example.com
+END:VCARD
+"
+ ["FirstD" "FamilyD"
+  nil
+  "OrgD
+UnitD"
+  nil
+  nil
+  ("userD@hostD.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstD FamilyD")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same as before, don't change anything.
+------------------------------------------------------------
+Y.BEGIN:VCARD
+Y.VERSION:3.0
+Y.N:FamilyD;FirstD
+Y.ORG:OrgD;UnitD
+Y.EMAIL:userD@hostD.example.com
+Y.END:VCARD
+"
+ ["FirstD" "FamilyD"
+  nil
+  "OrgD
+UnitD"
+  nil
+  nil
+  ("userD@hostD.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstD FamilyD")
+
+
+(bbdb-vcard-import-test
+ "
+** Case Insensitivity
+------------------------------------------------------------
+BEGIN:Vcard
+Version:3.0
+n:FamilyE;FirstE
+Org:OrgE;UnitE
+email:userE@hostE.example.com
+end:vcard
+"
+ ["FirstE" "FamilyE"
+  nil
+  "OrgE
+UnitE"
+  nil
+  nil
+  ("userE@hostE.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstE FamilyE")
+
+
+(bbdb-vcard-import-test
+ "
+** Non-ASCII Content
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:Franz Rübezahl
+N:Rübezahl;Franz
+NICKNAME:Fränzchen,Rübe
+ADR:Postschließfach 17;Zimmer Zwölf;Einbahnstraße;Ödstadt;;75480;
+ORG:Rübe AG
+END:VCARD
+"
+ ["Franz" "Rübezahl"
+  ("Fränzchen" "Rübe")
+  "Rübe AG"
+  nil
+  (["Office"
+    ("Postschließfach 17" "Zimmer Zwölf" "Einbahnstraße")
+    "Ödstadt"
+    ""
+    "75480"
+    ""])
+  nil
+  ((creation-date . "2010-03-06") (timestamp . "2010-03-06")) ]
+ "Rübe")
+
+
+(bbdb-vcard-import-test
+ "
+*** Multiple, structured ADR
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyF;FirstF
+ORG:OrgF;UnitF
+ADR;TYPE=dom,home,postal,parcel:Box111,LHS;Room 111,or not;First Street,First Corner;Cityone;First State;11111,22222;Country
+ADR;TYPE=intl,work,postal,parcel:Box222,RHS;Room 22,or something;Second Street,First Corner;Citytwo;Second State;222,33333;Country
+ADR;TYPE=dom,work,postal,parcel:;;Second Street,First Corner;Citytwo;;222,33333;
+ADR;TYPE=intl;TYPE=home;TYPE=parcel:;;Third Street,First Corner;Citythree;;222,33333;
+END:VCARD
+"
+ ["FirstF" "FamilyF"
+  nil
+  "OrgF
+UnitF"
+  nil
+  (["Home"
+    ("Box111" "LHS" "Room 111" "or not" "First Street" "First Corner")
+    "Cityone"
+    "First State"
+    "11111\n22222"
+    "Country"]
+   ["Office"
+    ("Box222" "RHS" "Room 22" "or something" "Second Street" "First Corner")
+    "Citytwo"
+    "Second State"
+    "222\n33333"
+    "Country"]
+   ["Office"
+    ("Second Street" "First Corner")
+    "Citytwo"
+    ""
+    "222\n33333"
+    ""]
+   ["Home"
+    ("Third Street" "First Corner")
+    "Citythree"
+    ""
+    "222\n33333"
+    ""])
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstF FamilyF")
+
+
+(bbdb-vcard-import-test
+ "
+*** Skip types from bbdb-vcard-skip-on-import
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyH;FirstH
+ORG:OrgH;UnitH
+EMAIL:userH@hostH.example.com
+X-GSM-FOO:Blah
+X-GSM-BAR:Blahblah
+END:VCARD
+"
+ ["FirstH" "FamilyH"
+  nil
+  "OrgH
+UnitH"
+  nil
+  nil
+  ("userH@hostH.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstH FamilyH")
+
+
+(bbdb-vcard-import-test
+ "
+*** Skip empty types.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyG;FirstG
+ORG:OrgG;UnitG
+EMAIL:userG@hostG.example.com
+ROLE:
+TITLE:
+GEO:
+END:VCARD
+"
+ ["FirstG" "FamilyG"
+  nil
+  "OrgG
+UnitG"
+  nil
+  nil
+  ("userG@hostG.example.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstG FamilyG")
+
+
+(bbdb-vcard-import-test
+ "
+*** Remove X-BBDB- prefixes
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyN;FirstN
+ORG:OrgN;UnitN
+EMAIL:userN@hostN.example.com
+X-BBDB-MARK-CHAR:b
+X-BBDB-TEX-NAME:{\\\\em FirstM FamilyM}
+END:VCARD
+"
+ ["FirstN" "FamilyN"
+  nil
+  "OrgN
+UnitN"
+  nil
+  nil
+  ("userN@hostN.example.com")
+  ((tex-name . "{\\em FirstM FamilyM}")
+   (mark-char . "b")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstN FamilyN")
+
+
+(bbdb-vcard-import-test
+ "
+** Merging of vcard NOTEs
+*** A vcard with two NOTEs.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyI;FirstI
+ORG:OrgI
+NOTE:Note No. 1a
+NOTE:Note No. 1b
+END:VCARD
+"
+ ["FirstI" "FamilyI"
+  nil
+  "OrgI"
+  nil
+  nil
+  nil
+  ((notes . "Note No. 1a;\nNote No. 1b")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstI FamilyI")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same as before, but a different NOTE.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyI;FirstI
+ORG:OrgI
+NOTE:Note No. 2
+END:VCARD
+"
+ ["FirstI" "FamilyI"
+  nil
+  "OrgI"
+  nil
+  nil
+  nil
+  ((notes . "Note No. 1a;\nNote No. 1b;\nNote No. 2")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstI FamilyI")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same as before, but a NOTE we've seen already
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyI;FirstI
+ORG:OrgI
+NOTE:Note No. 1b
+END:VCARD
+"
+ ["FirstI" "FamilyI"
+  nil
+  "OrgI"
+  nil
+  nil
+  nil
+  ((notes . "Note No. 1a;\nNote No. 1b;\nNote No. 2")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstI FamilyI")
+
+
+
+(bbdb-vcard-import-test
+ "
+** Merging of vcard CATEGORIES
+*** A vcard with two CATEGORIES.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyM;FirstM
+ORG:OrgI
+CATEGORIES:Category 1a,Category 1b
+CATEGORIES:Category 2a,Category 2b
+END:VCARD
+"
+ ["FirstM" "FamilyM"
+  nil
+  "OrgI"
+  nil
+  nil
+  nil
+  ((mail-alias . "Category 1a,Category 1b,Category 2a,Category 2b")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstM FamilyM")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same as before, but a different CATEGORIES.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyM;FirstM
+ORG:OrgI
+CATEGORIES:Category 3
+END:VCARD
+"
+ ["FirstM" "FamilyM"
+  nil
+  "OrgI"
+  nil
+  nil
+  nil
+  ((mail-alias . "Category 1a,Category 1b,Category 2a,Category 2b,Category 3")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstM FamilyM")
+
+
+(bbdb-vcard-import-test
+ "
+*** Same as before, but a CATEGORIES we've seen already
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyM;FirstM
+ORG:OrgI
+CATEGORIES:Category 2b
+END:VCARD
+"
+ ["FirstM" "FamilyM"
+  nil
+  "OrgI"
+  nil
+  nil
+  nil
+  ((mail-alias . "Category 1a,Category 1b,Category 2a,Category 2b,Category 3")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstM FamilyM")
+
+
+(bbdb-vcard-import-test
+ "
+** A vcard with two other vcards inside; we check the outer one
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:OuterfirstA OuterlastA
+N:OuterlastA OuterfirstA
+AGENT:BEGIN:VCARD\\nVERSION:3.0\\nN:InnerlastA\\;InnerfirstA\\nFN:InnerfirstA InnerlastA\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerA@hostA.com\\nEND:VCARD\\n
+B.AGENT:BEGIN:VCARD\\nVERSION:3.0\\nN:InnerlastB\\;InnerfirstB\\nFN:InnerfirstB InnerlastB\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerB@hostB.com\\nEND:VCARD\\n
+NOTE:A note
+END:VCARD
+"
+ ["OuterlastA" "OuterfirstA"
+  ("OuterfirstA OuterlastA")
+  nil
+  nil
+  nil
+  nil
+  ((b\.agent . "BEGIN:VCARD
+VERSION:3.0
+N:InnerlastB;InnerfirstB
+FN:InnerfirstB InnerlastB
+TEL:+1-919-555-1234
+EMAIL;TYPE=INTERNET:InnerB@hostB.com
+END:VCARD
+")
+   (agent . "BEGIN:VCARD
+VERSION:3.0
+N:InnerlastA;InnerfirstA
+FN:InnerfirstA InnerlastA
+TEL:+1-919-555-1234
+EMAIL;TYPE=INTERNET:InnerA@hostA.com
+END:VCARD
+")
+   (notes . "A note")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "OuterfirstA OuterlastA")
+
+
+(bbdb-vcard-import-test
+ "
+** A vcard with two other vcards inside; we check the first inner one
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:OuterfirstA OuterlastA
+N:OuterlastA OuterfirstA
+AGENT:BEGIN:VCARD\\nVERSION:3.0\\nN:InnerlastA\\;InnerfirstA\\nFN:InnerfirstA InnerlastA\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerA@hostA.com\\nEND:VCARD\\n
+B.AGENT:BEGIN:VCARD\\nVERSION:3.0\\nN:InnerlastB\\;InnerfirstB\\nFN:InnerfirstB InnerlastB\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerB@hostB.com\\nEND:VCARD\\n
+NOTE:A note
+END:VCARD
+"
+ ["InnerfirstA" "InnerlastA"
+  nil
+  nil
+  (["Office" "+1-919-555-1234"])
+  nil
+  ("InnerA@hostA.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "InnerfirstA InnerlastA")
+
+
+(bbdb-vcard-import-test
+ "
+** A vcard with two other vcards inside; we check the second inner one
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+FN:OuterfirstA OuterlastA
+N:OuterlastA OuterfirstA
+AGENT:BEGIN:VCARD\\nVERSION:3.0\\nN:InnerlastA\\;InnerfirstA\\nFN:InnerfirstA InnerlastA\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerA@hostA.com\\nEND:VCARD\\n
+B.AGENT:BEGIN:VCARD\\nVERSION:3.0\\nN:InnerlastB\\;InnerfirstB\\nFN:InnerfirstB InnerlastB\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerB@hostB.com\\nEND:VCARD\\n
+NOTE:A note
+END:VCARD
+"
+ ["InnerfirstB" "InnerlastB"
+  nil
+  nil
+  (["Office" "+1-919-555-1234"])
+  nil
+  ("InnerB@hostB.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "InnerfirstB InnerlastB")
+
+
+(bbdb-vcard-import-test
+ "
+** Treatment of REV
+*** Store REV as creation-date in new records...
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyJ;FirstJ
+ORG:OrgJ
+REV:1997-03-27T22:27:10Z
+END:VCARD
+"
+ ["FirstJ" "FamilyJ"
+  nil
+  "OrgJ"
+  nil
+  nil
+  nil
+  ((creation-date . "1997-03-27") (timestamp . "2010-03-04")) ]
+ "FirstJ FamilyJ"
+ nil nil t)
+
+
+(bbdb-vcard-import-test
+ "
+*** ...but not in existing records
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyJ;FirstJ
+ORG:OrgJ
+REV:1977-12-03T22:27:10Z
+END:VCARD
+"
+ ["FirstJ" "FamilyJ"
+  nil
+  "OrgJ"
+  nil
+  nil
+  nil
+  ((creation-date . "1997-03-27") (timestamp . "2010-03-04")) ]
+ "FirstJ FamilyJ"
+ nil nil t)
+
+
+
+(bbdb-vcard-import-test
+ "
+** Matching BDAY and N induce merge
+*** Storing a new person
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyK;FirstK
+ORG:CompanyK
+BDAY:1927-03-27
+END:VCARD
+"
+ ["FirstK" "FamilyK"
+  nil
+  "CompanyK"
+  nil
+  nil
+  nil
+  ((anniversary . "1927-03-27 birthday")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstK FamilyK")
+
+
+(bbdb-vcard-import-test
+ "
+*** Not quite the same person: BDAY differs.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyK;FirstK
+ORG:CompanyK2
+BDAY:1937-04-28
+END:VCARD
+"
+ ["FirstK" "FamilyK"
+  nil
+  "CompanyK2"
+  nil
+  nil
+  nil
+  ((anniversary . "1937-04-28 birthday")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstK FamilyK"
+ "CompanyK2")
+
+
+(bbdb-vcard-import-test
+ "
+*** Known person due to matching BDAY. Different ORG, though.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyK;FirstK
+ORG:CompanyK1
+BDAY:1927-03-27
+END:VCARD
+"
+ ["FirstK" "FamilyK"
+  nil
+  "CompanyK1"
+  nil
+  nil
+  nil
+  ((anniversary . "1927-03-27 birthday")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04")) ]
+ "FirstK FamilyK"
+ "CompanyK1")
+
+
+
+(bbdb-vcard-import-test
+ "
+*** Anniversaries
+** Non-birthday anniversaries
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyM;FirstM
+BDAY:1927-03-27
+X-BBDB-ANNIVERSARY:1960-12-12 wedding\\n1970-11-11 blah\\n1998-03-12 %s created bbdb-anniv.el %d years ago
+END:VCARD
+"
+ ["FirstM" "FamilyM"
+  nil
+  nil
+  nil
+  nil
+  nil
+  ((anniversary . "1927-03-27 birthday\n1960-12-12 wedding\n1970-11-11 blah\n1998-03-12 %s created bbdb-anniv.el %d years ago")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstM FamilyM")
+
+
+(bbdb-vcard-import-test
+ "
+** Non-birthday anniversaries, no BDAY
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyN;FirstN
+X-BBDB-ANNIVERSARY:1960-12-12 wedding\\n1970-11-11 blah
+END:VCARD
+"
+ ["FirstN" "FamilyN"
+  nil
+  nil
+  nil
+  nil
+  nil
+  ((anniversary . "1960-12-12 wedding\n1970-11-11 blah")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstN FamilyN")
+
+
+
+(bbdb-vcard-import-test
+ "
+** No BDAY, but unlabelled birthday in anniversary
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyO;FirstO
+X-BBDB-ANNIVERSARY:1960-12-12\\n1970-11-11 blah
+NOTE:On re-import, birthday gets labelled.
+   Therefore, re-import test of this one should fail.
+END:VCARD
+"
+ ["FirstO" "FamilyO"
+  nil
+  nil
+  nil
+  nil
+  nil
+  ((anniversary . "1960-12-12\n1970-11-11 blah")
+   (notes . "On re-import, birthday gets labelled.  Therefore, re-import test of this one should fail.")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstO FamilyO")
+
+
+
+(bbdb-vcard-import-test
+ "
+** Matching TEL and N induce merge
+*** Storing a new person
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyL;FirstL
+TEL;TYPE=work:111100001
+TEL;TYPE=home:111100002
+TEL:111100003
+ORG:CompanyL
+END:VCARD
+"
+ ["FirstL" "FamilyL"
+  nil
+  "CompanyL"
+  (["Office" "111100001"]
+   ["Home" "111100002"]
+   ["Office" "111100003"]
+)
+  nil
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstL FamilyL")
+
+
+(bbdb-vcard-import-test
+ "
+*** Not quite the same person: no matching TEL.
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyL;FirstL
+TEL;TYPE=work:222200001
+TEL;TYPE=home:222200002
+TEL:222200003
+ORG:CompanyL2
+END:VCARD
+"
+ ["FirstL" "FamilyL"
+  nil
+  "CompanyL2"
+  (["Office" "222200001"]
+   ["Home" "222200002"]
+   ["Office" "222200003"])
+  nil
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstL FamilyL"
+ "CompanyL2")
+
+
+(bbdb-vcard-import-test
+ "
+*** Known person: matching TEL (but different ORG).
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:3.0
+N:FamilyL;FirstL
+TEL;TYPE=work:333300001
+TEL;TYPE=work:111100002
+TEL:333300003
+ORG:CompanyL3
+END:VCARD
+"
+ ["FirstL" "FamilyL"
+  nil
+  "CompanyL3"
+   (["Office" "111100003"]
+    ["Home" "111100002"]
+    ["Office" "111100001"]
+    ["Office" "333300001"]
+    ["Office" "111100002"]
+    ["Office" "333300003"])
+  nil
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "FirstL FamilyL"
+ "CompanyL3")
+
+
+
+(bbdb-vcard-import-test
+ "
+** From RFC 2426: author's address.  Note the omission or type N
+   which is declared mandatory by this very RFC.
+------------------------------------------------------------
+BEGIN:vCard
+VERSION:3.0
+FN:Frank Dawson
+ORG:Lotus Development Corporation
+ADR;TYPE=WORK,POSTAL,PARCEL:;;6544 Battleford Drive
+ ;Raleigh;NC;27613-3502;U.S.A.
+TEL;TYPE=VOICE,MSG,WORK:+1-919-676-9515
+TEL;TYPE=FAX,WORK:+1-919-676-9564
+EMAIL;TYPE=INTERNET,PREF:Frank_Dawson@Lotus.com
+EMAIL;TYPE=INTERNET:fdawson@earthlink.net
+URL:http://home.earthlink.net/~fdawson
+END:vCard
+"
+ ["" ""
+  ("Frank Dawson")
+  "Lotus Development Corporation"
+  (["Office" "+1-919-676-9515"] ["Office" "+1-919-676-9564"])
+  (["Office"
+    ("6544 Battleford Drive")
+    "Raleigh"
+    "NC"
+    "27613-3502"
+    "U.S.A."])
+  ("Frank_Dawson@Lotus.com" "fdawson@earthlink.net")
+  ((www . "http://home.earthlink.net/~fdawson")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "Frank Dawson")
+
+(bbdb-vcard-import-test
+ "
+** The other author of RFC 2426
+------------------------------------------------------------
+BEGIN:vCard
+VERSION:3.0
+FN:Tim Howes
+ORG:Netscape Communications Corp.
+ADR;TYPE=WORK:;;501 E. Middlefield Rd.;Mountain View;
+ CA; 94043;U.S.A.
+TEL;TYPE=VOICE,MSG,WORK:+1-415-937-3419
+TEL;TYPE=FAX,WORK:+1-415-528-4164
+EMAIL;TYPE=INTERNET:howes@netscape.com
+END:vCard
+"
+ ["" ""
+  ("Tim Howes")
+  "Netscape Communications Corp."
+  (["Office" "+1-415-937-3419"]
+   ["Office" "+1-415-528-4164"])
+  (["Office"
+    ("501 E. Middlefield Rd.")
+    "Mountain View"
+    "CA"
+    " 94043"
+    "U.S.A."])
+  ("howes@netscape.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "Tim Howes")
+
+
+
+(bbdb-vcard-import-test
+ "
+** vCard version 2.1
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:2.1
+N:Friday;Fred
+TEL;WORK;VOICE:+1-213-555-1234
+TEL;WORK;FAX:+1-213-555-5678
+END:VCARD
+"
+ ["Fred" "Friday"
+  nil
+  nil
+  (["Office" "+1 213 555 1234"]
+   ["Office" "+1 213 555 5678"])
+  nil
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "Fred Friday")
+
+
+(bbdb-vcard-import-test
+ "
+** vCard version 2.1
+*** Case insensitivity
+------------------------------------------------------------
+begin:VCARD
+version:2.1
+n:Thursday;Tom
+tel;WORK;VOICE:+1-213-555-1234
+tel;WORK;FAX:+1-213-555-5678
+end:VCARD
+"
+ ["Tom" "Thursday"
+  nil
+  nil
+  (["Office" "+1 213 555 1234"]
+   ["Office" "+1 213 555 5678"])
+  nil
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "Tom Thursday")
+
+
+(bbdb-vcard-import-test
+ "
+** vCard version 2.1
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:2.1
+N:Smith;John;M.;Mr.;Esq.
+TEL;WORK;VOICE;MSG:+1 (919) 555-1234
+TEL;CELL:+1 (919) 554-6758
+TEL;WORK;FAX:+1 (919) 555-9876
+ADR;WORK;PARCEL;POSTAL;DOM:Suite 101;1 Central St.;AnyTown;NC;27654
+END:VCARD
+"
+ ["Mr. John M." "Smith Esq."
+  nil
+  nil
+  (["Office" "+1 (919) 555 1234"]
+   ["Mobile" "+1 (919) 554 6758"]
+   ["Office" "+1 (919) 555 9876"])
+  (["Office" ("Suite 101" "1 Central St." "AnyTown") "NC" "27654" "" ""])
+  nil
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+  "Smith")
+
+
+(bbdb-vcard-import-test
+ "
+** vCard version 2.1
+*** Quoted-printable
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:2.1
+N:Doe;John;;;
+FN:John Doe
+ORG:Doe Company, The;
+TITLE: President
+NOTE;ENCODING=QUOTED-PRINTABLE: This is a note associated
+  with this contact=0D=0A
+TEL;WORK;VOICE:(987) 123-4567
+TEL;HOME;VOICE:(987) 765-4321
+TEL;CELL;VOICE:(987) 135-8642
+TEL;WORK;FAX:(987) 246-1357
+ADR;WORK:;;1234 North Street;Anytown;TX 751234;;United States
+  of America
+LABEL;WORK;ENCODING=QUOTED-PRINTABLE:1234 North Street=0D=0AAnytown,
+  TX 751234 =0D=0AUnited States of America
+URL:
+URL:<WWLINK TYPE=\"GENERIC\"
+ VALUE=\"http://www.doeweb.com\">http://www.doeweb.com</WWLINK>
+EMAIL;PREF;INTERNET:jdoe@nowhere.com
+REV:19980114T170559Z
+END:VCARD
+"
+ ["John" "Doe"
+  nil
+  "Doe Company, The
+"
+  (["Office" "+1 987 123 4567"]
+   ["Home" "+1 987 765 4321"]
+   ["Mobile" "+1 987 135 8642"]
+   ["Office" "+1 987 246 1357"])
+  (["Office" ("1234 North Street") "Anytown" "TX 751234" "" "United States of America"])
+  ("jdoe@nowhere.com")
+  ((label\;type=work . "1234 North Street
+Anytown, TX 751234 
+United States of America")
+   (title . "President")
+   (notes . "This is a note associated with this contact
+")
+   (www . "http://www.doeweb.com")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "John Doe")
+
+
+
+(bbdb-vcard-import-test
+ "
+** A v2.1 vcard with another vcard inside; we check the outer one
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:2.1
+N:Outerlast2A; Outerfirst2A
+FN:Outerfirst2A Outerlast2A
+AGENT:BEGIN:VCARD\\nVERSION:2.1\\nN:Innerlast2A\\;Innerfirst2A\\nFN:Innerfirst2A Innerlast2A\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerA@hostA.com\\nEND:VCARD\\n
+NOTE:A note
+END:VCARD
+"
+ [" Outerfirst2A" "Outerlast2A"
+  ("Outerfirst2A Outerlast2A")
+  nil
+  nil
+  nil
+  nil
+  ((agent . "BEGIN:VCARD\\
+VERSION:2.1\\
+N:Innerlast2A\\;Innerfirst2A\\
+FN:Innerfirst2A Innerlast2A\\
+TEL:+1-919-555-1234\\
+EMAIL\\;TYPE=INTERNET:InnerA@hostA.com\\
+END:VCARD\\
+")
+   (notes . "A note")
+   (creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "Outerfirst2A Outerlast2A")
+
+
+(bbdb-vcard-import-test
+ "
+** A v2.1 vcard with another vcard inside; we check the inner one
+------------------------------------------------------------
+BEGIN:VCARD
+VERSION:2.1
+N:Outerlast2A Outerfirst2A
+AGENT:BEGIN:VCARD\\nVERSION:2.1\\nN:Innerlast2A\\;Innerfirst2A\\nFN:Innerfirst2A Innerlast2A\\nTEL:+1-919-555-
+ 1234\\nEMAIL\\;TYPE=INTERNET:InnerA@hostA.com\\nEND:VCARD\\n
+NOTE:A note
+END:VCARD
+"
+ ["Innerfirst2A" "Innerlast2A"
+  nil
+  nil
+  (["Office" "+1 919 555 1234"])
+  nil
+  ("InnerA@hostA.com")
+  ((creation-date . "2010-03-04") (timestamp . "2010-03-04"))]
+ "Innerfirst2A Innerlast2A")
+
+
+
+;;;; The Export/Re-Import Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bbdb "" nil)
+(with-current-buffer "*BBDB*"
+  (bbdb-vcard-export "/tmp/test-bbdb-0.vcf" t nil))
+
+(let ((first-bbdb (bbdb-search (bbdb-records) ""))
+      second-bbdb)
+  (bbdb-save-db)
+  (save-buffer bbdb-buffer)
+  (kill-buffer bbdb-buffer)
+  (kill-buffer "*BBDB*")
+  (delete-file "/tmp/test-bbdb")
+  (bbdb-vcard-import-file "/tmp/test-bbdb-0.vcf")
+  (setq second-bbdb (bbdb-search (bbdb-records) ""))
+  (bbdb-vcard-compare-bbdbs first-bbdb second-bbdb))
+;; FIXME: previous line messes bbdb up.
--- /dev/null
+++ bbdb-2.36/extern/bbdb-vcard/README
@@ -0,0 +1,19 @@
+bbdb-vcard.el
+=============
+
+bbdb-vcard.el imports and exports vCards (version 3.0) as defined in
+RFC 2425 and RFC 2426 to/from The Insidious Big Brother Database
+(BBDB).  Version 2.1 vCards are converted into version 3.0 on import.
+
+On a file, a buffer or a region containing one or more vCards, use
+`bbdb-vcard-import-file', `bbdb-vcard-import-buffer', or
+`bbdb-vcard-import-region' respectively to import them into BBDB.
+
+In buffer *BBDB*, press v to export the record under point.  Press * v
+to export all records in buffer into one vCard file.  Press * C-u v to
+export them into one file each.
+
+To put one or all vCard(s) into the kill ring, press V or * V
+respectively.
+
+Refer to the Commentary in file bbdb-vcard.el for further information.
