GNU bug report logs - #11916
24.1.50; Making url-dav work

Previous Next

Package: emacs;

Reported by: David Engster <deng <at> randomsample.de>

Date: Wed, 11 Jul 2012 21:06:02 UTC

Severity: normal

Found in version 24.1.50

Done: Chong Yidong <cyd <at> gnu.org>

Bug is archived. No further changes may be made.

Full log


View this message in rfc822 format

From: David Engster <deng <at> randomsample.de>
To: Stefan Monnier <monnier <at> IRO.UMontreal.CA>
Cc: 11916 <at> debbugs.gnu.org
Subject: bug#11916: 24.1.50; Making url-dav work
Date: Sat, 21 Jul 2012 14:11:13 +0200
[Message part 1 (text/plain, inline)]
Stefan Monnier writes:
>> You might get name clashes; for example, the code might parse a
>> 'collection' although it is actually not a "DAV:collection" but a
>> "FOOBAR:collection". Granted, it's not very likely, and if this would be
>> used in a read-only fashion (like parsing atom feeds) I'd drop the
>> namespaces in a heartbeat. But since url-dav will usually be used to
>> manipulate actual files on remote servers, I'd rather not risk it.
>
> I see.  So using libxml wouldn't be an option (or maybe libxml can also
> do it, but we'd need to change libxml-parse-xml-region for that?).

Yes, libxml can do namespace parsing.

>>> Of course, I was thinking of changing it in a backward compatible way,
>>> by letting the `parse-ns' argument specify which kind of result you
>>> want.  The changes should be mostly limited to xml-maybe-do-ns.
>> I could live with that.
>
> Could you prepare a patch for that?

Attached. I had to go another route, though; turns out the `parse-ns'
argument is already overloaded in `xml-parse-tag' (it can be used to
provide a namespace->URI mapping), but that wasn't mentioned in the
other parse functions. So I had to introduce an additional argument.

I also attached my current changes in url-dav.el, which next to
supporting the new `simple-qnames' argument contain a few other
fixes. Here's the complete ChangeLog:

xml.el:

