Add webmail as mail source. Now only support HoTMaiL.
authorShengHuo ZHU <zsh@cs.rochester.edu>
Mon, 22 Nov 1999 09:44:42 +0000 (09:44 +0000)
committerShengHuo ZHU <zsh@cs.rochester.edu>
Mon, 22 Nov 1999 09:44:42 +0000 (09:44 +0000)
lisp/ChangeLog
lisp/mail-source.el
lisp/webmail.el [new file with mode: 0644]

index ca0aabe..75643dc 100644 (file)
@@ -1,3 +1,10 @@
+1999-11-22 04:35:43  Shenghuo ZHU  <zsh@cs.rochester.edu>
+
+       * mail-source.el (mail-source-keyword-map): Add webmail.
+       (mail-source-fetcher-alist): Ditto.
+       (mail-source-fetch-webmail): New function.
+       * webmail.el: New file.
+
 1999-11-21 12:20:02  Shenghuo ZHU  <zsh@cs.rochester.edu>
 
        * nnwarchive.el (nnwarchive-request-group): Print 0 if it is nil.
index e8516d0..4922df5 100644 (file)
@@ -101,7 +101,11 @@ This variable is a list of mail source specifiers."
        (:mailbox "INBOX")
        (:predicate "UNSEEN UNDELETED")
        (:fetchflag "\Deleted")
-       (:dontexpunge)))
+       (:dontexpunge))
+      (webmail
+       (:wmtype hotmail)
+       (:user (or (user-login-name) (getenv "LOGNAME") (getenv "USER")))
+       (:password)))
     "Mapping from keywords to default values.
 All keywords that can be used must be listed here."))
 
@@ -110,7 +114,8 @@ All keywords that can be used must be listed here."))
     (directory mail-source-fetch-directory)
     (pop mail-source-fetch-pop)
     (maildir mail-source-fetch-maildir)
-    (imap mail-source-fetch-imap))
+    (imap mail-source-fetch-imap)
+    (webmail mail-source-fetch-webmail))
   "A mapping from source type to fetcher function.")
 
 (defvar mail-source-password-cache nil)
