changeset 110:e8229c276c63

Update mediawiki.el with upstream
author Jordi Gutiérrez Hermoso <jordigh@octave.org>
date Fri, 06 Feb 2015 14:26:21 -0500
parents 6645c3bbefaf
children f3c23edcd97a
files packages/mediawiki.el
diffstat 1 files changed, 342 insertions(+), 217 deletions(-) [+]
line wrap: on
line diff
--- a/packages/mediawiki.el
+++ b/packages/mediawiki.el
@@ -1,18 +1,18 @@
 ;;; mediawiki.el --- mediawiki frontend
 
-;; Copyright (C) 2008, 2009, 2010 Mark A. Hershberger
+;; Copyright (C) 2008, 2009, 2010, 2011 Mark A. Hershberger
 
 ;; Original Authors: Jerry <unidevel@yahoo.com.cn>,
 ;;      Chong Yidong <cyd at stupidchicken com> for wikipedia.el,
 ;;      Uwe Brauer <oub at mat.ucm.es> for wikimedia.el
 ;; Author: Mark A. Hershberger <mah@everybody.org>
-;; Version: 2.2.1
+;; Version: 2.2.4.1
 ;; Created: Sep 17 2004
 ;; Keywords: mediawiki wikipedia network wiki
 ;; URL: http://launchpad.net/mediawiki-el
-;; Last Modified: <2010-07-11 04:02:16 mah>
+;; Last Modified: <2012-12-31 15:54:27 mah>
 