(xml-node-name): Mention `simple-qnames' in doc-string.
(xml-parse-file, xml-parse-region, xml--parse-buffer)
(xml-parse-tag, xml-parse-tag-1, xml-parse-attlist): Add argument
`simple-qnames'.  Adapt all calls to parse functions to hand over this
new argument.  Adapt doc-strings to mention `simple-qnames' and also
mention that `parse-ns' can be used to provide namespace mappings.
(xml-maybe-do-ns): Return symbol instead of cons depending on
`simple-qnames' argument.


url-dav.el:

(url-dav-supported-p): Added doc-string and remove check for feature
`xml' and function `xml-expand-namespace' which never existed in Emacs
proper.
(url-dav-process-response): Remove all indentation from XML
before parsing.  Change call to `xml-parse-region' to do namespace
expansion with simple qualified names.
(url-dav-request): Add autoload.
(url-dav-directory-files): Properly deal with empty directories.  Call
hexify before generating relative URLs.
(url-dav-file-directory-p): Fix bug when checking for 'DAV:collection
(resources are returned as a list).

-David

[xml-diff.patch (text/x-patch, inline)]
=== modified file 'lisp/xml.el'
--- lisp/xml.el	2012-07-04 16:14:05 +0000
+++ lisp/xml.el	2012-07-21 10:47:53 +0000
@@ -118,16 +118,18 @@
   "Return the tag associated with NODE.
 Without namespace-aware parsing, the tag is a symbol.
 
-With namespace-aware parsing, the tag is a cons of a string
-representing the uri of the namespace with the local name of the
-tag.  For example,
+With namespace-aware parsing, by default the tag is a cons of a
+string representing the uri of the namespace with the local name
+of the tag.  For example,
 
     <foo>
 
 would be represented by
 
-    '(\"\" . \"foo\")."
+    '(\"\" . \"foo\").
 
+If you would rather like a plain symbol instead, provide a
+non-nil SIMPLE-QNAMES argument to the parser functions."
   (car node))
 
 (defsubst xml-node-attributes (node)
@@ -309,17 +311,24 @@
 ;;; Entry points:
 
 ;;;###autoload
-(defun xml-parse-file (file &optional parse-dtd parse-ns)
+(defun xml-parse-file (file &optional parse-dtd parse-ns simple-qnames)
   "Parse the well-formed XML file FILE.
 Return the top node with all its children.
 If PARSE-DTD is non-nil, the DTD is parsed rather than skipped.
-If PARSE-NS is non-nil, then QNAMES are expanded."
+If PARSE-NS is non-nil, expand QNAMES; if the value of PARSE-NS
+is a list, use it as an alist mapping namespaces to URIs.
+Expanded names will by default be returned as a cons
+
+  (\"foo:\" . \"bar\").
+
+If you would like to get a plain symbol 'foo:bar instead, set
+SIMPLE-QNAMES to a non-nil value."
   (with-temp-buffer
     (insert-file-contents file)
-    (xml--parse-buffer parse-dtd parse-ns)))
+    (xml--parse-buffer parse-dtd parse-ns simple-qnames)))
 
 ;;;###autoload
-(defun xml-parse-region (&optional beg end buffer parse-dtd parse-ns)
+(defun xml-parse-region (&optional beg end buffer parse-dtd parse-ns simple-qnames)
   "Parse the region from BEG to END in BUFFER.
 Return the XML parse tree, or raise an error if the region does
 not contain well-formed XML.
@@ -329,14 +338,21 @@
 If BUFFER is nil, it defaults to the current buffer.
 If PARSE-DTD is non-nil, parse the DTD and return it as the first
 element of the list.
-If PARSE-NS is non-nil, expand QNAMES."
+If PARSE-NS is non-nil, expand QNAMES; if the value of PARSE-NS
+is a list, use it as an alist mapping namespaces to URIs.
+Expanded names will by default be returned as a cons
+
+  (\"foo:\" . \"bar\").
+
+If you would like to get a plain symbol 'foo:bar instead, set
+SIMPLE-QNAMES to a non-nil value."
   ;; Use fixed syntax table to ensure regexp char classes and syntax
   ;; specs DTRT.
   (unless buffer
     (setq buffer (current-buffer)))
   (with-temp-buffer
     (insert-buffer-substring-no-properties buffer beg end)
-    (xml--parse-buffer parse-dtd parse-ns)))
+    (xml--parse-buffer parse-dtd parse-ns simple-qnames)))
 
 ;; XML [5]
 
@@ -344,7 +360,7 @@
 ;;   document  ::=  prolog element Misc*
 ;;   prolog    ::=  XMLDecl? Misc* (doctypedecl Misc*)?
 
-(defun xml--parse-buffer (parse-dtd parse-ns)
+(defun xml--parse-buffer (parse-dtd parse-ns simple-qnames)
   (with-syntax-table xml-syntax-table
     (let ((case-fold-search nil)	; XML is case-sensitive.
 	  ;; Prevent entity definitions from changing the defaults
@@ -356,7 +372,7 @@
 	(if (search-forward "<" nil t)
 	    (progn
 	      (forward-char -1)
-	      (setq result (xml-parse-tag-1 parse-dtd parse-ns))
+	      (setq result (xml-parse-tag-1 parse-dtd parse-ns simple-qnames))
 	      (cond
 	       ((null result)
 		;; Not looking at an xml start tag.
@@ -377,7 +393,7 @@
 	  (cons dtd (nreverse xml))
 	(nreverse xml)))))
 
-(defun xml-maybe-do-ns (name default xml-ns)
+(defun xml-maybe-do-ns (name default xml-ns simple-qnames)
   "Perform any namespace expansion.
 NAME is the name to perform the expansion on.
 DEFAULT is the default namespace.  XML-NS is a cons of namespace
@@ -386,7 +402,10 @@
 
 During namespace-aware parsing, any name without a namespace is
 put into the namespace identified by DEFAULT.  nil is used to
-specify that the name shouldn't be given a namespace."
+specify that the name shouldn't be given a namespace.
+Expanded names will by default be returned as a cons.  If you
+would like to get plain symbols, set SIMPLE-QNAMES to a non-nil
+value."
   (if (consp xml-ns)
       (let* ((nsp (string-match ":" name))
 	     (lname (if nsp (substring name (match-end 0)) name))
@@ -397,15 +416,24 @@
 	     (ns (or (cdr (assoc (if special "xmlns" prefix)
                                  xml-ns))
                      "")))
-        (cons ns (if special "" lname)))
+	(if (and simple-qnames
+		 (not (string= prefix "xmlns")))
+	    (intern (concat ns lname))
+	  (cons ns (if special "" lname))))
     (intern name)))
 
-(defun xml-parse-tag (&optional parse-dtd parse-ns)
+(defun xml-parse-tag (&optional parse-dtd parse-ns simple-qnames)
   "Parse the tag at point.
 If PARSE-DTD is non-nil, the DTD of the document, if any, is parsed and
 returned as the first element in the list.
 If PARSE-NS is non-nil, expand QNAMES; if the value of PARSE-NS
 is a list, use it as an alist mapping namespaces to URIs.
+Expanded names will by default be returned as a cons
+
+  (\"foo:\" . \"bar\").
+
+If you would like to get a plain symbol 'foo:bar instead, set
+SIMPLE-QNAMES to a non-nil value.
 
 Return one of:
  - a list : the matching node
@@ -421,9 +449,9 @@
       (with-syntax-table xml-syntax-table
 	(insert-buffer-substring-no-properties buf pos)
 	(goto-char (point-min))
-	(xml-parse-tag-1 parse-dtd parse-ns)))))
+	(xml-parse-tag-1 parse-dtd parse-ns simple-qnames)))))
 
-(defun xml-parse-tag-1 (&optional parse-dtd parse-ns)
+(defun xml-parse-tag-1 (&optional parse-dtd parse-ns simple-qnames)
   "Like `xml-parse-tag', but possibly modify the buffer while working."
   (let ((xml-validating-parser (or parse-dtd xml-validating-parser))
 	(xml-ns (cond ((consp parse-ns) parse-ns)
@@ -433,7 +461,7 @@
      ((looking-at "<\\?")
       (search-forward "?>")
       (skip-syntax-forward " ")
-      (xml-parse-tag-1 parse-dtd xml-ns))
+      (xml-parse-tag-1 parse-dtd xml-ns simple-qnames))
      ;; Character data (CDATA) sections, in which no tag should be interpreted
      ((looking-at "<!\\[CDATA\\[")
       (let ((pos (match-end 0)))
@@ -447,8 +475,8 @@
       (let ((dtd (xml-parse-dtd parse-ns)))
 	(skip-syntax-forward " ")
 	(if xml-validating-parser
-	    (cons dtd (xml-parse-tag-1 nil xml-ns))
-	  (xml-parse-tag-1 nil xml-ns))))
+	    (cons dtd (xml-parse-tag-1 nil xml-ns simple-qnames))
+	  (xml-parse-tag-1 nil xml-ns simple-qnames))))
      ;; skip comments
      ((looking-at "<!--")
       (search-forward "-->")
@@ -456,7 +484,7 @@
       (skip-syntax-forward " ")
       (unless (eobp)
 	(let ((xml-sub-parser t))
-	  (xml-parse-tag-1 parse-dtd xml-ns))))
+	  (xml-parse-tag-1 parse-dtd xml-ns simple-qnames))))
      ;; end tag
      ((looking-at "</")
       '())
@@ -466,7 +494,7 @@
       ;; Parse this node
       (let* ((node-name (match-string-no-properties 1))
 	     ;; Parse the attribute list.
-	     (attrs (xml-parse-attlist xml-ns))
+	     (attrs (xml-parse-attlist xml-ns simple-qnames))
 	     children)
 	;; add the xmlns:* attrs to our cache
 	(when (consp xml-ns)
@@ -476,7 +504,8 @@
 			      (caar attr)))
 	      (push (cons (cdar attr) (cdr attr))
 		    xml-ns))))
