Skip to content

Commit

Permalink
Added mime-type sniffing for untyped inline media objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Vincent Geddes committed Dec 9, 2013
1 parent b256c66 commit 49bc086
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 70 deletions.
100 changes: 64 additions & 36 deletions bbdb-vcard.el
Expand Up @@ -259,6 +259,26 @@ corresponding 'TYPE' parameter value in a vCard field. The string PREFIX and
SUFFIX are used to form a unique filename for the media object in order to
form a unique filename. MIMETYPE is currently unused.")

(defvar bbdb-vcard-mime-types
'(("audio/basic" . "basic")
("audio/wav" . "wav")
("audio/ogg" . "ogg")
("audio/mp3" . "mp3")
("audio/mpeg" . "mp3")
("audio/mp4" . "m4a")
("audio/aac" . "aac")
("image/png" . "png")
("image/jpeg" . "jpeg")
("image/gif" . "gif")
("image/tiff" . "tiff")
("image/xbm" . "xbm")
("image/xpm" . "xpm")
("application/pgp-keys" . "gpg"))
"Maps a mime-type to the name of a media descriptor")






(defun bbdb-vcard-initialize ()
Expand Down Expand Up @@ -598,10 +618,8 @@ Extend existing BBDB records where possible."
(bbdb-record-set-field
bbdb-record 'image-filename
(bbdb-vcard-import-inline-media vcard-photo)))
;; an url
((and (or (equal "uri" (cdr (assoc "value-format" vcard-photo)))
(assoc "type" vcard-photo))
(cdr (assoc "value" vcard-photo)))
;; otherwise a uri
(t
(bbdb-record-set-field
bbdb-record 'image-uri
(bbdb-vcard-unescape-strings (cdr (assoc "value" vcard-photo)))))))
Expand All @@ -613,10 +631,8 @@ Extend existing BBDB records where possible."
(bbdb-record-set-field
bbdb-record 'gpg-key-filename
(bbdb-vcard-import-inline-media vcard-key)))
;; an url
((and (or (equal "uri" (cdr (assoc "value-format" vcard-key)))
(assoc "type" vcard-key))
(cdr (assoc "value" vcard-key)))
;; otherwise a uri
(t
(bbdb-record-set-field
bbdb-record 'gpg-key-uri
(bbdb-vcard-unescape-strings (cdr (assoc "value" vcard-key)))))))
Expand All @@ -628,10 +644,8 @@ Extend existing BBDB records where possible."
(bbdb-record-set-field
bbdb-record 'sound-filename
(bbdb-vcard-import-inline-media vcard-sound)))
;; an url
((and (or (equal "uri" (cdr (assoc "value-format" vcard-sound)))
(assoc "type" vcard-sound))
(cdr (assoc "value" vcard-sound)))
;; otherwise a uri
(t
(bbdb-record-set-field
bbdb-record 'sound-uri
(bbdb-vcard-unescape-strings (cdr (assoc "value" vcard-sound)))))))
Expand Down Expand Up @@ -822,12 +836,12 @@ SPLIT-VALUE-AT-SEMI-COLON-P is non-nil, split the value at key
nil t))
(goto-char (match-end 2))
(setq parameters nil)
(setf raw-params (match-string 3))
(push (cons "value" (if split-value-at-semi-colon-p
(bbdb-vcard-split-structured-text
(match-string 4) ";")
(match-string 4)))
parameters)
(setf raw-params (match-string 3))
(setf index 0)
(when raw-params
(while (string-match "\\([^;:=]+\\)=\\([^;:]+\\)" raw-params index)
Expand Down Expand Up @@ -1125,9 +1139,8 @@ The inverse function of `bbdb-split'."
(mapconcat 'identity list separator)))

(defun bbdb-vcard-compute-media-id (data)
"Compute a representative id for a data blob. Basically a sha1sum truncated
to 16 characters."
(concat (substring (sha1 data) 0 16)))
"Compute a representative id for a data blob. Basically a sha1sum."
(sha1 data))

(defun bbdb-vcard-build-filename (descriptor data)
(expand-file-name (concat (nth 0 descriptor)
Expand All @@ -1136,32 +1149,47 @@ to 16 characters."
(concat bbdb-vcard-directory
bbdb-vcard-media-directory)))

(defun bbdb-vcard-string-chomp (string)
(if (string-match "[ \t\n]*$" string)
(replace-match "" nil nil string)
string))

(defun bbdb-vcard-sniff-mime-type (data)
(with-temp-buffer
(insert data)
(if (zerop (call-process-region 1
(+ 1 (buffer-size))
"file"
t t nil
"-b" "--mime-type" "-"))
(bbdb-vcard-string-chomp (buffer-string))
nil)))

(defun bbdb-vcard-import-inline-media (vcard-media)
"imports inline binary content and saves it to disk. `type' is valid vCard
media type, either 'sound or 'photo. `data' is the base64 encoded media content"
"imports inline binary content and saves it to disk."
(let* ((type (cdr (assoc "type" vcard-media)))
(encoding (cdr (assoc "encoding" vcard-media)))
(value (cdr (assoc "value" vcard-media)))
(data (condition-case nil
(base64-decode-string value)
(error nil)))
(mime-type (and data (bbdb-vcard-sniff-mime-type data)))
(descriptor (cadr (assoc type bbdb-vcard-media-types))))
(when (and (not descriptor) mime-type)
(setq descriptor
(cadr (assoc (cdr (assoc mime-type bbdb-vcard-mime-types))
bbdb-vcard-media-types))))
;; sanity checks
(if (and (equal encoding "b")
(and (stringp value)
(> (length value) 0))
descriptor)
(let ((data (condition-case nil
(base64-decode-string value)
(error nil))))
(if data
(let ((filename (bbdb-vcard-build-filename descriptor data))
(coding-system-for-write 'no-conversion))
(condition-case nil
(progn
(unless (file-exists-p (file-name-directory filename))
(make-directory (file-name-directory filename) t))
(write-region data nil filename)
(concat bbdb-vcard-media-directory (file-name-nondirectory filename)))
(error nil)))
nil))
(if (and (equal encoding "b") data descriptor)
(let ((filename (bbdb-vcard-build-filename descriptor data))
(coding-system-for-write 'no-conversion))
(condition-case nil
(progn
(unless (file-exists-p (file-name-directory filename))
(make-directory (file-name-directory filename) t))
(write-region data nil filename)
(concat bbdb-vcard-media-directory (file-name-nondirectory filename)))
(error nil)))
nil)))

;;;; do stuff after BBDB is loaded
Expand Down
106 changes: 72 additions & 34 deletions test-bbdb-vcard.el
Expand Up @@ -12,7 +12,7 @@
(require 'ert)


(defvar bbdb-vcard-test-fields
(defvar bbdb-vcard-test-fields
'(firstname lastname affix aka organization phone address mail xfields)
"BBDB record fields to check")

Expand All @@ -37,7 +37,7 @@

(defun bbdb-vcard-test
(vcard bbdb-entry search-name
&optional search-org search-net check-creation-date-p)
&optional search-org search-net check-export)
"Import VCARD and search for it in bbdb by SEARCH-NAME,
SEARCH-ORG, (perhaps later) SEARCH-NET. Perform assert checks."
(bbdb-vcard-iterate-vcards 'bbdb-vcard-import-vcard vcard)
Expand Down Expand Up @@ -65,17 +65,17 @@ SEARCH-ORG, (perhaps later) SEARCH-NET. Perform assert checks."
;; IMPORT/EXPORT test
;; export record as vcard and import it again
;; both records should match
(let* ((r1 bbdb-search-result)
(r2 (progn
(bbdb-save)
(delete-file (buffer-file-name bbdb-buffer))
(kill-buffer bbdb-buffer)
(with-temp-buffer
(insert (bbdb-vcard-from r1))
(message "VCARD\n%s" (bbdb-vcard-from r1))
(bbdb-vcard-import-buffer (current-buffer))
(car (bbdb-search (bbdb-records) ""))))))
(bbdb-vcard-record-equal r1 r2))))
(when check-export
(let* ((r1 bbdb-search-result)
(r2 (progn
(bbdb-save)
(delete-file (buffer-file-name bbdb-buffer))
(kill-buffer bbdb-buffer)
(with-temp-buffer
(insert (bbdb-vcard-from r1))
(bbdb-vcard-import-buffer (current-buffer))
(car (bbdb-search (bbdb-records) ""))))))
(bbdb-vcard-record-equal r1 r2)))))


(defmacro bbdb-vcard-test-fixture (body)
Expand Down Expand Up @@ -123,6 +123,46 @@ comparable after re-import."
(string< (symbol-name (car x)) (symbol-name (car y))))))))


(ert-deftest bbdb-vcard-test-sniff-mime-type ()
"Test MIME-type sniffing"
(should (equal (bbdb-vcard-sniff-mime-type "Plain Text")
"text/plain"))
(should (equal (bbdb-vcard-sniff-mime-type "")
"application/x-empty"))
(should (equal (bbdb-vcard-sniff-mime-type
(base64-decode-string "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKElEQVQoz2P8//8/AymAiYFEANWw19aWSA2M9HLSqAa6asBMAYMwpgFt4guDkeJQ1gAAAABJRU5ErkJggg=="))
"image/png")))

(ert-deftest bbdb-vcard-test-import-inline-media ()
"Test media import"
(let* ((bbdb-vcard-directory (format "/tmp/%s/" (make-temp-name "bbdb-vcard-")))
(bbdb-media-directory "media/")
(data "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKElEQVQoz2P8//8/AymAiYFEANWw19aWSA2M9HLSqAa6asBMAYMwpgFt4guDkeJQ1gAAAABJRU5ErkJggg==")
(result-filename (format "media/image-%s.png"
(sha1 (base64-decode-string data))))
(vcard-media-with-type `(("value" . ,data) ("type" . "png") ("encoding" . "b")))
(vcard-media-no-type `(("value" . ,data) ("encoding" . "b"))))
(unwind-protect
(progn
;; with type
(should (equal
(bbdb-vcard-import-inline-media vcard-media-with-type)
result-filename))
(should (file-exists-p (concat bbdb-vcard-directory result-filename)))
;; delete imported file
(condition-case nil
(delete-file (concat bbdb-vcard-directory result-filename))
(error nil))
;; no type
(should (equal
(bbdb-vcard-import-inline-media vcard-media-no-type)
result-filename))
(should (file-exists-p (concat bbdb-vcard-directory result-filename))))
(condition-case nil
(delete-directory bbdb-vcard-directory t)
(error nil)))))


;;;; The Import Tests
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Expand Down Expand Up @@ -180,10 +220,10 @@ Subunit1")
"Country"])
("first1@provider1")
((x-foo . "extended type 1")
(key . "The Key No 1")
(gpg-key-uri . "The Key No 1")
(class . "CONFIDENTIAL")
(uid . "111-111-111-111")
(sound . "Audible1")
(sound-uri . "Audible1")
(sort-string . "aaa000")
(prodid . "-//ONLINE DIRECTORY//NONSGML Version 1//EN")
(agent . "CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com")
Expand All @@ -194,7 +234,7 @@ Subunit1")
(tz . "+01:00")
(mailer . "Wanderlust1")
(label . "Label 1")
(photo . "The Alphabet:abcdefghijklmnopqrstuvwsyz")
(image-uri . "The Alphabet:abcdefghijklmnopqrstuvwsyz")
(mail-alias . "category1")
(birthday . "1999-12-05")
(notes . "This vcard uses every type defined in rfc2426.")
Expand Down Expand Up @@ -250,10 +290,10 @@ Subunit1")
(["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")
(gpg-key-uri . "The Key No 1")
(class . "CONFIDENTIAL")
(uid . "111-111-111-111")
(sound . "Audible1")
(sound-uri . "Audible1")
(sort-string . "aaa000")
(agent . "CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com")
(logo . "encoded logo #1")
Expand All @@ -263,19 +303,18 @@ Subunit1")
(tz . "+01:00;Here")
(mailer . "Wanderlust1;Wanderlust2")
(label . "Label 1;Label 2")
(photo . "The Alphabet:abcdefghij;klmnopqrstuvwsyz")
(image-uri . "The Alphabet:abcdefghij;klmnopqrstuvwsyz")
(mail-alias . "category1")
(birthday . "1999-12-05")
(notes . "This isn't a decent vCard. It shouldn't render our bbdb unusable. We don't expect it to re-import unchanged, though.")
(url . "http://first1.host1.org; My home"))]
"First2 Last2"
nil nil t)))
nil nil)))


(bbdb-vcard-import-test
"
** The following is made of examples from rfc2426.
------------------------------------------------------------
(ert-deftest bbdb-vcard-test-rfc2426 ()
"The following is made of examples from rfc2426."
(bbdb-vcard-test-fixture
(bbdb-vcard-test "
BEGIN:VCARD
VERSION:3.0
FN:Mr. John Q. Public\\, Esq.
Expand Down Expand Up @@ -331,9 +370,9 @@ KEY;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQA
UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==
END:VCARD
"
["Dr. John Philip Paul" "Stevenson Jr. M.D. A.C.P."
nil
("Mr. John Q. Public, Esq." "Robbie")
["John Philip Paul" "Stevenson"
("Dr." "Jr." "M.D." "A.C.P.")
("Robbie")
("ABC, Inc.
North American Division
Marketing")
Expand All @@ -345,10 +384,10 @@ Marketing")
"91921-1234"
""])
("jqpublic@xyz.dom1.com" "jdoe@isp.net")
((key\;encoding=b . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNjE5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYDVQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAxMPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRzY2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MBEGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau+hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIPmx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//yrZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==")
((gpg-key-filename . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0ZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNjE5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYDVQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAxMPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRzY2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2dXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MBEGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau+hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIPmx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//yrZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7UZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==")
(class . "PUBLIC")
(uid . "19950401-080045-40000F192713-0052")
(sound\;type=basic\;encoding=b . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0")
(sound-filename . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0")
(prodid . "-//ONLINE DIRECTORY//NONSGML Version 1//EN")
(agent\;value=uri . "CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com")
(logo\;encoding=b\;type=jpeg . "MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bmljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0")
Expand All @@ -362,14 +401,13 @@ 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")
(image-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-31T22:27:10Z") (timestamp . "2010-03-04"))]
(url . "http://www.swbyps.restaurant.french/~chezchic.html"))]
"John"
nil nil t)
nil nil t)))


(bbdb-vcard-import-test
Expand Down

0 comments on commit 49bc086

Please sign in to comment.