;;; org-mobile.el --- Code for asymmetric sync with a mobile device
-;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+;; Copyright (C) 2009-2011 Free Software Foundation, Inc.
;;
;; Author: Carsten Dominik <carsten at orgmode dot org>
;; Keywords: outlines, hypermedia, calendar, wp
;; Homepage: http://orgmode.org
-;; Version: 6.35i
+;; Version: 7.4
;;
;; This file is part of GNU Emacs.
;;
;;; Commentary:
;;
;; This file contains the code to interact with Richard Moreland's iPhone
-;; application MobileOrg. This code is documented in Appendix B of the
-;; Org-mode manual. The code is not specific for the iPhone, however.
-;; Any external viewer/flagging/editing application that uses the same
-;; conventions could be used.
+;; application MobileOrg, as well as with the Android version by Matthew Jones.
+;; This code is documented in Appendix B of the Org-mode manual. The code is
+;; not specific for the iPhone and Android - any external
+;; viewer/flagging/editing application that uses the same conventions could
+;; be used.
(require 'org)
(require 'org-agenda)
+;;; Code:
+
(eval-when-compile (require 'cl))
(defgroup org-mobile nil
:type 'directory)
(defcustom org-mobile-use-encryption nil
- "Non-nil means keep only encrypted files on the webdav server.
+ "Non-nil means keep only encrypted files on the WebDAV server.
Encryption uses AES-256, with a password given in
`org-mobile-encryption-password'.
When nil, plain files are kept on the server.
Turning on encryption requires to set the same password in the MobileOrg
-application."
+application. Before turning this on, check of MobileOrg does already
+support it - at the time of this writing it did not yet."
:group 'org-mobile
:type 'boolean)
(defcustom org-mobile-encryption-tempfile "~/orgtmpcrypt"
"File that is being used as a temporary file for encryption.
-This must be local file on your local machine (not on the webdav server).
+This must be local file on your local machine (not on the WebDAV server).
You might want to put this file into a directory where only you have access."
:group 'org-mobile
:type 'directory)
This is a single password which is used for AES-256 encryption. The same
password must also be set in the MobileOrg application. All Org files,
including mobileorg.org will be encrypted using this password.
-Note that, whe Org runs the encryption commands, the password could
-be visible on your system with the `ps' command. So this method is only
-intended to keep the files secure on the server, not on your own machine."
+
+SECURITY CONSIDERATIONS:
+
+Note that, when Org runs the encryption commands, the password could
+be visible briefly on your system with the `ps' command. So this method is
+only intended to keep the files secure on the server, not on your own machine.
+
+Also, if you set this variable in an init file (.emacs or .emacs.d/init.el
+or custom.el...) and if that file is stored in a way so that other can read
+it, this also limits the security of this approach. You can also leave
+this variable empty - Org will then ask for the password once per Emacs
+session."
:group 'org-mobile
:type '(string :tag "Password"))
+(defvar org-mobile-encryption-password-session nil)
+
+(defun org-mobile-encryption-password ()
+ (or (org-string-nw-p org-mobile-encryption-password)
+ (org-string-nw-p org-mobile-encryption-password-session)
+ (setq org-mobile-encryption-password-session
+ (read-passwd "Password for MobileOrg: " t))))
+
(defcustom org-mobile-inbox-for-pull "~/org/from-mobile.org"
"The file where captured notes and flags will be appended to.
During the execution of `org-mobile-pull', the file
(string :tag "Selection Keys"))))
(defcustom org-mobile-force-id-on-agenda-items t
- "Non-nil means make all agenda items carry and ID."
+ "Non-nil means make all agenda items carry an ID."
:group 'org-mobile
:type 'boolean)
(defun org-mobile-check-setup ()
"Check if org-mobile-directory has been set up."
+ (org-mobile-cleanup-encryption-tempfile)
(unless (and org-directory
(stringp org-directory)
(string-match "\\S-" org-directory)
(file-name-directory org-mobile-inbox-for-pull)))
(error
"Variable `org-mobile-inbox-for-pull' must point to a file in an existing directory"))
+ (unless (and org-mobile-checksum-binary
+ (string-match "\\S-" org-mobile-checksum-binary))
+ (error "No executable found to compute checksums"))
(when org-mobile-use-encryption
- (unless (string-match "\\S-" org-mobile-encryption-password)
+ (unless (string-match "\\S-" (org-mobile-encryption-password))
(error
"To use encryption, you must set `org-mobile-encryption-password'"))
(unless (file-writable-p org-mobile-encryption-tempfile)
- (error "Cannot write to entryption tempfile %s"
+ (error "Cannot write to encryption tempfile %s"
org-mobile-encryption-tempfile))
(unless (executable-find "openssl")
- (error "openssl is needed to encrypt files."))))
+ (error "openssl is needed to encrypt files"))))
(defun org-mobile-create-index-file ()
"Write the index file in the WebDAV directory."
(lambda (a b) (string< (cdr a) (cdr b)))))
(def-todo (default-value 'org-todo-keywords))
(def-tags (default-value 'org-tag-alist))
+ (target-file (expand-file-name org-mobile-index-file
+ org-mobile-directory))
file link-name todo-kwds done-kwds tags drawers entry kwds dwds twds)
(org-prepare-agenda-buffers (mapcar 'car files-alist))
(t nil)))
org-tag-alist-for-agenda))))
(with-temp-file
- (expand-file-name org-mobile-index-file org-mobile-directory)
+ (if org-mobile-use-encryption
+ org-mobile-encryption-tempfile
+ target-file)
(while (setq entry (pop def-todo))
(insert "#+READONLY\n")
(setq kwds (mapcar (lambda (x) (if (string-match "(" x)
(insert (format "* [[file:%s][%s]]\n"
link-name link-name)))
(push (cons org-mobile-index-file (md5 (buffer-string)))
- org-mobile-checksum-files))))
+ org-mobile-checksum-files))
+ (when org-mobile-use-encryption
+ (org-mobile-encrypt-and-move org-mobile-encryption-tempfile
+ target-file)
+ (org-mobile-cleanup-encryption-tempfile))))
(defun org-mobile-copy-agenda-files ()
"Copy all agenda files to the stage or WebDAV directory."
(when (string-match "[a-fA-F0-9]\\{30,40\\}" check)
(push (cons link-name (match-string 0 check))
org-mobile-checksum-files))))
+
(setq file (expand-file-name org-mobile-capture-file
org-mobile-directory))
(save-excursion
(setq buf (find-file file))
- (and (= (point-min) (point-max)) (insert "\n"))
- (save-buffer)
+ (when (and (= (point-min) (point-max)))
+ (insert "\n")
+ (save-buffer)
+ (when org-mobile-use-encryption
+ (write-file org-mobile-encryption-tempfile)
+ (org-mobile-encrypt-and-move org-mobile-encryption-tempfile file)))
(push (cons org-mobile-capture-file (md5 (buffer-string)))
org-mobile-checksum-files))
+ (org-mobile-cleanup-encryption-tempfile)
(kill-buffer buf)))
(defun org-mobile-write-checksums ()
(if (org-bound-and-true-p
org-mobile-force-id-on-agenda-items)
(org-id-get m 'create)
- (org-entry-get m "ID")))
+ (or (org-entry-get m "ID")
+ (org-mobile-get-outline-path-link m))))
(insert " :PROPERTIES:\n :ORIGINAL_ID: " id
"\n :END:\n")))))
(beginning-of-line 2))
- (push (cons (file-name-nondirectory file) (md5 (buffer-string)))
+ (push (cons "agendas.org" (md5 (buffer-string)))
org-mobile-checksum-files))
(message "Agenda written to Org file %s" file)))
+(defun org-mobile-get-outline-path-link (pom)
+ (org-with-point-at pom
+ (concat "olp:"
+ (org-mobile-escape-olp (file-name-nondirectory buffer-file-name))
+ "/"
+ (mapconcat 'org-mobile-escape-olp
+ (org-get-outline-path)
+ "/")
+ "/"
+ (org-mobile-escape-olp (nth 4 (org-heading-components))))))
+
+(defun org-mobile-escape-olp (s)
+ (let ((table '((?: . "%3a") (?\[ . "%5b") (?\] . "%5d") (?/ . "%2f"))))
+ (org-link-escape s table)))
+
;;;###autoload
(defun org-mobile-create-sumo-agenda ()
"Create a file that contains all custom agenda views."
(when sumo
(org-store-agenda-views))
(when org-mobile-use-encryption
- (org-mobile-encrypt-file file1 file)
- (delete-file file1))))
+ (org-mobile-encrypt-and-move file1 file)
+ (delete-file file1)
+ (org-mobile-cleanup-encryption-tempfile))))
(defun org-mobile-encrypt-and-move (infile outfile)
"Encrypt INFILE locally to INFILE_enc, then move it to OUTFILE.
"Encrypt INFILE to OUTFILE, using `org-mobile-encryption-password'."
(shell-command
(format "openssl enc -aes-256-cbc -salt -pass %s -in %s -out %s"
- (shell-quote-argument (concat "pass:" org-mobile-encryption-password))
+ (shell-quote-argument (concat "pass:"
+ (org-mobile-encryption-password)))
(shell-quote-argument (expand-file-name infile))
(shell-quote-argument (expand-file-name outfile)))))
"Decrypt INFILE to OUTFILE, using `org-mobile-encryption-password'."
(shell-command
(format "openssl enc -d -aes-256-cbc -salt -pass %s -in %s -out %s"
- (shell-quote-argument (concat "pass:" org-mobile-encryption-password))
+ (shell-quote-argument (concat "pass:"
+ (org-mobile-encryption-password)))
(shell-quote-argument (expand-file-name infile))
(shell-quote-argument (expand-file-name outfile)))))
+(defun org-mobile-cleanup-encryption-tempfile ()
+ "Remove the encryption tempfile if it exists."
+ (and (stringp org-mobile-encryption-tempfile)
+ (file-exists-p org-mobile-encryption-tempfile)
+ (delete-file org-mobile-encryption-tempfile)))
+
(defun org-mobile-move-capture ()
"Move the contents of the capture file to the inbox file.
Return a marker to the location where the new content has been added.
(capture-buffer
(if (not org-mobile-use-encryption)
(find-file-noselect capture-file)
- (delete-file org-mobile-encryption-tempfile)
+ (org-mobile-cleanup-encryption-tempfile)
(setq encfile (concat org-mobile-encryption-tempfile "_enc"))
(copy-file capture-file encfile)
(org-mobile-decrypt-file encfile org-mobile-encryption-tempfile)
(kill-buffer capture-buffer)
(when org-mobile-use-encryption
(org-mobile-encrypt-and-move org-mobile-encryption-tempfile
- capture-file))
+ capture-file)
+ (org-mobile-cleanup-encryption-tempfile))
(if not-empty insertion-point)))
(defun org-mobile-update-checksum-for-capture-file (buffer-string)
auto-correction feature. Needs a bit more thinking. So this function
is currently a noop.")
-
-(defun org-find-olp (path)
- "Return a marker pointing to the entry at outline path OLP.
-If anything goes wrong, the return value will instead an error message,
-as a string."
- (let* ((file (pop path))
- (buffer (find-file-noselect file))
- (level 1)
- (lmin 1)
- (lmax 1)
- limit re end found pos heading cnt)
- (unless buffer (error "File not found :%s" file))
- (with-current-buffer buffer
- (save-excursion
- (save-restriction
- (widen)
- (setq limit (point-max))
- (goto-char (point-min))
- (while (setq heading (pop path))
- (setq re (format org-complex-heading-regexp-format
- (regexp-quote heading)))
- (setq cnt 0 pos (point))
- (while (re-search-forward re end t)
- (setq level (- (match-end 1) (match-beginning 1)))
- (if (and (>= level lmin) (<= level lmax))
- (setq found (match-beginning 0) cnt (1+ cnt))))
- (when (= cnt 0) (error "Heading not found on level %d: %s"
- lmax heading))
- (when (> cnt 1) (error "Heading not unique on level %d: %s"
- lmax heading))
- (goto-char found)
- (setq lmin (1+ level) lmax (+ lmin (if org-odd-levels-only 1 0)))
- (setq end (save-excursion (org-end-of-subtree t t))))
- (when (org-on-heading-p)
- (move-marker (make-marker) (point))))))))
-
(defun org-mobile-locate-entry (link)
(if (string-match "\\`id:\\(.*\\)$" link)
(org-id-find (match-string 1 link) 'marker)
t)
(t (error "Body was changed in MobileOrg and on the computer")))))))
-
(defun org-mobile-tags-same-p (list1 list2)
"Are the two tag lists the same?"
(not (or (org-delete-all list1 list2)
(provide 'org-mobile)
-;; arch-tag: ace0e26c-58f2-4309-8a61-05ec1535f658
;;; org-mobile.el ends here