--- /dev/null
+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