-	(setq children (list attrs (xml-maybe-do-ns node-name "" xml-ns)))
+	(setq children (list attrs (xml-maybe-do-ns node-name ""
+						    xml-ns simple-qnames)))
 	(cond
 	 ;; is this an empty element ?
 	 ((looking-at "/>")
@@ -502,7 +531,7 @@
 		       node-name))
 	       ;; Read a sub-element and push it onto CHILDREN.
 	       ((= (char-after) ?<)
-		(let ((tag (xml-parse-tag-1 nil xml-ns)))
+		(let ((tag (xml-parse-tag-1 nil xml-ns simple-qnames)))
 		  (when tag
 		    (push tag children))))
 	       ;; Read some character data.
@@ -585,7 +614,7 @@
       (goto-char end-marker)
       (buffer-substring start (point)))))
 
-(defun xml-parse-attlist (&optional xml-ns)
+(defun xml-parse-attlist (&optional xml-ns simple-qnames)
   "Return the attribute-list after point.
 Leave point at the first non-blank character after the tag."
   (let ((attlist ())
@@ -594,7 +623,8 @@
     (while (looking-at (eval-when-compile
 			 (concat "\\(" xml-name-re "\\)\\s-*=\\s-*")))
       (setq end-pos (match-end 0))
-      (setq name (xml-maybe-do-ns (match-string-no-properties 1) nil xml-ns))
+      (setq name (xml-maybe-do-ns (match-string-no-properties 1)
+				  nil xml-ns simple-qnames))
       (goto-char end-pos)
 
       ;; See also: http://www.w3.org/TR/2000/REC-xml-20001006#AVNormalize

[url-dav-diff.patch (text/x-patch, inline)]
=== modified file 'lisp/url/url-dav.el'
--- lisp/url/url-dav.el	2012-07-11 23:13:41 +0000
+++ lisp/url/url-dav.el	2012-07-21 11:45:23 +0000
@@ -53,10 +53,10 @@
 
 ;;;###autoload
 (defun url-dav-supported-p (url)
-  (and (featurep 'xml)
-       (fboundp 'xml-expand-namespace)
-       (url-intersection url-dav-supported-protocols
-			 (plist-get (url-http-options url) 'dav))))
+  "Return WebDAV protocol version supported by URL.
+Returns nil if WebDAV is not supported."
+  (url-intersection url-dav-supported-protocols
+		    (plist-get (url-http-options url) 'dav)))
 
 (defun url-dav-node-text (node)
   "Return the text data from the XML node NODE."
@@ -385,7 +385,12 @@
     (when buffer
       (unwind-protect
 	  (with-current-buffer buffer
+	    ;; First remove all indentation and line endings
 	    (goto-char url-http-end-of-headers)
+	    (indent-rigidly (point) (point-max) -1000)
+	    (save-excursion
+	      (while (re-search-forward "\r?\n" nil t)
+		(replace-match "")))
 	    (setq overall-status url-http-response-status)
 
 	    ;; XML documents can be transferred as either text/xml or
@@ -395,7 +400,7 @@
 		 url-http-content-type
 		 (string-match "\\`\\(text\\|application\\)/xml"
 			       url-http-content-type))
-		(setq tree (xml-parse-region (point) (point-max)))))
+		(setq tree (xml-parse-region (point) (point-max) nil nil t t))))
 	;; Clean up after ourselves.
 	(kill-buffer buffer)))
 
@@ -411,6 +416,7 @@
 	;; nobody but us needs to know the difference.
 	(list (cons url properties))))))
 
+;;;###autoload
 (defun url-dav-request (url method tag body
 				 &optional depth headers namespaces)
   "Perform WebDAV operation METHOD on URL.  Return the parsed responses.
@@ -768,8 +774,8 @@
 (defun url-dav-directory-files (url &optional full match nosort files-only)
   "Return a list of names of files in URL.
 There are three optional arguments:
-If FULL is non-nil, return absolute file names.  Otherwise return names
- that are relative to the specified directory.
+If FULL is non-nil, return absolute URLs.  Otherwise return names
+ that are relative to the specified URL.
 If MATCH is non-nil, mention only file names that match the regexp MATCH.
 If NOSORT is non-nil, the list is not sorted--its order is unpredictable.
  NOSORT is useful if you plan to sort the result yourself."
@@ -779,8 +785,9 @@
 	(files nil)
 	(parsed-url (url-generic-parse-url url)))
 
-    (if (= (length properties) 1)
-	(signal 'file-error (list "Opening directory" "not a directory" url)))
+    (when (and (= (length properties) 1)
+	       (not (url-dav-file-directory-p url)))
+      (signal 'file-error (list "Opening directory" "not a directory" url)))
 
     (while properties
       (setq child-props (pop properties)
@@ -791,10 +798,13 @@
 	  nil
 
 	;; Fully expand the URL and then rip off the beginning if we
-	;; are not supposed to return fully-qualified names.
+	;; are not supposed to return fully-qualified names.  
 	(setq child-url (url-expand-file-name child-url parsed-url))
 	(if (not full)
-	    (setq child-url (substring child-url (length url))))
+	    ;; Parts of the URL might be hex'ed.
+	    (setq child-url (url-unhex-string
+			     (substring (url-hexify-string child-url)
+					(length (url-hexify-string url))))))
 
 	;; We don't want '/' as the last character in filenames...
 	(if (string-match "/$" child-url)
@@ -814,7 +824,8 @@
 (defun url-dav-file-directory-p (url)
   "Return t if URL names an existing DAV collection."
   (let ((properties (cdar (url-dav-get-properties url '(DAV:resourcetype)))))
-    (eq (plist-get properties 'DAV:resourcetype) 'DAV:collection)))
+    (when (member 'DAV:collection (plist-get properties 'DAV:resourcetype))
+      t)))
 
 (defun url-dav-make-directory (url &optional parents)
   "Create the directory DIR and any nonexistent parent dirs."


This bug report was last modified 12 years and 361 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.