Re: converting notmuch email to 'TODO' entry in org-mode
authorPeter Feigl <craven@gmx.net>
Fri, 24 May 2013 16:37:12 +0000 (18:37 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:54:58 +0000 (09:54 -0800)
fd/fed68e49b2a757641ce5afc8974a37841995c3 [new file with mode: 0644]

diff --git a/fd/fed68e49b2a757641ce5afc8974a37841995c3 b/fd/fed68e49b2a757641ce5afc8974a37841995c3
new file mode 100644 (file)
index 0000000..9503135
--- /dev/null
@@ -0,0 +1,561 @@
+Return-Path: <craven@gmx.net>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id CCFB5431FC7\r
+       for <notmuch@notmuchmail.org>; Fri, 24 May 2013 09:37:27 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0.001\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0.001 tagged_above=-999 required=5\r
+       tests=[FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001]\r
+       autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id DeQ6fIh-PCUv for <notmuch@notmuchmail.org>;\r
+       Fri, 24 May 2013 09:37:20 -0700 (PDT)\r
+Received: from mout.gmx.net (mout.gmx.net [212.227.17.22])\r
+       by olra.theworths.org (Postfix) with ESMTP id DDF0F431FAE\r
+       for <notmuch@notmuchmail.org>; Fri, 24 May 2013 09:37:19 -0700 (PDT)\r
+Received: from mailout-de.gmx.net ([10.1.76.17]) by mrigmx.server.lan\r
+       (mrigmx002) with ESMTP (Nemesis) id 0LkDn2-1U3q9A3tW6-00cBfD for\r
+       <notmuch@notmuchmail.org>; Fri, 24 May 2013 18:37:14 +0200\r
+Received: (qmail invoked by alias); 24 May 2013 16:37:14 -0000\r
+Received: from www.nexoid.at (EHLO mail.nexoid.at) [178.79.130.240]\r
+       by mail.gmx.net (mp017) with SMTP; 24 May 2013 18:37:14 +0200\r
+X-Authenticated: #201305\r
+X-Provags-ID: V01U2FsdGVkX186+zce4as6Z48LsepOVZYzq/j7l8/jCJpKZwbjeQ\r
+       +MxNV8H4UzIfv/\r
+Received: from nexoid (localhost [127.0.0.1])\r
+       by mail.nexoid.at (Postfix) with ESMTP id C78B9E36C\r
+       for <notmuch@notmuchmail.org>; Fri, 24 May 2013 18:37:12 +0200 (CEST)\r
+From: Peter Feigl <craven@gmx.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: Re: converting notmuch email to 'TODO' entry in org-mode\r
+In-Reply-To: <87txls7f8r.fsf@krugs.de>\r
+References: <uaxbo803kqx.fsf@beesknees.cern.ch> <87txls7f8r.fsf@krugs.de>\r
+User-Agent: Notmuch/0.11+77~gad6d0d5 (http://notmuchmail.org) Emacs/24.1.50.2\r
+       (i686-pc-linux-gnu)\r
+Date: Fri, 24 May 2013 18:37:12 +0200\r
+Message-ID: <87d2sgl4g7.fsf@nexoid.at>\r
+MIME-Version: 1.0\r
+Content-Type: multipart/mixed; boundary="=-=-="\r
+X-Y-GMX-Trusted: 0\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 24 May 2013 16:37:27 -0000\r
+\r
+--=-=-=\r
+Content-Type: text/plain\r
+\r
+This is rather bad but working code I use to import meeting requests\r
+into org. I've been wanting to improve it and put it on github, but\r
+haven't gotten around to doing that yet :-/\r
+Just (require '..) both of them, then you should have an additional\r
+button for vCalendar attachments, allowing you to import it.\r
+Replying, Accepting and Rejecting are WIP, parts are there, but they\r
+don't work reliably with Microsoft Outlook yet (which doesn't really\r
+honour the standards at all :-/)\r
+\r
+Good luck with it :)\r
+\r
+Peter\r
+\r
+--=-=-=\r
+Content-Type: application/emacs-lisp\r
+Content-Disposition: attachment; filename=notmuch-ical.el\r
+Content-Transfer-Encoding: quoted-printable\r
+\r
+;; provide ical-create for inserting an ical attachment into e-mails\r
+(defun ical-create (record)\r
+  (interactive (list (ical-read-new-record)))\r
+  (let* ((start (first record))\r
+        (end (second record))\r
+        (event (third record))\r
+        (location (fourth record))\r
+        (organizer-email (cadr (fifth record)))\r
+        (organizer-name (car (fifth record)))\r
+        (attendees (sixth record))\r
+        (attendees-formatted (mapconcat (lambda (x) (format "ATTENDEE;ROLE=3DREQ-=\r
+PARTICIPANT;PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE;\r
+ CN=3D\"%s\":\r
+ MAILTO:%s" (car x) (cadr x))) attendees "\n"))\r
+        (description (seventh record))\r
+        (dtstamp (format-zulu-time (decode-time (current-time))))\r
+        (dtstart (format-zulu-time (parse-time-string start)))\r
+        (dtend (format-zulu-time (parse-time-string end)))\r
+        (uid (uuid-create))\r
+        (created dtstamp))\r
+\r
+    (mml-insert-multipart "alternative")\r
+    (mml-insert-part "text/plain")\r
+    (insert "Event: " event "\nStart: " start "\nEnd: " end "\nOrganizer: "=\r
+ organizer-name "\nAttendees: " (string-join ", " (mapcar #'car attendees))=\r
+ "\n")\r
+    (mml-insert-tag 'part 'type "text/calendar" 'disposition "inline" 'meth=\r
+od "REQUEST" 'encoding "8bit")\r
+    (insert "BEGIN:VCALENDAR\r
+METHOD:REQUEST\r
+PRODID:Emacs\r
+VERSION:2.0\r
+BEGIN:VEVENT\r
+DTSTAMP:" dtstamp "\r
+DTSTART:" dtstart "\r
+SUMMARY:" event "\r
+UID:" uid "\r
+" attendees-formatted "\r
+ORGANIZER;CN=3D\"" organizer-name "\":MAILTO:" organizer-email "\r
+LOCATION:" location "\r
+DTEND:" dtend "\r
+DESCRIPTION:" description "\r
+SEQUENCE:0\r
+PRIORITY:5\r
+CLASS:\r
+CREATED:" created "\r
+STATUS:CONFIRMED\r
+TRANSP:OPAQUE\r
+X-MICROSOFT-CDO-BUSYSTATUS:BUSY\r
+X-MICROSOFT-CDO-INSTTYPE:0\r
+X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY\r
+X-MICROSOFT-CDO-IMPORTANCE:1\r
+X-MICROSOFT-CDO-APPT-SEQUENCE:0\r
+END:VEVENT\r
+END:VCALENDAR\r
+")))\r
+\r
+(defun ical-read-new-record ()\r
+  (let* ((start (org-read-date nil nil nil "Start date (and time): "))\r
+        (end (org-read-date nil nil nil "End date (and time): "))\r
+        (event (bbdb-read-string "Event: "))\r
+        (location (bbdb-read-string "Location: "))\r
+        (from+to (message-extract-from-to-headers))\r
+        (organizer (caar from+to)) ;; todo: read from header\r
+        (attendees (cdr from+to)) ;; todo: read from header\r
+        (description (bbdb-read-string "Description: ")))\r
+    (list start end event location organizer attendees description)))\r
+\r
+(defun uuid-create ()\r
+  "Return a newly generated UUID. This uses a simple hashing of variable da=\r
+ta."\r
+  (let ((s (md5 (format "%s%s%s%s%s%s%s%s%s%s"\r
+                        (user-uid)\r
+                        (emacs-pid)\r
+                        (system-name)\r
+                        (user-full-name)\r
+                        user-mail-address\r
+                        (current-time)\r
+                        (emacs-uptime)\r
+                        (garbage-collect)\r
+                        (random)\r
+                        (recent-keys)))))\r
+    (format "%s-%s-3%s-%s-%s"\r
+            (substring s 0 8)\r
+            (substring s 8 12)\r
+            (substring s 13 16)\r
+            (substring s 16 20)\r
+            (substring s 20 32))))\r
+\r
+(defun message-extract-from-to-headers ()\r
+  (interactive)\r
+  (save-excursion\r
+    (message-narrow-to-headers)\r
+    (beginning-of-buffer)\r
+    (search-forward-regexp "^From: ")\r
+    (let ((pt (point)))\r
+      (message-next-header)\r
+      (let ((from-header (buffer-substring pt (point))))\r
+       (beginning-of-buffer)\r
+       (search-forward-regexp "^To: ")\r
+       (let ((pt (point)))\r
+         (message-next-header)\r
+         (let ((to-header (remove-from-string (buffer-substring pt (point)) "\n")=\r
+))\r
+           (widen)\r
+           (cons (mail-extract-address-components from-header t) (mail-extract-ad=\r
+dress-components to-header t))))))))\r
+\r
+;;; fix to *not* increase the day by one\r
+(defun org-ical-ts-to-string (s keyword &optional inc)\r
+  "Take a time string S and convert it to iCalendar format.\r
+KEYWORD is added in front, to make a complete line like DTSTART....\r
+When INC is non-nil, increase the hour by two (if time string contains\r
+a time), or the day by one (if it does not contain a time)."\r
+  (let ((t1 (org-parse-time-string s 'nodefault))\r
+       t2 fmt have-time time)\r
+    (if (and (car t1) (nth 1 t1) (nth 2 t1))\r
+       (setq t2 t1 have-time t)\r
+      (setq t2 (org-parse-time-string s)))\r
+    (let ((s (car t2))   (mi (nth 1 t2)) (h (nth 2 t2))\r
+         (d (nth 3 t2)) (m  (nth 4 t2)) (y (nth 5 t2)))\r
+      (when inc\r
+       (if have-time\r
+           (if org-agenda-default-appointment-duration\r
+               (setq mi (+ org-agenda-default-appointment-duration mi))\r
+             (setq h (+ 2 h)))\r
+         (setq d  d)))\r
+      (setq time (encode-time s mi h d m y)))\r
+    (setq fmt (if have-time ":%Y%m%dT%H%M%S" ";VALUE=3DDATE:%Y%m%d"))\r
+    (concat keyword (format-time-string fmt time))))\r
+\r
+;;;; **** show "import with org-ical" button in e-mails\r
+(defun notmuch-show-insert-part-text/calendar (msg part content-type nth de=\r
+pth declared-type)\r
+  (let ((button))\r
+    (setq button\r
+         (insert-button=20\r
+          (concat "[ Import with org-ical ]")\r
+          :type 'notmuch-show-import-org-ical-button-type\r
+          :notmuch-part nth\r
+          :notmuch-filename (plist-get part :filename)\r
+          :notmuch-content-type content-type))\r
+    (insert "\n")\r
+    nil))\r
+\r
+(define-button-type 'notmuch-show-import-org-ical-button-type\r
+  'action 'notmuch-show-import-org-ical-default\r
+  'keymap 'notmuch-show-import-org-ical-map\r
+  'follow-link t\r
+  'face 'message-mml)\r
+\r
+(defvar notmuch-show-import-org-ical-map\r
+  (let ((map (make-sparse-keymap)))\r
+    (set-keymap-parent map button-map)\r
+    ;; (define-key map "s" 'notmuch-show-part-button-save)\r
+    ;; (define-key map "v" 'notmuch-show-part-button-view)\r
+    ;; (define-key map "o" 'notmuch-show-part-button-interactively-view)\r
+    map)\r
+  "Submap for button commands")\r
+(fset 'notmuch-show-import-org-ical-map notmuch-show-import-org-ical-map)\r
+\r
+(defun notmuch-show-import-org-ical-default (&optional button)\r
+  (interactive)\r
+  (let ((button (or button (button-at (point)))))\r
+    (if button\r
+       (let ((nth (button-get button :notmuch-part)))\r
+         (if nth\r
+             (let ((id (notmuch-show-get-message-id)))\r
+               (notmuch-with-temp-part-buffer id nth\r
+                 (let ((file (make-temp-file "notmuch-ical")))\r
+                   (write-region (point-min) (point-max) file)\r
+                   (message "importing...")\r
+                   (org-ical-insert-or-update-event file)\r
+                   (message "done."))))\r
+           (message "no part number")))\r
+      (message "no button"))))\r
+\r
+\r
+;; notes for reply:\r
+\r
+;; <#part type=3D"text/calendar" method=3D"REPLY" disposition=3Dinline>\r
+;; BEGIN:VCALENDAR\r
+;; METHOD:REPLY\r
+;; PRODID:Emacs\r
+;; VERSION:2.0\r
+;; BEGIN:VEVENT\r
+;; ATTENDEE;ROLE=3DREQ-PARTICIPANT;PARTSTAT=3DACCEPTED;RSVP=3DTRUE;CN=3Dcra=\r
+ven@gmx\r
+;;  .net:MAILTO:craven@gmx.net\r
+;; DTSTART:20120321T130000Z\r
+;; DTEND:20120321T140000Z\r
+;; UID:040000008200E00074C5B7101A82E008000000005AE3DDCF5E07CD01000000000000=\r
+000\r
+;;  010000000A0757F7AAED41D4D8ABFCEFCA035BA3A\r
+;; CLASS:PUBLIC\r
+;; PRIORITY:5\r
+;; DTSTAMP:20120321T123325Z\r
+;; TRANSP:OPAQUE\r
+;; STATUS:CONFIRMED\r
+;; SEQUENCE:0\r
+;; END:VEVENT\r
+;; END:VCALENDAR\r
+\r
+--=-=-=\r
+Content-Type: application/emacs-lisp\r
+Content-Disposition: attachment; filename=org-ical.el\r
+Content-Transfer-Encoding: quoted-printable\r
+\r
+(require 'icalendar)\r
+\r
+;;; add this to org-capture-templates:\r
+\r
+;; ("K" "Kill Ring Head" entry\r
+;;  (file+headline "~/.org/life.org" "Dates")\r
+;;  "%c")\r
+\r
+(defun icalendar--get-event-properties-full (event prop)\r
+  "For the given EVENT return a list of all occurrences of the property PRO=\r
+P."\r
+  (let ((props (car (cddr event))) pp result)\r
+    (while props\r
+      (setq pp (car props))\r
+      (if (eq (car pp) prop)\r
+          (setq result (cons pp result)))\r
+      (setq props (cdr props)))\r
+    result))\r
+\r
+(defun ical-parse-important-data (ical-list)\r
+  "Return important data from an ical-list (which is returned from icalenda=\r
+r--read-element)"\r
+  (let* ((ev (icalendar--all-events ical-list))\r
+        (error-string "")\r
+        (event-ok t)\r
+        (found-error nil)\r
+        (zone-map (icalendar--convert-all-timezones ical-list))\r
+        e result)\r
+    ;; step through all events/appointments\r
+    (while ev\r
+      (setq e (car ev))\r
+      (setq ev (cdr ev))\r
+      (setq event-ok nil)\r
+      (condition-case error-val\r
+         (let* ((dtstart (icalendar--get-event-property e 'DTSTART))\r
+                (dtstart-zone (icalendar--find-time-zone\r
+                               (icalendar--get-event-property-attributes\r
+                                e 'DTSTART)\r
+                               zone-map))\r
+                (dtstart-dec (icalendar--decode-isodatetime dtstart nil\r
+                                                            dtstart-zone))\r
+                (start-d dtstart-dec)\r
+                (start-t dtstart-dec)\r
+                (dtend (icalendar--get-event-property e 'DTEND))\r
+                (dtend-zone (icalendar--find-time-zone\r
+                             (icalendar--get-event-property-attributes\r
+                              e 'DTEND)\r
+                             zone-map))\r
+                (dtend-dec (icalendar--decode-isodatetime dtend\r
+                                                          nil dtend-zone))\r
+                (dtend-1-dec (icalendar--decode-isodatetime dtend -1\r
+                                                            dtend-zone))\r
+                end-d\r
+                end-1-d\r
+                end-t\r
+                (summary (icalendar--convert-string-for-import\r
+                          (or (icalendar--get-event-property e 'SUMMARY)\r
+                              "No summary")))\r
+                (rrule (icalendar--get-event-property e 'RRULE))\r
+                (rdate (icalendar--get-event-property e 'RDATE))\r
+                (duration (icalendar--get-event-property e 'DURATION)))\r
+           (icalendar--dmsg "%s: `%s'" start-d summary)\r
+           ;; check whether start-time is missing\r
+           (if  (and dtstart\r
+                     (string=3D\r
+                      (cadr (icalendar--get-event-property-attributes\r
+                             e 'DTSTART))\r
+                      "DATE"))\r
+               (setq start-t nil))\r
+           (when duration\r
+             (let ((dtend-dec-d (icalendar--add-decoded-times\r
+                                 dtstart-dec\r
+                                 (icalendar--decode-isoduration duration)))\r
+                   (dtend-1-dec-d (icalendar--add-decoded-times\r
+                                   dtstart-dec\r
+                                   (icalendar--decode-isoduration duration\r
+                                                                  t))))\r
+               (if (and dtend-dec (not (eq dtend-dec dtend-dec-d)))\r
+                   (message "Inconsistent endtime and duration for %s"\r
+                            summary))\r
+               (setq dtend-dec dtend-dec-d)\r
+               (setq dtend-1-dec dtend-1-dec-d)))\r
+           (setq end-d (if dtend-dec\r
+                           dtend-dec\r
+                         start-d))\r
+           (setq end-1-d (if dtend-1-dec\r
+                             (icalendar--datetime-to-diary-date dtend-1-dec)\r
+                           start-d))\r
+           (setq end-t (if (and\r
+                            dtend-dec\r
+                            (not (string=3D\r
+                                  (cadr\r
+                                   (icalendar--get-event-property-attributes\r
+                                    e 'DTEND))\r
+                                  "DATE")))\r
+                           dtend-dec\r
+                         start-t))\r
+           (icalendar--dmsg "start-d: %s, end-d: %s" start-d end-d)\r
+           (let ((location (icalendar--get-event-property e 'LOCATION))\r
+                 (organizer (icalendar--get-event-properties-full e 'ORGANIZER))\r
+                 (summary (icalendar--get-event-property e 'SUMMARY))\r
+                 (description (icalendar--get-event-property e 'DESCRIPTION))\r
+                 (attendees (icalendar--get-event-properties-full e 'ATTENDEE))\r
+                 (uid (icalendar--get-event-property e 'UID))\r
+                 (sequence (icalendar--get-event-property e 'SEQUENCE))\r
+                 (created (icalendar--decode-isodatetime (icalendar--get-event-property =\r
+e 'CREATED)))\r
+                 (last-modified (icalendar--decode-isodatetime (icalendar--get-event-pro=\r
+perty e 'LAST-MODIFIED))))\r
+             (setq result (cons `(start ,start-d\r
+                                          end ,end-d\r
+                                          location ,location\r
+                                          organizer ,organizer\r
+                                          summary ,summary\r
+                                          description ,description=20\r
+                                          attendees ,attendees\r
+                                          uid ,uid\r
+                                          sequence ,sequence\r
+                                          created ,created\r
+                                          last-modified ,last-modified) result)))\r
+           ;; (cond\r
+           ;;  ;; recurring event\r
+           ;;  (rrule\r
+           ;;   (setq diary-string\r
+           ;;    (icalendar--convert-recurring-to-diary e dtstart-dec start-t\r
+           ;;                                           end-t))\r
+           ;;   (setq event-ok t))\r
+           ;;  (rdate\r
+           ;;   (icalendar--dmsg "rdate event")\r
+           ;;   (setq diary-string "")\r
+           ;;   (mapc (lambda (datestring)\r
+           ;;      (setq diary-string\r
+           ;;            (concat diary-string\r
+           ;;                    (format "......"))))\r
+           ;;    (icalendar--split-value rdate)))\r
+           ;;  ;; non-recurring event\r
+           ;;  ;; all-day event\r
+           ;;  ((not (string=3D start-d end-d))\r
+           ;;   (setq diary-string\r
+           ;;    (icalendar--convert-non-recurring-all-day-to-diary\r
+           ;;     e start-d end-1-d))\r
+           ;;   (setq event-ok t))\r
+           ;;  ;; not all-day\r
+           ;;  ((and start-t (or (not end-t)\r
+           ;;               (not (string=3D start-t end-t))))\r
+           ;;   (setq diary-string\r
+           ;;    (icalendar--convert-non-recurring-not-all-day-to-diary\r
+           ;;     e dtstart-dec dtend-dec start-t end-t))\r
+           ;;   (setq event-ok t))\r
+           ;;  ;; all-day event\r
+           ;;  (t\r
+           ;;   (icalendar--dmsg "all day event")\r
+           ;;   (setq diary-string (icalendar--datetime-to-diary-date\r
+           ;;                  dtstart-dec "/"))\r
+           ;;   (setq event-ok t)))\r
+           ;; add all other elements unless the user doesn't want to have\r
+           ;; them\r
+           ;; (if event-ok\r
+           ;;     (progn\r
+           ;;  ;; (setq diary-string\r
+           ;;  ;;       (concat diary-string " "\r
+           ;;  ;;            (icalendar--format-ical-event e)))\r
+           ;;  ;; (if do-not-ask (setq summary nil))\r
+           ;;  ;; ;; add entry to diary and store actual name of diary\r
+           ;;  ;; ;; file (in case it was nil)\r
+           ;;  ;; (setq diary-file\r
+           ;;  ;;       (icalendar--add-diary-entry diary-string diary-file\r
+           ;;  ;;                                non-marking summary))\r
+           ;;  (format )\r
+           ;;  )\r
+           ;;   ;; event was not ok\r
+           ;;   (setq found-error t)\r
+           ;;   (setq error-string\r
+           ;;    (format "%s\nCannot handle this event:%s"\r
+           ;;            error-string e)))\r
+           )))\r
+    result))\r
+\r
+\r
+(defun org-ical-format-contact (organizer-list)\r
+  (let ((e-mail (car (last organizer-list)))\r
+       (cn (find 'CN  organizer-list :test (lambda (key item) (and (listp item) (=\r
+eq (car item) key))))))\r
+    (if (string-prefix-p "mailto:" e-mail t)\r
+       (setq e-mail (substring e-mail (length "mailto:"))))\r
+    (format "%s<%s>" (if cn (format "\"%s\" " (cadr cn)) "") e-mail)))\r
+\r
+(defun org-ical-parse-file (filename)\r
+  (save-current-buffer\r
+    (set-buffer (find-file filename))\r
+    (prog1=20\r
+       (ical-parse-important-data (icalendar--read-element nil nil))\r
+      (kill-buffer))))\r
+\r
+(defun org-ical-create-entry (event)\r
+  (let* ((start (plist-get event 'start))\r
+        (end (plist-get event 'end))\r
+        (uid (plist-get event 'uid))\r
+        (description (plist-get event 'description))\r
+        (location (plist-get event 'location))\r
+        (organizer (plist-get event 'organizer))\r
+        (summary (plist-get event 'summary))\r
+        (created (plist-get event 'created))\r
+        (last-modified (plist-get event 'last-modified))\r
+        (sequence (plist-get event 'sequence))\r
+        (priority (plist-get event 'priority))\r
+        (attendees (plist-get event 'attendees)))\r
+    (cons uid (replace-regexp-in-string "\\\\n" "\n"\r
+                                       (replace-regexp-in-string "\\\\," "," (substring-no-properties (format=\r
+=20\r
+                                                                                                       "* %s\r
+  :PROPERTIES:\r
+    :ID: %s\r
+    :START-TIME: %s\r
+    :END-TIME: %s\r
+    :LOCATION: %s\r
+    :ORGANIZER: %S\r
+    :ATTENDEES: %S\r
+    :CREATED: %s\r
+    :LAST-MODIFIED: %s\r
+    :SEQUENCE: %s\r
+    :PRIORITY: %s\r
+  :END:\r
+  %s\r
+  %s--%s"\r
+                                                                                                       summary\r
+                                                                                                       uid\r
+                                                                                                       (apply 'encode-time start)\r
+                                                                                                       (apply 'encode-time end)\r
+                                                                                                       location\r
+                                                                                                       (org-ical-format-contact (car organizer))\r
+                                                                                                       (mapcar 'org-ical-format-contact attendees)\r
+                                                                                                       (if created (format-time-string (org-time-stamp-format t t) (a=\r
+pply 'encode-time created)) nil)=20\r
+                                                                                                       (if last-modified (format-time-string (org-time-stamp-format t=\r
+ t) (apply 'encode-time last-modified)) nil)=20\r
+                                                                                                       sequence\r
+                                                                                                       priority\r
+                                                                                                       description\r
+                                                                                                       (format-time-string (org-time-stamp-format t) (apply 'encode-t=\r
+ime start))\r
+                                                                                                       (format-time-string (org-time-stamp-format t) (apply 'encode-t=\r
+ime end))\r
+)))))))\r
+\r
+(defun org-ical-insert-or-update-event (filename)\r
+  (let* ((events (org-ical-parse-file filename))\r
+        (uid+org-entries (mapcar 'org-ical-create-entry events)))\r
+    (dolist (uid+entry uid+org-entries)\r
+      (let ((uid (car uid+entry))\r
+           (entry (cdr uid+entry)))\r
+       (let ((org-entry (org-id-find uid)))\r
+         (if org-entry\r
+             (org-ical-update-event entry org-entry)\r
+           (org-ical-insert-event entry)))))))\r
+\r
+(defun org-ical-insert-event (new-entry)\r
+  (kill-new (substring-no-properties new-entry))\r
+  (org-capture nil "K"))\r
+\r
+(defun org-ical-update-event (new-entry org-entry)\r
+;; use org-narrow-to-subtree or org-tree-to-indirect-buffer\r
+  (error "Not implemented yet, updating event %S with existing entry %S" ne=\r
+w-entry org-entry))\r
+\r
+(provide 'org-ical)\r
+\r
+;(org-ical-insert-or-update-event "/tmp/test.ical")\r
+;(org-ical-parse-file "/tmp/test.ical")\r
+\r
+\r
+\r
+\r
+--=-=-=--\r