1 ;;; xml-rpc.el --- An elisp implementation of clientside XML-RPC
3 ;; Copyright (C) 2002-2010 Mark A. Hershberger
4 ;; Copyright (C) 2001 CodeFactory AB.
5 ;; Copyright (C) 2001 Daniel Lundin.
6 ;; Copyright (C) 2006 Shun-ichi Goto
7 ;; Modified for non-ASCII character handling.
9 ;; Author: Mark A. Hershberger <mah@everybody.org>
10 ;; Original Author: Daniel Lundin <daniel@codefactory.se>
12 ;; Created: May 13 2001
13 ;; Keywords: xml rpc network
14 ;; URL: http://launchpad.net/xml-rpc-el
15 ;; Last Modified: <2010-02-27 07:02:36 mah>
17 (defconst xml-rpc-version "1.6.8"
18 "Current version of xml-rpc.el")
20 ;; This file is NOT (yet) part of GNU Emacs.
22 ;; This program is free software: you can redistribute it and/or modify
23 ;; it under the terms of the GNU General Public License as published by
24 ;; the Free Software Foundation, either version 3 of the License, or
25 ;; (at your option) any later version.
27 ;; This program is distributed in the hope that it will be useful,
28 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
29 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 ;; GNU General Public License for more details.
32 ;; You should have received a copy of the GNU General Public License
33 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
37 ;; This is an XML-RPC client implementation in elisp, capable of both
38 ;; synchronous and asynchronous method calls (using the url package's async
39 ;; retrieval functionality).
40 ;; XML-RPC is remote procedure calls over HTTP using XML to describe the
41 ;; function call and return values.
43 ;; xml-rpc.el represents XML-RPC datatypes as lisp values, automatically
44 ;; converting to and from the XML datastructures as needed, both for method
45 ;; parameters and return values, making using XML-RPC methods fairly
46 ;; transparent to the lisp code.
50 ;; If you use ELPA (http://tromey.com/elpa), you can install via the
51 ;; M-x package-list-packages interface. This is preferrable as you
52 ;; will have access to updates automatically.
54 ;; Otherwise, just make sure this file in your load-path (usually
55 ;; ~/.emacs.d is included) and put (require 'xml-rpc) in your
56 ;; ~/.emacs or ~/.emacs.d/init.el file.
60 ;; xml-rpc.el uses the url package for http handling and xml.el for
61 ;; XML parsing. url is a part of the W3 browser package. The url
62 ;; package that is part of Emacs 22+ works great.
64 ;; xml.el is a part of GNU Emacs 21, but can also be downloaded from
65 ;; here: <URL:ftp://ftp.codefactory.se/pub/people/daniel/elisp/xml.el>
69 ;; Please use M-x xml-rpc-submit-bug-report to report bugs.
71 ;;; XML-RPC datatypes are represented as follows
76 ;; array: '(1 2 3 4) '(1 2 3 (4.1 4.2))
77 ;; struct: '(("name" . "daniel") ("height" . 6.1))
78 ;; dateTime: (:datetime (1234 124))
83 ;; Here follows some examples demonstrating the use of xml-rpc.el
85 ;; Normal synchronous operation
86 ;; ----------------------------
88 ;; (xml-rpc-method-call "http://localhost:80/RPC" 'foo-method foo bar zoo)
90 ;; Asynchronous example (cb-foo will be called when the methods returns)
91 ;; ---------------------------------------------------------------------
93 ;; (defun cb-foo (foo)
94 ;; (print (format "%s" foo)))
96 ;; (xml-rpc-method-call-async 'cb-foo "http://localhost:80/RPC"
97 ;; 'foo-method foo bar zoo)
100 ;; Some real world working examples for fun and play
101 ;; -------------------------------------------------
103 ;; Check the temperature (celsius) outside jonas@codefactory.se's apartment
105 ;; (xml-rpc-method-call
106 ;; "http://flint.bengburken.net:80/xmlrpc/onewire_temp.php"
110 ;; Fetch the latest NetBSD news the past 5 days from O'reillynet
112 ;; (xml-rpc-method-call "http://www.oreillynet.com/meerkat/xml-rpc/server.php"
114 ;; '(("channel" . 1024)
115 ;; ("search" . "/NetBSD/")
116 ;; ("time_period" . "5DAY")
118 ;; ("descriptions" . 200)
119 ;; ("categories" . 0)
122 ;; ("num_items" . 5)))
127 ;; 1.6.8 - Add a report-xml-rpc-bug function
129 ;; 1.6.7 - Skipped version
131 ;; 1.6.6 - Use the correct dateTime elements. Fix bug in parsing null int.
133 ;; 1.6.5.1 - Fix compile time warnings.
135 ;; 1.6.5 - Made handling of dateTime elements more robust.
137 ;; 1.6.4.1 - Updated to work with both Emacs22 and Emacs23.
139 ;; 1.6.2.2 - Modified to allow non-ASCII string again.
140 ;; It can handle non-ASCII page name and comment
143 ;; 1.6.2.1 - Modified to allow non-ASCII string.
144 ;; If xml-rpc-allow-unicode-string is non-nil,
145 ;; make 'value' object instead of 'base64' object.
146 ;; This is good for WikiRPC.
148 ;; 1.6.2 - Fix whitespace issues to work better with new xml.el
149 ;; Fix bug in string handling.
150 ;; Add support for gzip-encoding when needed.
152 ;; 1.6.1 - base64 support added.
153 ;; url-insert-entities-in-string done on string types now.
155 ;; 1.6 - Fixed dependencies (remove w3, add cl).
156 ;; Move string-to-boolean and boolean-to-string into xml-rpc
158 ;; Fix bug in xml-rpc-xml-to-response where non-existent var was.
159 ;; More tweaking of "Connection: close" header.
160 ;; Fix bug in xml-rpc-request-process-buffer so that this works with
161 ;; different mixes of the url.el code.
163 ;; 1.5.1 - Added Andrew J Cosgriff's patch to make the
164 ;; xml-rpc-clean-string function work in XEmacs.
166 ;; 1.5 - Added headers to the outgoing url-retreive-synchronously
167 ;; so that it would close connections immediately on completion.
169 ;; 1.4 - Added conditional debugging code. Added version tag.
171 ;; 1.2 - Better error handling. The documentation didn't match
172 ;; the code. That was changed so that an error was
173 ;; signaled. Also, better handling of various and
174 ;; different combinations of xml.el and url.el.
176 ;; 1.1 - Added support for boolean types. If the type of a
177 ;; returned value is not specified, string is assumed
179 ;; 1.0 - First version
190 (defconst xml-rpc-maintainer-address "mah@everybody.org"
191 "The address where bug reports should be sent.")
193 (defcustom xml-rpc-load-hook nil
194 "*Hook run after loading xml-rpc."
195 :type 'hook :group 'xml-rpc)
197 (defcustom xml-rpc-use-coding-system
198 (if (coding-system-p 'utf-8) 'utf-8 'iso-8859-1)
199 "The coding system to use."
200 :type 'symbol :group 'xml-rpc)
202 (defcustom xml-rpc-allow-unicode-string (coding-system-p 'utf-8)
203 "If non-nil, non-ASCII data is composed as 'value' instead of 'base64'.
204 And this option overrides `xml-rpc-base64-encode-unicode' and
205 `xml-rpc-base64-decode-unicode' if set as non-nil."
206 :type 'boolean :group 'xml-rpc)
208 (defcustom xml-rpc-base64-encode-unicode (coding-system-p 'utf-8)
209 "If non-nil, then strings with non-ascii characters will be turned
211 :type 'boolean :group 'xml-rpc)
213 (defcustom xml-rpc-base64-decode-unicode (coding-system-p 'utf-8)
214 "If non-nil, then base64 strings will be decoded using the
215 utf-8 coding system."
216 :type 'boolean :group 'xml-rpc)
218 (defcustom xml-rpc-debug 0
219 "Set this to 1 or greater to avoid killing temporary buffers.
220 Set it higher to get some info in the *Messages* buffer"
221 :type 'integerp :group 'xml-rpc)
223 (defvar xml-rpc-fault-string nil
224 "Contains the fault string if a fault is returned")
226 (defvar xml-rpc-fault-code nil
227 "Contains the fault code if a fault is returned")
230 ;; Value type handling functions
233 (defsubst xml-rpc-value-intp (value)
234 "Return t if VALUE is an integer."
237 (defsubst xml-rpc-value-doublep (value)
238 "Return t if VALUE is a double precision number."
241 (defsubst xml-rpc-value-stringp (value)
242 "Return t if VALUE is a string."
245 ;; An XML-RPC struct is a list where every car is cons or a list of
246 ;; length 1 or 2 and has a string for car.
247 (defsubst xml-rpc-value-structp (value)
248 "Return t if VALUE is an XML-RPC struct."
253 (while (and vals result)
255 (setq curval (car-safe vals))
257 (stringp (car-safe curval))))
258 (setq vals (cdr-safe vals)))
261 ;; A somewhat lazy predicate for arrays
262 (defsubst xml-rpc-value-arrayp (value)
263 "Return t if VALUE is an XML-RPC struct."
265 (not (xml-rpc-value-datetimep value))
266 (not (xml-rpc-value-structp value))))
268 (defun xml-rpc-submit-bug-report ()
269 "Submit a bug report on xml-rpc."
272 (let ((xml-rpc-tz-pd-defined-in
273 (if (fboundp 'find-lisp-object-file-name)
274 (find-lisp-object-file-name
275 'timezone-parse-date (symbol-function 'timezone-parse-date))
276 (symbol-file 'timezone-parse-date)))
277 (date-parses-as (timezone-parse-date "20091130T00:52:53")))
278 (reporter-submit-bug-report
279 xml-rpc-maintainer-address
280 (concat "xml-rpc.el " xml-rpc-version)
281 (list 'xml-rpc-tz-pd-defined-in
284 'xml-rpc-use-coding-system
285 'xml-rpc-allow-unicode-string
286 'xml-rpc-base64-encode-unicode
287 'xml-rpc-base64-decode-unicode))))
289 (defun xml-rpc-value-booleanp (value)
290 "Return t if VALUE is a boolean."
294 (defun xml-rpc-value-datetimep (value)
295 "Return t if VALUE is a datetime. For Emacs XML-RPC
296 implementation, you must put time keyword :datetime before the
297 time, or it will be confused for a list."
299 (eq (car value) :datetime)))
301 (defun xml-rpc-string-to-boolean (value)
302 "Return t if VALUE is a boolean"
303 (or (string-equal value "true") (string-equal value "1")))
305 (defun xml-rpc-caddar-safe (list)
306 (car-safe (cdr-safe (cdr-safe (car-safe list)))))
308 (defun xml-rpc-xml-list-to-value (xml-list)
309 "Convert an XML-RPC structure in an xml.el style XML-LIST to an elisp list, \
310 interpreting and simplifying it while retaining its structure."
311 (let (valtype valvalue)
313 ((and (xml-rpc-caddar-safe xml-list)
314 (listp (car-safe (cdr-safe (cdr-safe (car-safe xml-list))))))
316 (setq valtype (car (caddar xml-list))
317 valvalue (caddr (caddar xml-list)))
320 ((eq valtype 'base64)
321 (if xml-rpc-base64-decode-unicode
322 (decode-coding-string (base64-decode-string valvalue) 'utf-8)
323 (base64-decode-string valvalue)))
325 ((eq valtype 'boolean)
326 (xml-rpc-string-to-boolean valvalue))
328 ((eq valtype 'string)
331 ((or (eq valtype 'int) (eq valtype 'i4))
332 (string-to-number (or valvalue "0")))
334 ((eq valtype 'double)
335 (string-to-number valvalue))
337 ((eq valtype 'struct)
338 (mapcar (lambda (member)
339 (let ((membername (cadr (cdaddr member)))
340 (membervalue (xml-rpc-xml-list-to-value
342 (cons membername membervalue)))
343 (cddr (caddar xml-list))))
346 (let* ((struct (xml-rpc-xml-list-to-value (list valvalue)))
347 (fault-string (cdr (assoc "faultString" struct)))
348 (fault-code (cdr (assoc "faultCode" struct))))
349 (list 'fault fault-code fault-string)))
351 ((or (eq valtype 'dateTime.iso8601)
352 (eq valtype 'dateTime))
353 (list :datetime (date-to-time valvalue)))
356 (mapcar (lambda (arrval)
357 (xml-rpc-xml-list-to-value (list arrval)))
359 ((xml-rpc-caddar-safe xml-list)))))
361 (defun xml-rpc-boolean-to-string (value)
362 "Convert a boolean value to a string"
367 (defun xml-rpc-datetime-to-string (value)
368 "Convert a date time to a valid XML-RPC date"
369 (format-time-string "%Y%m%dT%H:%M:%S" (cadr value)))
371 (defun xml-rpc-value-to-xml-list (value)
372 "Return XML representation of VALUE properly formatted for use with the \
373 functions in xml.el."
377 ((xml-rpc-value-booleanp value)
378 `((value nil (boolean nil ,(xml-rpc-boolean-to-string value)))))
380 ((xml-rpc-value-datetimep value)
381 `((value nil (dateTime.iso8601 nil ,(xml-rpc-datetime-to-string value)))))
383 ((xml-rpc-value-arrayp value)
386 (while (setq xmlval (xml-rpc-value-to-xml-list (car value))
387 result (if result (append result xmlval)
390 `((value nil (array nil ,(append '(data nil) result))))))
392 ((xml-rpc-value-structp value)
395 (while (setq xmlval `((member nil (name nil ,(caar value))
396 ,(car (xml-rpc-value-to-xml-list
398 result (append result xmlval)
400 `((value nil ,(append '(struct nil) result)))))
402 ((xml-rpc-value-intp value)
403 `((value nil (int nil ,(int-to-string value)))))
404 ((xml-rpc-value-stringp value)
405 (let ((charset-list (find-charset-string value)))
406 (if (or xml-rpc-allow-unicode-string
407 (and (eq 1 (length charset-list))
408 (eq 'ascii (car charset-list)))
409 (not xml-rpc-base64-encode-unicode))
410 `((value nil (string nil ,value)))
411 `((value nil (base64 nil ,(if xml-rpc-base64-encode-unicode
412 (base64-encode-string
413 (encode-coding-string
414 value xml-rpc-use-coding-system))
415 (base64-encode-string value))))))))
416 ((xml-rpc-value-doublep value)
417 `((value nil (double nil ,(number-to-string value)))))
419 `((value nil (base64 nil ,(base64-encode-string value)))))))
421 (defun xml-rpc-xml-to-string (xml)
422 "Return a string representation of the XML tree as valid XML markup."
423 (let ((tree (xml-node-children xml))
424 (result (concat "<" (symbol-name (xml-node-name xml)) ">")))
428 (setq result (concat result (xml-rpc-xml-to-string (car tree)))))
429 ((stringp (car tree))
430 (setq result (concat result (car tree))))
432 (error "Invalid XML tree")))
433 (setq tree (cdr tree)))
434 (setq result (concat result "</" (symbol-name (xml-node-name xml)) ">"))
441 (defsubst xml-rpc-response-errorp (response)
442 "An 'xml-rpc-method-call' result value is always a list, where the first \
443 element in RESPONSE is either nil or if an error occured, a cons pair \
444 according to (errnum . \"Error string\"),"
445 (eq 'fault (car-safe (caddar response))))
447 (defsubst xml-rpc-response-error-code (response)
448 "Return the error code from RESPONSE."
449 (and (xml-rpc-response-errorp response)
450 (nth 1 (xml-rpc-xml-list-to-value response))))
452 (defsubst xml-rpc-response-error-string (response)
453 "Return the error code from RESPONSE."
454 (and (xml-rpc-response-errorp response)
455 (nth 2 (xml-rpc-xml-list-to-value response))))
457 (defun xml-rpc-xml-to-response (xml)
458 "Convert an XML list to a method response list. An error is
459 signaled if there is a fault or if the response does not appear
460 to be an XML-RPC response (i.e. no methodResponse). Otherwise,
461 the parsed XML response is returned."
462 ;; Check if we have a methodResponse
464 ((not (eq (car-safe (car-safe xml)) 'methodResponse))
465 (error "No methodResponse found"))
467 ;; Did we get a fault response
468 ((xml-rpc-response-errorp xml)
469 (let ((resp (xml-rpc-xml-list-to-value xml)))
470 (setq xml-rpc-fault-string (nth 2 resp))
471 (setq xml-rpc-fault-code (nth 1 resp))
472 (error "XML-RPC fault `%s'" xml-rpc-fault-string)))
474 ;; Interpret the XML list and produce a more useful data structure
476 (let ((valpart (cdr (cdaddr (caddar xml)))))
477 (xml-rpc-xml-list-to-value valpart)))))
483 (defun xml-rpc-request (server-url xml &optional async-callback-function)
484 "Perform http post request to SERVER-URL using XML.
486 If ASYNC-CALLBACK-FUNCTION is non-nil, the request will be performed
487 asynchronously and ASYNC-CALLBACK-FUNCTION should be a callback function to
488 be called when the reuest is finished. ASYNC-CALLBACK-FUNCTION is called with
489 a single argument being an xml.el style XML list.
491 It returns an XML list containing the method response from the XML-RPC server,
492 or nil if called with ASYNC-CALLBACK-FUNCTION."
493 (declare (special url-current-callback-data
494 url-current-callback-func
495 url-http-response-status))
498 (let ((url-request-method "POST")
499 (url-package-name "xml-rpc.el")
500 (url-package-version xml-rpc-version)
501 (url-request-data (concat "<?xml version=\"1.0\""
502 " encoding=\"UTF-8\"?>\n"
505 (when xml-rpc-allow-unicode-string
506 (encode-coding-region
507 (point-min) (point-max) 'utf-8))
510 (url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
511 (url-request-coding-system xml-rpc-use-coding-system)
512 (url-http-attempt-keepalives t)
513 (url-request-extra-headers (list
514 (cons "Connection" "keep-alive")
516 "text/xml; charset=utf-8"))))
517 (when (> xml-rpc-debug 1)
518 (print url-request-data (create-file-buffer "request-data")))
520 (cond ((boundp 'url-be-asynchronous) ; Sniff for w3 lib capability
521 (if async-callback-function
522 (setq url-be-asynchronous t
523 url-current-callback-data (list
524 async-callback-function
526 url-current-callback-func
527 'xml-rpc-request-callback-handler)
528 (setq url-be-asynchronous nil))
529 (url-retrieve server-url t)
531 (when (not url-be-asynchronous)
532 (let ((result (xml-rpc-request-process-buffer
534 (when (> xml-rpc-debug 1)
535 (with-current-buffer (create-file-buffer "result-data")
538 (t ; Post emacs20 w3-el
539 (if async-callback-function
540 (url-retrieve server-url async-callback-function)
541 (let ((buffer (url-retrieve-synchronously server-url))
543 (with-current-buffer buffer
544 (when (not (numberp url-http-response-status))
545 ;; this error may occur when keep-alive bug
546 ;; of url-http.el is not cleared.
547 (error "Why? url-http-response-status is %s"
548 url-http-response-status))
549 (when (> url-http-response-status 299)
550 (error "Error during request: %s"
551 url-http-response-status)))
552 (xml-rpc-request-process-buffer buffer)))))))))
555 (defun xml-rpc-clean-string (s)
556 (if (string-match "\\`[ \t\n\r]*\\'" s)
561 (defun xml-rpc-clean (l)
573 ;; a string, so clean it.
575 (let ((tmp (xml-rpc-clean-string elem)))
576 (when (and tmp xml-rpc-allow-unicode-string)
577 (setq tmp (decode-coding-string tmp xml-rpc-use-coding-system)))
579 (setq result (append result (list tmp)))
581 ;; a list, so recurse.
583 (setq result (append result (list (xml-rpc-clean elem)))))
585 ;; everthing else, as is.
587 (setq result (append result (list elem))))))
590 ((stringp l) ; will returning nil be acceptable ?
595 (defun xml-rpc-request-process-buffer (xml-buffer)
596 "Process buffer XML-BUFFER."
598 (with-current-buffer xml-buffer
599 (when (fboundp 'url-uncompress)
600 (let ((url-working-buffer xml-buffer))
602 (goto-char (point-min))
603 (search-forward-regexp "<\\?xml" nil t)
605 ;; Gather the results
606 (let* ((status (if (boundp 'url-http-response-status)
607 ;; Old URL lib doesn't save the result.
608 url-http-response-status 200))
610 ;; A probable XML response
611 ((looking-at "<\\?xml ")
612 (xml-rpc-clean (xml-parse-region (point-min)
615 ;; No HTTP status returned
618 (search-forward "\n---- Error was: ----\n")))
620 (buffer-substring errstart (point-max)))))
622 ;; Maybe they just gave us an the XML w/o PI?
623 ((search-forward "<methodResponse>" nil t)
624 (xml-rpc-clean (xml-parse-region (match-beginning 0)
629 (int-to-string status)))))
630 (when (< xml-rpc-debug 3)
631 (kill-buffer (current-buffer)))
635 (defun xml-rpc-request-callback-handler (callback-fun xml-buffer)
636 "Marshall a callback function request to CALLBACK-FUN with the results \
637 handled from XML-BUFFER."
638 (let ((xml-response (xml-rpc-request-process-buffer xml-buffer)))
639 (when (< xml-rpc-debug 1)
640 (kill-buffer xml-buffer))
641 (funcall callback-fun (xml-rpc-xml-to-response xml-response))))
644 (defun xml-rpc-method-call-async (async-callback-func server-url method
646 "Call an XML-RPC method asynchronously at SERVER-URL named METHOD with \
647 PARAMS as parameters. When the method returns, ASYNC-CALLBACK-FUNC will be \
648 called with the result as parameter."
649 (let* ((m-name (if (stringp method)
651 (symbol-name method)))
652 (m-params (mapcar '(lambda (p)
653 `(param nil ,(car (xml-rpc-value-to-xml-list
655 (if async-callback-func
658 (m-func-call `((methodCall nil (methodName nil ,m-name)
659 ,(append '(params nil) m-params)))))
660 (when (> xml-rpc-debug 1)
661 (print m-func-call (create-file-buffer "func-call")))
662 (xml-rpc-request server-url m-func-call async-callback-func)))
664 (defun xml-rpc-method-call (server-url method &rest params)
665 "Call an XML-RPC method at SERVER-URL named METHOD with PARAMS as \
668 (xml-rpc-method-call-async nil server-url method params)))
669 (cond ((stringp response)
670 (list (cons nil (concat "URL/HTTP Error: " response))))
672 (xml-rpc-xml-to-response response)))))
674 (unless (fboundp 'xml-escape-string)
675 (defun xml-debug-print (xml &optional indent-string)
676 "Outputs the XML in the current buffer.
677 XML can be a tree or a list of nodes.
678 The first line is indented with the optional INDENT-STRING."
679 (setq indent-string (or indent-string ""))
681 (xml-debug-print-internal node indent-string)))
683 (defalias 'xml-print 'xml-debug-print)
685 (when (not (boundp 'xml-entity-alist))
686 (defvar xml-entity-alist
693 (defun xml-escape-string (string)
694 "Return the string with entity substitutions made from
696 (mapconcat (lambda (byte)
697 (let ((char (char-to-string byte)))
698 (if (rassoc char xml-entity-alist)
699 (concat "&" (car (rassoc char xml-entity-alist)) ";")
701 ;; This differs from the non-unicode branch. Just
702 ;; grabbing the string works here.
705 (defun xml-debug-print-internal (xml indent-string)
706 "Outputs the XML tree in the current buffer.
707 The first line is indented with INDENT-STRING."
710 (insert indent-string ?< (symbol-name (xml-node-name tree)))
712 ;; output the attribute list
713 (setq attlist (xml-node-attributes tree))
715 (insert ?\ (symbol-name (caar attlist)) "=\""
716 (xml-escape-string (cdar attlist)) ?\")
717 (setq attlist (cdr attlist)))
719 (setq tree (xml-node-children tree))
725 ;; output the children
730 (xml-debug-print-internal node (concat indent-string " ")))
732 (insert (xml-escape-string node)))
734 (error "Invalid XML tree"))))
736 (when (not (and (null (cdr tree))
737 (stringp (car tree))))
738 (insert ?\n indent-string))
739 (insert ?< ?/ (symbol-name (xml-node-name xml)) ?>)))))
741 (let ((tdate (timezone-parse-date "20090101T010101Z")))
742 (when (not (string-equal (aref tdate 0) "2009"))
743 (defun timezone-parse-date (date)
744 "Parse DATE and return a vector [YEAR MONTH DAY TIME TIMEZONE].
745 Two-digit dates are `windowed'. Those <69 have 2000 added; otherwise 1900
746 is added. Three-digit dates have 1900 added.
747 TIMEZONE is nil for DATEs without a zone field.
749 Understands the following styles:
750 (1) 14 Apr 89 03:20[:12] [GMT]
751 (2) Fri, 17 Mar 89 4:01[:33] [GMT]
752 (3) Mon Jan 16 16:12[:37] [GMT] 1989
753 (4) 6 May 1992 1641-JST (Wednesday)
754 (5) 22-AUG-1993 10:59:12.82
755 (6) Thu, 11 Apr 16:17:12 91 [MET]
756 (7) Mon, 6 Jul 16:47:20 T 1992 [MET]
757 (8) 1996-06-24 21:13:12 [GMT]
758 (9) 1996-06-24 21:13-ZONE
759 (10) 19960624T211312"
760 ;; Get rid of any text properties.
762 (or (text-properties-at 0 date)
763 (next-property-change 0 date))
764 (setq date (copy-sequence date))
765 (set-text-properties 0 (length date) nil date))
766 (let ((date (or date ""))
771 (zone nil)) ;This may be nil.
773 "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)" date)
774 ;; Styles: (1) and (2) with timezone and buggy timezone
775 ;; This is most common in mail and news,
776 ;; so it is worth trying first.
777 (setq year 3 month 2 day 1 time 4 zone 5))
779 "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]*\\'" date)
780 ;; Styles: (1) and (2) without timezone
781 (setq year 3 month 2 day 1 time 4 zone nil))
783 "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\'" date)
784 ;; Styles: (6) and (7) without timezone
785 (setq year 6 month 3 day 2 time 4 zone nil))
787 "\\([^ \t,]+\\),[ \t]+\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\(T[ \t]+\\|\\)\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
788 ;; Styles: (6) and (7) with timezone and buggy timezone
789 (setq year 6 month 3 day 2 time 4 zone 7))
791 "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([0-9]+\\)" date)
792 ;; Styles: (3) without timezone
793 (setq year 4 month 1 day 2 time 3 zone nil))
795 "\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9:]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)[ \t]+\\([0-9]+\\)" date)
796 ;; Styles: (3) with timezone
797 (setq year 5 month 1 day 2 time 3 zone 4))
799 "\\([0-9]+\\)[ \t]+\\([^ \t,]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\([0-9]+\\)[ \t]*\\([-+a-zA-Z0-9]+\\)" date)
800 ;; Styles: (4) with timezone
801 (setq year 3 month 2 day 1 time 4 zone 5))
803 "\\([0-9]+\\)-\\([A-Za-z]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)\\(\\.[0-9]+\\)?[ \t]+\\([-+a-zA-Z0-9]+\\)" date)
804 ;; Styles: (5) with timezone.
805 (setq year 3 month 2 day 1 time 4 zone 6))
807 "\\([0-9]+\\)-\\([A-Za-z]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)\\(\\.[0-9]+\\)?" date)
808 ;; Styles: (5) without timezone.
809 (setq year 3 month 2 day 1 time 4 zone nil))
811 "\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)[ \t]+\\([0-9]+:[0-9]+:[0-9]+\\)[ \t]+\\([-+a-zA-Z0-9]+\\)" date)
812 ;; Styles: (8) with timezone.
813 (setq year 1 month 2 day 3 time 4 zone 5))
815 "\\([0-9]\\{4\\}\\)-?\\([0-9]\\{0,2\\}\\)-?\\([0-9]\\{0,2\\}\\)[T \t]+\\([0-9]\\{0,2\\}:?[0-9]\\{0,2\\}:?[0-9]\\{0,2\\}\\)[ \t]*\\([-+a-zA-Z]+[0-9:]*\\)" date)
816 ;; Styles: (8) with timezone with a colon in it.
817 (setq year 1 month 2 day 3 time 4 zone 5))
819 "\\([0-9]\\{4\\}\\)-?\\([0-9]\\{0,2\\}\\)-?\\([0-9]\\{0,2\\}\\)[T \t]+\\([0-9]+:?[0-9]+:?[0-9]+\\)" date)
820 ;; Styles: (8) without timezone.
821 (setq year 1 month 2 day 3 time 4 zone nil)))
824 (setq year (match-string year date))
825 ;; Guess ambiguous years. Assume years < 69 don't predate the
826 ;; Unix Epoch, so are 2000+. Three-digit years are assumed to
827 ;; be relative to 1900.
828 (when (< (length year) 4)
829 (let ((y (string-to-number year)))
832 (setq year (int-to-string (+ 1900 y)))))
834 (if (or (= (aref date (+ (match-beginning month) 2)) ?-)
835 (let ((n (string-to-number
837 (aref date (+ (match-beginning month) 2))))))
838 (= (aref (number-to-string n) 0)
839 (aref date (+ (match-beginning month) 2)))))
840 ;; Handle numeric months, spanning exactly two digits.
842 (match-beginning month)
843 (+ (match-beginning month) 2))
844 (let* ((string (substring date
845 (match-beginning month)
846 (+ (match-beginning month) 3)))
848 (cdr (assoc (upcase string) timezone-months-assoc))))
850 (int-to-string monthnum)))))
851 (setq day (match-string day date))
852 (setq time (match-string time date)))
853 (when zone (setq zone (match-string zone date)))
856 (vector year month day time zone)
857 (vector "0" "0" "0" "0" nil))))))
862 ;; time-stamp-pattern: "20/^;; Last Modified: <%%>$"
865 ;;; xml-rpc.el ends here