-(defconst mediawiki-version "2.2.1"
+(defconst mediawiki-version "2.2.4.1"
   "Current version of mediawiki.el")
 
 ;; This file is NOT (yet) part of GNU Emacs.
@@ -57,7 +57,8 @@
 ;; Save a wiki buffer:  C-x C-s
 ;; Save a wiki buffer with a different name:  C-x C-w
 
-;;; TODO:
+;;; TODO
+
 ;;  * Optionally use org-mode formatting for editing and translate
 ;;    that to mw
 ;;  * Move url-* methods to url-http
@@ -67,6 +68,13 @@
 ;;    English or German.  This should probably just be replaced with
 ;;    customizable words given MediaWiki's wide language support.
 
+;;; Changes
+
+;; Since 2.2.4:
+;;  * Made it clearer where debugging information is found when
+;;    mediawiki-debug is non-nil by adding messages to the message
+;;    buffer when debug buffers are killed.
+
 ;;; History
 
 ;; From the News section of wikipedia.el comes this bit, kept here for
@@ -130,179 +138,188 @@
 (require 'mml)
 (require 'mm-url)
 (require 'ring)
-(eval-when-compile (require 'cl))
+(eval-when-compile (progn
+                     (require 'cl)
+                     ;; Below copied from url-http to avoid compilation warnings
+                     (defvar url-http-extra-headers)
+                     (defvar url-http-target-url)
+                     (defvar url-http-proxy)
+                     (defvar url-http-connection-opened)
+                     ;; This should only be used in xemacs, anyway
+                     (setq byte-compile-not-obsolete-funcs (list 'assoc-ignore-case))))
 
 ;; As of 2010-06-22, these functions are in Emacs
-(unless (fboundp 'url-user-for-url)
-  (require 'url-parse)
-  (defmacro url-bit-for-url (method lookfor url)
+(unless (fboundp 'url-bit-for-url)
+  (defun url-bit-for-url (method lookfor url)
     (when (fboundp 'auth-source-user-or-password)
-      `(let* ((urlobj (url-generic-parse-url url))
-              (bit (funcall ,method urlobj))
-              (methods (list 'url-recreate-url
-                             'url-host)))
-         (while (and (not bit) (> (length methods) 0))
-           (setq bit
-                 (auth-source-user-or-password
-                  ,lookfor (funcall (pop methods) urlobj) (url-type urlobj))))
-         bit)))
+      (let* ((urlobj (url-generic-parse-url url))
+             (bit (funcall method urlobj))
+             (methods (list 'url-recreate-url
+                            'url-host)))
+        (if (and (not bit) (> (length methods) 0))
+            (auth-source-user-or-password
+             lookfor (funcall (pop methods) urlobj) (url-type urlobj))
+          bit)))))
 
+(unless (fboundp 'url-url-for-url)
   (defun url-user-for-url (url)
     "Attempt to use .authinfo to find a user for this URL."
-    (url-bit-for-url 'url-user "login" url))
+    (url-bit-for-url 'url-user "login" url)))
 
+(unless (fboundp 'url-password-for-url)
   (defun url-password-for-url (url)
     "Attempt to use .authinfo to find a password for this URL."
     (url-bit-for-url 'url-password "password" url)))
 
-(if (string= "GET / HTTP/1.0
\nMIME-Version: 1.0
\nConnection: close
\nHost: example.com
\nAccept: */*
\nUser-Agent: URL/Emacs
\nContent-length: 4
\n
\ntest"
-        (let ((url-http-target-url (url-generic-parse-url "http://example.com/"))
-              (url-http-data "test") (url-http-version "1.0")
-              url-http-method url-http-attempt-keepalives url-extensions-header
-              url-http-extra-headers url-http-proxy url-mime-charset-string)
-         (url-http-create-request)))
-    (defun url-http-create-request (&optional ref-url)
-      "Create an HTTP request for `url-http-target-url', referred to by REF-URL."
-      (declare (special proxy-info
-                        url-http-method url-http-data
-                        url-http-extra-headers))
-      (let* ((extra-headers)
-             (request nil)
-             (no-cache (cdr-safe (assoc "Pragma" url-http-extra-headers)))
-             (using-proxy url-http-proxy)
-             (proxy-auth (if (or (cdr-safe (assoc "Proxy-Authorization"
-                                                  url-http-extra-headers))
-                                 (not using-proxy))
-                             nil
-                           (let ((url-basic-auth-storage
-                                  'url-http-proxy-basic-auth-storage))
-                             (url-get-authentication url-http-target-url nil 'any nil))))
-             (real-fname (concat (url-filename url-http-target-url)
-                                 (url-recreate-url-attributes url-http-target-url)))
-             (host (url-host url-http-target-url))
-             (auth (if (cdr-safe (assoc "Authorization" url-http-extra-headers))
-                       nil
-                     (url-get-authentication (or
-                                              (and (boundp 'proxy-info)
-                                                   proxy-info)
-                                              url-http-target-url) nil 'any nil))))
-        (if (equal "" real-fname)
-            (setq real-fname "/"))
-        (setq no-cache (and no-cache (string-match "no-cache" no-cache)))
-        (if auth
-            (setq auth (concat "Authorization: " auth "\r\n")))
-        (if proxy-auth
-            (setq proxy-auth (concat "Proxy-Authorization: " proxy-auth "\r\n")))
+(when (fboundp 'url-http-create-request)
+  (if (string= "GET / HTTP/1.0
\nMIME-Version: 1.0
\nConnection: close
\nHost: example.com
\nAccept: */*
\nUser-Agent: URL/Emacs
\nContent-length: 4
\n
\ntest"
+	       (let ((url-http-target-url (url-generic-parse-url "http://example.com/"))
+		     (url-http-data "test") (url-http-version "1.0")
+		     url-http-method url-http-attempt-keepalives url-extensions-header
+		     url-http-extra-headers url-http-proxy url-mime-charset-string)
+		 (url-http-create-request)))
+      (defun url-http-create-request (&optional ref-url)
+	"Create an HTTP request for `url-http-target-url', referred to by REF-URL."
+	(declare (special proxy-info
+			  url-http-method url-http-data
+			  url-http-extra-headers))
+	(let* ((extra-headers)
+	       (request nil)
+	       (no-cache (cdr-safe (assoc "Pragma" url-http-extra-headers)))
+	       (using-proxy url-http-proxy)
+	       (proxy-auth (if (or (cdr-safe (assoc "Proxy-Authorization"
+						    url-http-extra-headers))
+				   (not using-proxy))
+			       nil
+			     (let ((url-basic-auth-storage
+				    'url-http-proxy-basic-auth-storage))
+			       (url-get-authentication url-http-target-url nil 'any nil))))
+	       (real-fname (concat (url-filename url-http-target-url)
+				   (url-recreate-url-attributes url-http-target-url)))
+	       (host (url-host url-http-target-url))
+	       (auth (if (cdr-safe (assoc "Authorization" url-http-extra-headers))
+			 nil
+		       (url-get-authentication (or
+						(and (boundp 'proxy-info)
+						     proxy-info)
+						url-http-target-url) nil 'any nil))))
+	  (if (equal "" real-fname)
+	      (setq real-fname "/"))
+	  (setq no-cache (and no-cache (string-match "no-cache" no-cache)))
+	  (if auth
+	      (setq auth (concat "Authorization: " auth "\r\n")))
+	  (if proxy-auth
+	      (setq proxy-auth (concat "Proxy-Authorization: " proxy-auth "\r\n")))
 
-        ;; Protection against stupid values in the referer
-        (if (and ref-url (stringp ref-url) (or (string= ref-url "file:nil")
-                                               (string= ref-url "")))
-            (setq ref-url nil))
+	  ;; Protection against stupid values in the referer
+	  (if (and ref-url (stringp ref-url) (or (string= ref-url "file:nil")
+						 (string= ref-url "")))
+	      (setq ref-url nil))
 
-        ;; We do not want to expose the referer if the user is paranoid.
-        (if (or (memq url-privacy-level '(low high paranoid))
-                (and (listp url-privacy-level)
-                     (memq 'lastloc url-privacy-level)))
-            (setq ref-url nil))
+	  ;; We do not want to expose the referer if the user is paranoid.
+	  (if (or (memq url-privacy-level '(low high paranoid))
+		  (and (listp url-privacy-level)
+		       (memq 'lastloc url-privacy-level)))
+	      (setq ref-url nil))
 
-        ;; url-http-extra-headers contains an assoc-list of
-        ;; header/value pairs that we need to put into the request.
-        (setq extra-headers (mapconcat
-                             (lambda (x)
-                               (concat (car x) ": " (cdr x)))
-                             url-http-extra-headers "\r\n"))
-        (if (not (equal extra-headers ""))
-            (setq extra-headers (concat extra-headers "\r\n")))
+	  ;; url-http-extra-headers contains an assoc-list of
+	  ;; header/value pairs that we need to put into the request.
+	  (setq extra-headers (mapconcat
+			       (lambda (x)
+				 (concat (car x) ": " (cdr x)))
+			       url-http-extra-headers "\r\n"))
+	  (if (not (equal extra-headers ""))
+	      (setq extra-headers (concat extra-headers "\r\n")))
 
-        ;; This was done with a call to `format'.  Concatting parts has
-        ;; the advantage of keeping the parts of each header together and
-        ;; allows us to elide null lines directly, at the cost of making
-        ;; the layout less clear.
-        (setq request
-              ;; We used to concat directly, but if one of the strings happens
-              ;; to being multibyte (even if it only contains pure ASCII) then
-              ;; every string gets converted with `string-MAKE-multibyte' which
-              ;; turns the 127-255 codes into things like latin-1 accented chars
-              ;; (it would work right if it used `string-TO-multibyte' instead).
-              ;; So to avoid the problem we force every string to be unibyte.
-              (mapconcat
-               ;; FIXME: Instead of `string-AS-unibyte' we'd want
-               ;; `string-to-unibyte', so as to properly signal an error if one
-               ;; of the strings contains a multibyte char.
-               'string-as-unibyte
-               (delq nil
-                     (list
-                      ;; The request
-                      (or url-http-method "GET") " "
-                      (if using-proxy (url-recreate-url url-http-target-url) real-fname)
-                      " HTTP/" url-http-version "\r\n"
-                      ;; Version of MIME we speak
-                      "MIME-Version: 1.0\r\n"
-                      ;; (maybe) Try to keep the connection open
-                      "Connection: " (if (or using-proxy
-                                             (not url-http-attempt-keepalives))
-                                         "close" "keep-alive") "\r\n"
-                                         ;; HTTP extensions we support
-                                         (if url-extensions-header
-                                             (format
-                                              "Extension: %s\r\n" url-extensions-header))
-                                         ;; Who we want to talk to
-                                         (if (/= (url-port url-http-target-url)
-                                                 (url-scheme-get-property
-                                                  (url-type url-http-target-url) 'default-port))
-                                             (format
-                                              "Host: %s:%d\r\n" host (url-port url-http-target-url))
-                                           (format "Host: %s\r\n" host))
-                                         ;; Who its from
-                                         (if url-personal-mail-address
-                                             (concat
-                                              "From: " url-personal-mail-address "\r\n"))
-                                         ;; Encodings we understand
-                                         (if url-mime-encoding-string
-                                             (concat
-                                              "Accept-encoding: " url-mime-encoding-string "\r\n"))
-                                         (if url-mime-charset-string
-                                             (concat
-                                              "Accept-charset: " url-mime-charset-string "\r\n"))
-                                         ;; Languages we understand
-                                         (if url-mime-language-string
-                                             (concat
-                                              "Accept-language: " url-mime-language-string "\r\n"))
-                                         ;; Types we understand
-                                         "Accept: " (or url-mime-accept-string "*/*") "\r\n"
-                                         ;; User agent
-                                         (url-http-user-agent-string)
-                                         ;; Proxy Authorization
-                                         proxy-auth
-                                         ;; Authorization
-                                         auth
-                                         ;; Cookies
-                                         (url-cookie-generate-header-lines host real-fname
-                                                                           (equal "https" (url-type url-http-target-url)))
-                                         ;; If-modified-since
-                                         (if (and (not no-cache)
-                                                  (member url-http-method '("GET" nil)))
-                                             (let ((tm (url-is-cached url-http-target-url)))
-                                               (if tm
-                                                   (concat "If-modified-since: "
-                                                           (url-get-normalized-date tm) "\r\n"))))
-                                         ;; Whence we came
-                                         (if ref-url (concat
-                                                      "Referer: " ref-url "\r\n"))
-                                         extra-headers
-                                         ;; Length of data
-                                         (if url-http-data
-                                             (concat
-                                              "Content-length: " (number-to-string
-                                                                  (length url-http-data))
-                                              "\r\n"))
-                                         ;; End request
-                                         "\r\n"
-                                         ;; Any data
-                                         url-http-data "\r\n"))
-               ""))
-        (url-http-debug "Request is: \n%s" request)
-        request)))
+	  ;; This was done with a call to `format'.  Concatting parts has
+	  ;; the advantage of keeping the parts of each header together and
+	  ;; allows us to elide null lines directly, at the cost of making
+	  ;; the layout less clear.
+	  (setq request
+		;; We used to concat directly, but if one of the strings happens
+		;; to being multibyte (even if it only contains pure ASCII) then
+		;; every string gets converted with `string-MAKE-multibyte' which
+		;; turns the 127-255 codes into things like latin-1 accented chars
+		;; (it would work right if it used `string-TO-multibyte' instead).
+		;; So to avoid the problem we force every string to be unibyte.
+		(mapconcat
+		 ;; FIXME: Instead of `string-AS-unibyte' we'd want
+		 ;; `string-to-unibyte', so as to properly signal an error if one
+		 ;; of the strings contains a multibyte char.
+		 'string-as-unibyte
+		 (delq nil
+		       (list
+			;; The request
+			(or url-http-method "GET") " "
+			(if using-proxy (url-recreate-url url-http-target-url) real-fname)
+			" HTTP/" url-http-version "\r\n"
+			;; Version of MIME we speak
+			"MIME-Version: 1.0\r\n"
+			;; (maybe) Try to keep the connection open
+			"Connection: " (if (or using-proxy
+					       (not url-http-attempt-keepalives))
+					   "close" "keep-alive") "\r\n"
+					   ;; HTTP extensions we support
+			(if url-extensions-header
+			    (format
+			     "Extension: %s\r\n" url-extensions-header))
+			;; Who we want to talk to
+			(if (/= (url-port url-http-target-url)
+				(url-scheme-get-property
+				 (url-type url-http-target-url) 'default-port))
+			    (format
+			     "Host: %s:%d\r\n" host (url-port url-http-target-url))
+			  (format "Host: %s\r\n" host))
+			;; Who its from
+			(if url-personal-mail-address
+			    (concat
+			     "From: " url-personal-mail-address "\r\n"))
+			;; Encodings we understand
+			(if url-mime-encoding-string
+			    (concat
+			     "Accept-encoding: " url-mime-encoding-string "\r\n"))
+			(if url-mime-charset-string
+			    (concat
+			     "Accept-charset: " url-mime-charset-string "\r\n"))
+			;; Languages we understand
+			(if url-mime-language-string
+			    (concat
+			     "Accept-language: " url-mime-language-string "\r\n"))
+			;; Types we understand
+			"Accept: " (or url-mime-accept-string "*/*") "\r\n"
+			;; User agent
+			(url-http-user-agent-string)
+			;; Proxy Authorization
+			proxy-auth
+			;; Authorization
+			auth
+			;; Cookies
+			(url-cookie-generate-header-lines host real-fname
+							  (equal "https" (url-type url-http-target-url)))
+			;; If-modified-since
+			(if (and (not no-cache)
+				 (member url-http-method '("GET" nil)))
+			    (let ((tm (url-is-cached url-http-target-url)))
+			      (if tm
+				  (concat "If-modified-since: "
+					  (url-get-normalized-date tm) "\r\n"))))
+			;; Whence we came
+			(if ref-url (concat
+				     "Referer: " ref-url "\r\n"))
+			extra-headers
+			;; Length of data
+			(if url-http-data
+			    (concat
+			     "Content-length: " (number-to-string
+						 (length url-http-data))
+			     "\r\n"))
+			;; End request
+			"\r\n"
+			;; Any data
+			url-http-data "\r\n"))
+		 ""))
+	  (url-http-debug "Request is: \n%s" request)
+	  request))))
 
 (unless (fboundp 'mm-url-encode-multipart-form-data)
   (defun mm-url-encode-multipart-form-data (pairs &optional boundary)
@@ -324,10 +341,26 @@
       ;; Delete any returned items that are empty
       (delq nil
             (mapcar (lambda (data)
-                      (when (car data)
-                        ;; For each pair
+
+                      (cond
+                       ((consp (car data))
+                        (let ((fieldname (cadar data))
+                              (filename  (caadar data))
+                              (mimetype  (car (caadar data)))
+                              (content   (caar (caadar data))))
+
+                          (concat
+                           ;; Encode the name
+                           "Content-Disposition: form-data; name=\"" fieldname "\"\r\n"
+                           "Content-Type: " mimetype "\r\n"
+                           "Content-Transfer-Encoding: binary\r\n\r\n"
+                           content
+                           "\r\n")))
+
+                       ((stringp (car data))
+                                ;; For each pair
+
                         (concat
-
                          ;; Encode the name
                          "Content-Disposition: form-data; name=\""
                          (car data) "\"\r\n"
@@ -339,7 +372,8 @@
                                ((integerp (cdr data))
                                 (int-to-string (cdr data))))
 
-                         "\r\n")))
+                         "\r\n"))
+                       (t (error "I don't handle this."))))
                     pairs))
       ;; use the boundary as a separator
       (concat "--" boundary "\r\n"))
@@ -351,15 +385,13 @@
 (unless (fboundp 'assoc-string)
   (defun assoc-string (key list &optional case-fold)
     (if case-fold
-	(assoc-ignore-case key list)
+        (assoc-ignore-case key list)
       (assoc key list))))
 
 (defun url-compat-retrieve (url post-process bufname callback cbargs)
   (cond ((boundp 'url-be-asynchronous) ; Sniff w3 lib capability
 	 (if callback
-	     (setq url-be-asynchronous t
-		   url-current-callback-data cbargs
-		   url-current-callback-func callback)
+	     (setq url-be-asynchronous t)
 	   (setq url-be-asynchronous nil))
 	 (url-retrieve url t)
 	 (when (not url-be-asynchronous)
@@ -393,8 +425,7 @@
 
   (let* ((url-request-extra-headers
           (if headers headers
-            (if url-request-extra-headers url-request-extra-headers
-              (cons nil nil))))
+            (when url-request-extra-headers url-request-extra-headers)))
          (boundary (int-to-string (random)))
          (cs 'utf-8)
          (content-type
@@ -430,12 +461,12 @@
   (declare (special url-http-end-of-headers))
   (let ((kill-this-buffer (current-buffer)))
     (when (and (integerp status) (not (< status 300)))
-      (kill-buffer kill-this-buffer)
+      (mediawiki-debug kill-this-buffer "url-http-response-post-process:1")
       (error "Oops! Invalid status: %d" status))
 
     (when (or (not (boundp 'url-http-end-of-headers))
               (not url-http-end-of-headers))
-      (kill-buffer kill-this-buffer)
+      (mediawiki-debug kill-this-buffer "url-http-response-post-process:2")
       (error "Oops! Don't see end of headers!"))
 
     ;; FIXME: need to limit redirects
@@ -451,7 +482,7 @@
       (let ((str (decode-coding-string
                   (buffer-substring-no-properties (point) (point-max))
                   'utf-8)))
-        (kill-buffer (current-buffer))
+        (mediawiki-debug (current-buffer) "url-http-response-post-process:3")
         (when bufname
           (set-buffer bufname)
           (insert str)
@@ -475,6 +506,12 @@
   :tag "MediaWiki Site Default"
   :group 'mediawiki)
 
+(defcustom mediawiki-debug nil
+  "Turn on debugging (non-nil)"
+  :type 'boolean
+  :tag "MediaWiki Debugging"
+  :group 'mediawiki)
+
 (defcustom mediawiki-site-alist '(("Wikipedia"
                                    "http://en.wikipedia.org/w/"
                                    "username"
@@ -498,6 +535,9 @@
   :type 'hook
   :group 'mediawiki)
 
+(defvar mediawiki-page-history '()
+  "Assoc list of visited pages on this MW site.")
+
 (defvar mediawiki-enumerate-with-terminate-paragraph nil
 "*Before insert enumerate/itemize do \\[mediawiki-terminate-paragraph].")
 
@@ -864,17 +904,33 @@
 
 (defvar mediawiki-draft-mode-map ())
 
+(defun mediawiki-debug (buffer function)
+  "When debugging is turned on, log the name of the buffer so it
+can be examined. If debugging is off, just kill the buffer.  This
+allows you to see what is being sent to and from the server."
+  (if (not mediawiki-debug)
+      (kill-buffer buffer)
+    (message "Examine the '%s' buffer called from '%s'."
+             (buffer-name buffer)
+             function)))
+
+(defun mediawiki-translate-pagename (name)
+  "Given NAME, returns the typical name that MediaWiki would use.
+Right now, this only means replacing \"_\" with \" \"."
+  (if (not name)
+      "Main Page"
+    (mapconcat 'identity (split-string name "_" t) " ")))
+
 (defun mediawiki-make-api-url (&optional sitename)
   (format (concat (mediawiki-site-url (or sitename mediawiki-site))
                   "api.php")))
 
 (defun mediawiki-api-call (sitename action args)
   (let* ((raw (url-http-post (mediawiki-make-api-url sitename)
-;;               (concat (mediawiki-make-api-url sitename) "?"
-;;                       (mm-url-encode-www-form-urlencoded
                         (delq nil
                               (append args (list (cons "format" "xml")
-                                                 (cons "action" action))))))
+                                                 (cons "action" action))))
+                        (string= action "upload")))
          (result (assoc 'api
                             (with-temp-buffer
                               (insert raw)
@@ -902,12 +958,15 @@
                   (if action
                       mediawiki-argument-pattern
                     "?title=%s"))
-	  (mm-url-form-encode-xwfu title)
+	  (mm-url-form-encode-xwfu
+           (mediawiki-translate-pagename title))
 	  action))
 
 (defun mediawiki-open (name)
   "Open a wiki page specified by NAME from the mediawiki engine"
-  (interactive "sWiki Page: ")
+  (interactive
+   (let ((hist (cdr (assoc-string mediawiki-site mediawiki-page-history))))
+     (list (read-string "Wiki Page: " nil 'hist))))
   (when (or (not (stringp name))
             (string-equal "" name))
     (error "Need to specify a name"))
@@ -920,25 +979,38 @@
 	(mediawiki-open title)
       (error "Error: %s is not a mediawiki document" (buffer-name)))))
 
+(defun mediawiki-add-page-history (site title)
+  (let ((hist (cdr (assoc-string site mediawiki-page-history))))
+    (unless hist
+      (add-to-list 'mediawiki-page-history (cons site "")))
+    (setcdr (assoc-string site mediawiki-page-history) (append (list title) hist))))
+
 (defun mediawiki-edit (site title)
   "Edit wiki file with the name of title"
   (when (not (ring-p mediawiki-page-ring))
     (setq mediawiki-page-ring (make-ring 30)))
 
-  (with-current-buffer (get-buffer-create
-                        (concat site ": " title))
-    (ring-insert mediawiki-page-ring (current-buffer))
-    (delete-region (point-min) (point-max))
-    (mediawiki-mode)
-    (set-buffer-file-coding-system 'utf-8)
-    (insert (or (mediawiki-get site title) ""))
+  (let ((pagetitle (mediawiki-translate-pagename title)))
 
-    (set-buffer-modified-p nil)
-    (setq buffer-undo-list t)
-    (buffer-enable-undo)
-    (mediawiki-pop-to-buffer (current-buffer))
-    (setq mediawiki-page-title title)
-    (goto-char (point-min))))
+    (mediawiki-add-page-history site title)
+    (with-current-buffer (get-buffer-create
+                          (concat site ": " pagetitle))
+      (unless (mediawiki-logged-in-p site)
+        (mediawiki-do-login site)
+        (setq mediawiki-site site))
+      (ring-insert mediawiki-page-ring (current-buffer))
+      (delete-region (point-min) (point-max))
+      (mediawiki-mode)
+      (set-buffer-file-coding-system 'utf-8)
+      (insert (or (mediawiki-get site pagetitle) ""))
+
+      (set-buffer-modified-p nil)
+      (setq buffer-undo-list t)
+      (buffer-enable-undo)
+      (mediawiki-pop-to-buffer (current-buffer))
+      (setq mediawiki-page-title pagetitle)
+      (goto-char (point-min))
+      (current-buffer))))
 
 (defun mediawiki-get-edit-form-vars (str bufname)
   "Extract the form variables from a page.  This should only be
@@ -997,9 +1069,14 @@
                form start)))
       vars)))
 
-(defun mediawiki-logged-in-p ()
-  "Returns t if we are logged in already."
-  (not (eq nil mediawiki-site)))         ; FIXME should check cookies
+(defun mediawiki-logged-in-p (&optional site)
+  "Returns t if we are we have cookies for the site."
+  (let ((urlobj (url-generic-parse-url
+                 (mediawiki-site-url (or site mediawiki-site)))))
+    (url-cookie-retrieve
+     (url-host urlobj)
+     (url-filename urlobj)
+     (equal "https" (url-type urlobj)))))
 
 (defun mediawiki-pop-to-buffer (bufname)
   "Pop to buffer and then execute a hook."
@@ -1040,13 +1117,15 @@
      (t rev))))
 
 (defun mediawiki-pagelist-find-page (pagelist title)
-  "Extract a page from a pagelist returned by mediawiki"
+  "Extract a page TITLE from a PAGELIST returned by mediawiki"
   (let ((pl (cddr (assq 'pages pagelist)))
         page current)
     (while (and (not page)
                 (setq current (pop pl)))
+      ;; This fails when underbars are here instead of spaces,
+      ;; so we make sure that it has the mediawiki pagename
       (when (string= (mediawiki-page-get-title current)
-                     title)
+                     (mediawiki-translate-pagename title))
         (setq page current)))
     page))
 
@@ -1086,6 +1165,42 @@
        (buffer-substring-no-properties (point-min) (point-max)))
     (error "Error: %s is not a mediawiki document" (buffer-name))))
 
+(defun mediawiki-prompt-for-page ()
+    (let* ((prompt (concat "Page"
+                         (when mediawiki-page-title
+                           (format " (default %s)" mediawiki-page-title))
+                         ": "))
+         (answer (completing-read prompt '())))
+    (if (string= "" answer)
+        mediawiki-page-title
+      answer)))
+
+(defun mediawiki-prompt-for-summary ()
+    (completing-read  "Summary: " '()))
+
+(defun mediawiki-save-on (&optional site name summary)
+  (interactive)
+  (when (not site)
+    (setq site (mediawiki-prompt-for-site)))
+  (when (not name)
+    (setq name (mediawiki-translate-pagename (mediawiki-prompt-for-page))))
+  (when (not summary)
+    (setq summary (mediawiki-prompt-for-summary)))
+
+  (setq mediawiki-site (mediawiki-do-login site))
+  (mediawiki-get mediawiki-site name)
+  (mediawiki-save-as name summary))
+
+(defun mediawiki-save-as (&optional name summary)
+  (interactive "sSave As: \nsSummary: ")
+  (if name
+      (mediawiki-save-page
+       mediawiki-site
+       name
+       summary
+       (buffer-substring-no-properties (point-min) (point-max)))
+    (error "Error: %s is not a mediawiki document" (buffer-name))))
+
 (defun mediawiki-save-and-bury (&optional summary)
   (interactive "sSummary: ")
   (mediawiki-save summary)
@@ -1147,7 +1262,8 @@
                    (append
                     args (list (cons "lgtoken"
                                      (cdr (assq 'token result)))))))))
-    result))
+    (when (string= "Success" (cdr (assoc 'result result)))
+      sitename)))
 
 (defun mediawiki-do-logout (&optional sitename)
   (interactive)
@@ -1160,15 +1276,25 @@
 (defun mediawiki-save-page (site title summary content)
   "Save the current page to a MediaWiki wiki."
   ;; FIXME error checking, conflicts!
-  (mediawiki-api-call site "edit" (list (cons "title" title)
-                                        (cons "text" content)
-                                        (cons "summary" summary)
-                                        (cons "token" mediawiki-edittoken)
-                                        (cons "basetimestamp"
-                                              (or mediawiki-basetimestamp ""))
-                                        (cons "starttimestamp"
-                                              (or mediawiki-starttimestamp ""))))
-  (set-buffer-modified-p nil))
+  (if (not mediawiki-edittoken)
+      (error "Need an edit token!")
+    (mediawiki-api-call site "edit" (list (cons "title"
+                                                (mediawiki-translate-pagename title))
+                                          (cons "text" content)
+                                          (cons "summary" summary)
+                                          (cons "token" mediawiki-edittoken)
+                                          (cons "basetimestamp"
+                                                (or mediawiki-basetimestamp ""))
+                                          (cons "starttimestamp"
+                                                (or mediawiki-starttimestamp ""))))
+    (set-buffer-modified-p nil)))
+
+;; (cdr (assoc 'edittoken (cadr (caddr (caddr (mediawiki-api-call "mw-svn" "query"
+;;                                                                (list '("prop" . "info")
+;;                                                                      '("intoken" . "edit")
+;;                                                                      '("titles" . (concat "File:" filename)))))))))
+;
+;(mediawiki-api-call "mw-svn" "upload" (list '("filename" . "info.exe") '("file" . "edit") '("token" . token)))
 
 (defun mediawiki-browse (&optional buf)
   "Open the buffer BUF in a browser. If BUF is not given,
@@ -1195,8 +1321,7 @@
     (setq site (mediawiki-prompt-for-site)))
   (when (or (eq nil mediawiki-site)
             (not (string-equal site mediawiki-site)))
-    (mediawiki-do-login site)
-    (setq mediawiki-site site))
+    (setq mediawiki-site (mediawiki-do-login site)))
   (mediawiki-edit site (mediawiki-site-first-page site)))
 
 (defun mediawiki-open-page-at-point ()
@@ -1747,7 +1872,7 @@
       (narrow-to-region b e)
       (run-hook-with-args-until-success 'mediawiki-draft-handler-functions)
     (when (equal mediawiki-draft-buffer (buffer-name))
-      (kill-buffer (current-buffer))
+      (mediawiki-debug (current-buffer) "mediawiki-draft-region")
       (jump-to-register mediawiki-draft-register)))))
 
 ;;;###autoload
@@ -1825,7 +1950,7 @@
       (with-temp-buffer
 	(insert (concat "\n\n" mediawiki-draft-leader-text)
 		(insert-register mediawiki-draft-reply-register 1)
-		(insert (concat " " (current-time-string) " " 
+		(insert (concat " " (current-time-string) " "
 				mediawiki-draft-leader-text  "\n\n\f\n\n"
 				text "\n\f\n"))
 		(if (not (bolp))
@@ -1840,7 +1965,7 @@
 		  (append-to-file (point-min) (point-max)
 				  mediawiki-draft-data-file)))))
     (when (equal mediawiki-draft-buffer (buffer-name))
-      (kill-buffer (current-buffer)))
+      (mediawiki-debug (current-buffer) "mediawiki-draft-send"))
     (switch-to-buffer target-buffer)))
 
 (define-derived-mode mediawiki-draft-mode text-mode "MW-Draft"
@@ -1974,7 +2099,7 @@
 
     (define-key mediawiki-mode-map "\C-c\C-q" 'mediawiki-unfill-article)
     (define-key mediawiki-mode-map "\C-c\M-q" 'mediawiki-fill-article)
-    (define-key mediawiki-mode-map "\M-u" 'mediawiki-unfill-paragraph-or-region)
+    (define-key mediawiki-mode-map "\C-c\M-u" 'mediawiki-unfill-paragraph-or-region)
     (define-key mediawiki-mode-map "\C-c\C-u" 'mediawiki-unfill-paragraph-simple)
     (define-key mediawiki-mode-map "\C-c\C-f\C-s" 'mediawiki-insert-strong-emphasis)
     (define-key mediawiki-mode-map "\C-c\C-f\C-b" 'mediawiki-insert-bold)
@@ -2009,8 +2134,8 @@
     (define-key mediawiki-mode-map "\C-\\" 'mediawiki-insert-itemize)
     (define-key mediawiki-mode-map [(control return)] 'mediawiki-insert-itemize)
     (define-key mediawiki-mode-map "\C-ca" 'auto-capitalize-mode)
-    (define-key mediawiki-mode-map "\C-ci" 'set-input-method)
-    (define-key mediawiki-mode-map "\C-ct" 'toggle-input-method)
+;    (define-key mediawiki-mode-map "\C-ci" 'set-input-method)
+;    (define-key mediawiki-mode-map "\C-ct" 'toggle-input-method)
 
     (define-key mediawiki-mode-map [(backtab)] 'mediawiki-goto-prev-link)
     (define-key mediawiki-mode-map [(tab)]     'mediawiki-goto-next-link)