@@ -490,6 +495,16 @@ If ARGS, PROMPT is used as an argument to `format'."
       (kill-buffer buf)
       found)))
 
+(eval-and-compile
+  (autoload 'webmail-fetch "webmail"))
+
+(defun mail-source-fetch-webmail (source callback)
+  "Fetch for webmail source."
+  (mail-source-bind (webmail source)
+    (save-excursion
+      (webmail-fetch mail-source-crash-box wmtype user password)
+      (mail-source-callback callback (symbol-name wmtype)))))
+
 (provide 'mail-source)
 
 ;;; mail-source.el ends here
diff --git a/lisp/webmail.el b/lisp/webmail.el
new file mode 100644 (file)
index 0000000..80592cf
--- /dev/null
@@ -0,0 +1,394 @@
+;;; webmail.el --- interfacing with web mail
+;; Copyright (C) 1999 Free Software Foundation, Inc.
+
+;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
+;; Keywords: hotmail
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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.
+
+;;; Commentary:
+
+;; Note: You need to have `url' and `w3' installed for this backend to
+;; work.
+
+;; Todo: To support more web mail.
+
+;; Known bugs: 
+;; 1. In w3, there are two copies of url-maybe-relative.
+;;    If it is loaded from w3.el, (load-library "url"). 
+;;    Fixed in w3 4.0pre46.
+;; 2. Hotmail only accept one line cookie, while w3 breaks cookies 
+;;    into lines.
+;;    Maybe fixed in w3 4.0pre47+?.
+
+;; Warning:
+;; webmail is an experimental function, which means NO WARRANTY.
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(require 'nnoo)
+(require 'message)
+(require 'gnus-util)
+(require 'gnus)
+(require 'nnmail)
+(require 'mm-util)
+(require 'mail-source)
+(require 'mml)
+(eval-when-compile
+  (ignore-errors
+    (require 'w3)
+    (require 'url)
+    (require 'w3-forms)
+    (require 'nnweb)))
+;; Report failure to find w3 at load time if appropriate.
+(eval '(progn
+        (require 'w3)
+        (require 'url)
+        (require 'w3-forms)
+        (require 'nnweb)))
+
+
+;;;
+
+(defvar webmail-type-definition
+  '((hotmail
+     ;; Hotmail hate other HTTP user agents and use one line cookie
+     (paranoid agent cookie)
+     (address . "www.hotmail.com")
+     (open-url "http://www.hotmail.com")
+     (open-snarf . webmail-hotmail-open)
+     ;; W3 hate redirect POST
+     (login-url
+      "http://%s/cgi-bin/dologin?login=%s&passwd=%s&enter=Sign+in&sec=no&curmbox=ACTIVE&_lang=&js=yes&id=2&tw=-10000&beta="
+       webmail-aux user password)
+     (trash-url 
+      "%s&login=%s&f=33792&curmbox=ACTIVE&_lang=&js=&foo=inbox&page=&%s=on&Move+To.x=Move+To&tobox=trAsH" 
+      webmail-aux user id)    
+     (list-snarf . webmail-hotmail-list)
+     (article-snarf . webmail-hotmail-article))))
+
+(defvar webmail-variables
+  '(address article-snarf article-url list-snarf list-url 
+           login-url login-snarf open-url open-snarf site articles
+           post-process paranoid trash-url))
+
+(defconst webmail-version "webmail 1.0")
+
+(defvar webmail-newmail-only nil
+  "Only fetch new mails.")
+
+(defvar webmail-move-to-trash-can t
+  "Move mail to trash can after fetch it.")
+
+;;; Internal variables
+
+(defvar webmail-address nil)
+(defvar webmail-paranoid nil)
+(defvar webmail-aux nil)
+(defvar webmail-article-snarf nil)
+(defvar webmail-article-url nil)
+(defvar webmail-list-snarf nil)
+(defvar webmail-list-url nil)
+(defvar webmail-login-url nil)
+(defvar webmail-login-snarf nil)
+(defvar webmail-open-snarf nil)
+(defvar webmail-open-url nil)
+(defvar webmail-trash-url nil)
+(defvar webmail-articles nil)
+(defvar webmail-post-process nil)
+
+(defvar webmail-buffer nil)
+;;; Interface functions
+
+(defun webmail-setdefault (type)
+  (let ((type-def (cdr (assq type webmail-type-definition)))
+       (vars webmail-variables)
+       pair)
+    (dolist (var vars)
+      (if (setq pair (assq var type-def))
+         (set (intern (concat "webmail-" (symbol-name var))) (cdr pair))
+       (set (intern (concat "webmail-" (symbol-name var))) nil)))))
+
+(defun webmail-encode-www-form-urlencoded (pairs)
+  "Return PAIRS encoded for forms."
+  (mapconcat
+   (function
+    (lambda (data)
+      (concat (w3-form-encode-xwfu (car data)) "="
+             (w3-form-encode-xwfu (cdr data)))))
+   pairs "&"))
+
+(defun webmail-fetch-simple (url content)
+  (let ((url-request-data content)
+       (url-request-method "POST")
+       (url-request-extra-headers
+        '(("Content-type" . "application/x-www-form-urlencoded"))))
+    (nnweb-insert url))
+  t)
+
+(defun webmail-fetch-form (url pairs)
+  (let ((url-request-data (webmail-encode-www-form-urlencoded pairs))
+       (url-request-method "POST")
+       (url-request-extra-headers
+        '(("Content-type" . "application/x-www-form-urlencoded"))))
+    (nnweb-insert url))
+  t)
+
+(defun webmail-eval (expr)
+  (cond
+   ((consp expr)
+    (cons (webmail-eval (car expr)) (webmail-eval (cdr expr))))
+   ((symbolp expr)
+    (eval expr))
+   (t
+    expr)))
+
+(defun webmail-url (xurl)
+  (let ((url-confirmation-func 'identity))
+    (cond 
+     ((eq (car xurl) 'content)
+      (pop xurl)
+      (webmail-fetch-simple (if (stringp (car xurl))
+                               (car xurl)
+                             (apply 'format (webmail-eval (car xurl))))
+                           (apply 'format (webmail-eval (cdr xurl)))))
+     ((eq (car xurl) 'post)
+      (pop xurl)
+      (webmail-fetch-form (car xurl) (webmail-eval (cdr xurl))))
+     (t
+      (nnweb-insert (apply 'format (webmail-eval xurl)))))))
+
+(defun webmail-decode-entities ()
+  (goto-char (point-min))
+  (while (re-search-forward "&\\(#[0-9]+\\|[a-z]+\\);" nil t)
+    (replace-match (char-to-string 
+                   (if (eq (aref (match-string 1) 0) ?\#)
+                       (string-to-number (substring (match-string 1) 1))
+                     (or (cdr (assq (intern (match-string 1))
+                                    w3-html-entities))
+                         ?#)))
+                  t t)))
+
+(defun webmail-decode-entities-string (str)
+  (with-temp-buffer
+    (insert str)
+    (webmail-decode-entities)
+    (buffer-substring (point-min) (point-max))))
+
+(defun webmail-remove-markup ()
+  (goto-char (point-min))
+  (while (search-forward "<!--" nil t)
+    (delete-region (match-beginning 0)
+                  (or (search-forward "-->" nil t)
+                      (point-max))))
+  (goto-char (point-min))
+  (while (re-search-forward "<[^>]+>" nil t)
+    (replace-match "" t t)))
+
+(defun webmail-init ()
+  "Initialize buffers and such."
+  (if (gnus-buffer-live-p webmail-buffer)
+      (set-buffer webmail-buffer)
+    (setq webmail-buffer
+         (nnheader-set-temp-buffer " *webmail*"))))
+
+(defvar url-package-name)
+(defvar url-package-version)
+(defvar url-cookie-multiple-line)
+
+(defun webmail-fetch (file wmtype user password)
+  (webmail-setdefault wmtype)
+  (let ((url-package-name (if (memq 'agent webmail-paranoid)
+                             "Mozilla"
+                           url-package-name))
+       (url-package-version (if (memq 'agent webmail-paranoid)
+                                "4.0"
+                              url-package-version))
+       (url-cookie-multiple-line (if (memq 'cookie webmail-paranoid)
+                                     nil
+                                   url-cookie-multiple-line)))
+    (webmail-init)
+    (when webmail-open-url 
+      (erase-buffer)
+      (webmail-url webmail-open-url))
+    (if webmail-open-snarf (funcall webmail-open-snarf))
+    (when webmail-login-url 
+      (erase-buffer)
+      (webmail-url webmail-login-url))
+    (if webmail-login-snarf 
+       (funcall webmail-login-snarf))
+    (when webmail-list-url 
+      (erase-buffer)
+      (webmail-url webmail-list-url))
+    (if webmail-list-snarf 
+       (funcall webmail-list-snarf))
+    (let (item id (n 0))
+      (while (setq item (pop webmail-articles))
+       (message "Fetching mail #%d..." (setq n (1+ n)))
+       (erase-buffer)
+       (nnweb-insert (cdr item))
+       (setq id (car item))
+       (if webmail-article-snarf 
+           (funcall webmail-article-snarf file id))
+       (when (and webmail-trash-url webmail-move-to-trash-can)
+         (message "Move mail #%d to trash can..." n)
+         (webmail-url webmail-trash-url))))
+    (if webmail-post-process
+       (funcall webmail-post-process))))
+
+;;; hotmail
+
+(defun webmail-hotmail-open ()
+  (goto-char (point-min))
+  (if (re-search-forward 
+       "action=\"https?://\\([^/]+\\)/cgi-bin/dologin" nil t)
+      (setq webmail-aux (match-string 1))
+    (error "Can't find login url (open@1)")))
+
+(defun webmail-hotmail-list ()
+  (let (site url newp)
+    (goto-char (point-min))
+    (if (re-search-forward "[0-9]+ messages, [0-9]+ new") 
+       (message "Found %s" (match-string 0)))
+    (goto-char (point-min))
+    (if (re-search-forward 
+        "action=\"https?://\\([^/]+\\)/cgi-bin/HoTMaiL" nil t)
+       (setq site (match-string 1))
+      (error "Can't find server url (list@1)"))
+    (goto-char (point-min))
+    (if (re-search-forward "disk=\\([^&]+\\)&" nil t)
+       (setq webmail-aux 
+             (concat "http://" site "/cgi-bin/HoTMaiL?disk=" 
+                     (match-string 1)))
+      (error "Can't find disk (list@2)"))
+    (goto-char (point-max))
+    (while (re-search-backward 
+           "newmail\\.gif\\|href=\"\\(/cgi-bin/getmsg\\?[^\"]+\\)\"" 
+           nil t)
+      (if (setq url (match-string 1))
+         (progn
+           (if (or newp (not webmail-newmail-only))
+               (let (id)
+                 (if (string-match "msg=\\([^&]+\\)" url)
+                     (setq id (match-string 1 url)))
+                 (push (cons id (concat "http://" site url)) 
+                       webmail-articles)))
+           (setq newp nil))
+       (setq newp t)))))
+
+(defun webmail-hotmail-article (file id)
+  (let (p attachment count tbufs mime)
+    (save-restriction
+      (goto-char (point-min))
+      (if (not (search-forward "FILE: wc_pnames.asp -->" nil t))
+         (error "Can't find start label (article@1)"))
+      (setq p (match-end 0))
+      (search-backward "<table" nil t)
+      (narrow-to-region (point-min) p)
+      (delete-region (point-min) (match-beginning 0)) 
+      (while (search-forward "<a href=" nil t)
+       (setq p (match-beginning 0))
+       (search-forward "</a>" nil t)
+       (delete-region p (match-end 0)))
+      (webmail-remove-markup)
+      (webmail-decode-entities)
+      (goto-char (point-min))
+      (delete-blank-lines)
+      (goto-char (point-max))
+      (widen)
+      (insert "\n")
+      (setq p (point))
+      (while (re-search-forward "<div>\\|\\(http://[^/]+/cgi-bin/getmsg/\\([^\?]+\\)\?[^\"]*\\)\"" nil t)
+       (if (setq attachment (match-string 1))
+           (let ((filename (match-string 2))
+                 bufname) ;; Attachment
+             (delete-region p (match-end 0))
+             (save-excursion
+               (set-buffer (generate-new-buffer " *webmail-att*"))
+               (nnweb-insert attachment)
+               (push (current-buffer) tbufs)
+               (setq bufname (buffer-name)))
+             (setq mime t)
+             (insert "<#part type=" 
+                     (or (and filename
+                              (string-match "\\.[^\\.]+$" filename)
+                              (mailcap-extension-to-mime
+                               (match-string 0 filename)))
+                         "application/octet-stream"))
+             (insert " buffer=\"" bufname "\"")
+             (insert " filename=\"" filename "\"")
+             (insert " description=\"inline\"")
+             (insert "><#/part>\n")
+             (setq p (point)))
+         (delete-region p (match-end 0))
+         (setq count 1)
+         (while (and (> count 0) 
+                     (re-search-forward "</div>\\|\\(<div>\\)" nil t))
+           (if (match-string 1)
+               (setq count (1+ count))
+             (if (= (setq count (1- count)) 0)
+                 (delete-region (match-beginning 0)
+                                (match-end 0)))))
+         (narrow-to-region p (point))
+         (goto-char (point-min))
+         (cond 
+          ((looking-at "<pre>")
+           (goto-char (match-end 0))
+           (if (looking-at "$") (forward-char))
+           (delete-region (point-min) (point))
+           (webmail-remove-markup)
+           (webmail-decode-entities)
+           nil)
+          (t
+           (setq mime t)
+           (insert "<#part type=\"text/html\" disposition=inline>")
+           (goto-char (point-max))
+           (insert "<#/part>")))
+         (goto-char (point-max))
+         (setq p (point))
+         (widen)))
+      (delete-region p (point-max))
+      (goto-char (point-min))
+      ;; Some blank line to seperate mails.
+      (insert "\n\nFrom nobody " (current-time-string) "\n")
+      (if id
+         (insert "Message-ID: <" id "@hotmail.com>\n"))
+      (unless (looking-at "$") 
+       (search-forward "\n\n" nil t)
+       (forward-line -1))
+      (narrow-to-region (point) (point-max))
+      (if mime
+         (insert "MIME-Version: 1.0\n"
+                 (prog1
+                     (mml-generate-mime)
+                   (delete-region (point-min) (point-max)))))
+      (goto-char (point-min))
+      (widen)
+      (let (case-fold-search)
+       (while (re-search-forward "^From " nil t)
+         (beginning-of-line)
+         (insert ">"))))
+    (mm-append-to-file (point-min) (point-max) file)
+    (dolist (buf tbufs)
+      (kill-buffer buf))))
+
+(provide 'webmail)
+
+;;; webmail.el ends here