(handle_composition_prop): Fix for the case of empty
[bpt/emacs.git] / lisp / url / url-http.el
CommitLineData
8c8b8430 1;;; url-http.el --- HTTP retrieval routines
b43eb9ab 2
55d1d8b7 3;; Copyright (C) 1999, 2001, 2004, 2005, 2006 Free Software Foundation, Inc.
b43eb9ab 4
8c8b8430 5;; Author: Bill Perry <wmperry@gnu.org>
8c8b8430 6;; Keywords: comm, data, processes
3f19601e 7
b43eb9ab
SM
8;; This file is part of GNU Emacs.
9;;
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14;;
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19;;
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to the
4fc5845f
LK
22;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23;; Boston, MA 02110-1301, USA.
8c8b8430 24
b43eb9ab
SM
25;;; Commentary:
26
27;;; Code:
8c8b8430 28
12f1edc8
SM
29(eval-when-compile (require 'cl))
30(defvar url-http-extra-headers)
31(defvar url-http-target-url)
8c8b8430
SM
32(require 'url-gw)
33(require 'url-util)
34(require 'url-parse)
35(require 'url-cookie)
36(require 'mail-parse)
37(require 'url-auth)
55d1d8b7 38(require 'url)
8c8b8430 39(autoload 'url-cache-create-filename "url-cache")
8c8b8430
SM
40
41(defconst url-http-default-port 80 "Default HTTP port.")
42(defconst url-http-asynchronous-p t "HTTP retrievals are asynchronous.")
43(defalias 'url-http-expand-file-name 'url-default-expander)
44
45(defvar url-http-real-basic-auth-storage nil)
46(defvar url-http-proxy-basic-auth-storage nil)
47
48(defvar url-http-open-connections (make-hash-table :test 'equal
49 :size 17)
50 "A hash table of all open network connections.")
51
52(defvar url-http-version "1.1"
53 "What version of HTTP we advertise, as a string.
54Valid values are 1.1 and 1.0.
55This is only useful when debugging the HTTP subsystem.
56
57Setting this to 1.0 will tell servers not to send chunked encoding,
55d1d8b7 58and other HTTP/1.1 specific features.")
8c8b8430
SM
59
60(defvar url-http-attempt-keepalives t
61 "Whether to use a single TCP connection multiple times in HTTP.
62This is only useful when debugging the HTTP subsystem. Setting to
55d1d8b7
SM
63nil will explicitly close the connection to the server after every
64request.")
8c8b8430
SM
65
66;(eval-when-compile
67;; These are all macros so that they are hidden from external sight
68;; when the file is byte-compiled.
69;;
70;; This allows us to expose just the entry points we want.
71
72;; These routines will allow us to implement persistent HTTP
73;; connections.
74(defsubst url-http-debug (&rest args)
75 (if quit-flag
76 (let ((proc (get-buffer-process (current-buffer))))
77 ;; The user hit C-g, honor it! Some things can get in an
78 ;; incredibly tight loop (chunked encoding)
79 (if proc
80 (progn
81 (set-process-sentinel proc nil)
82 (set-process-filter proc nil)))
83 (error "Transfer interrupted!")))
84 (apply 'url-debug 'http args))
85
86(defun url-http-mark-connection-as-busy (host port proc)
87 (url-http-debug "Marking connection as busy: %s:%d %S" host port proc)
88 (puthash (cons host port)
89 (delq proc (gethash (cons host port) url-http-open-connections))
90 url-http-open-connections)
91 proc)
92
93(defun url-http-mark-connection-as-free (host port proc)
94 (url-http-debug "Marking connection as free: %s:%d %S" host port proc)
95 (set-process-buffer proc nil)
96 (set-process-sentinel proc 'url-http-idle-sentinel)
97 (puthash (cons host port)
98 (cons proc (gethash (cons host port) url-http-open-connections))
99 url-http-open-connections)
100 nil)
101
102(defun url-http-find-free-connection (host port)
103 (let ((conns (gethash (cons host port) url-http-open-connections))
104 (found nil))
105 (while (and conns (not found))
106 (if (not (memq (process-status (car conns)) '(run open)))
107 (progn
108 (url-http-debug "Cleaning up dead process: %s:%d %S"
109 host port (car conns))
110 (url-http-idle-sentinel (car conns) nil))
111 (setq found (car conns))
112 (url-http-debug "Found existing connection: %s:%d %S" host port found))
113 (pop conns))
114 (if found
115 (url-http-debug "Reusing existing connection: %s:%d" host port)
116 (url-http-debug "Contacting host: %s:%d" host port))
117 (url-lazy-message "Contacting host: %s:%d" host port)
55d1d8b7
SM
118 (url-http-mark-connection-as-busy
119 host port
120 (or found
121 (let ((buf (generate-new-buffer " *url-http-temp*")))
122 ;; `url-open-stream' needs a buffer in which to do things
123 ;; like authentication. But we use another buffer afterwards.
8dff56de
SM
124 (unwind-protect
125 (let ((proc (url-open-stream host buf host port)))
126 ;; Drop the temp buffer link before killing the buffer.
bedeb7fd
SM
127 (set-process-buffer proc nil)
128 proc)
55d1d8b7 129 (kill-buffer buf)))))))
8c8b8430
SM
130
131;; Building an HTTP request
132(defun url-http-user-agent-string ()
133 (if (or (eq url-privacy-level 'paranoid)
134 (and (listp url-privacy-level)
135 (memq 'agent url-privacy-level)))
136 ""
137 (format "User-Agent: %sURL/%s%s\r\n"
138 (if url-package-name
139 (concat url-package-name "/" url-package-version " ")
140 "")
141 url-version
142 (cond
143 ((and url-os-type url-system-type)
144 (concat " (" url-os-type "; " url-system-type ")"))
145 ((or url-os-type url-system-type)
146 (concat " (" (or url-system-type url-os-type) ")"))
147 (t "")))))
148
149(defun url-http-create-request (url &optional ref-url)
150 "Create an HTTP request for URL, referred to by REF-URL."
151 (declare (special proxy-object proxy-info))
152 (let* ((extra-headers)
153 (request nil)
154 (no-cache (cdr-safe (assoc "Pragma" url-request-extra-headers)))
155 (proxy-obj (and (boundp 'proxy-object) proxy-object))
156 (proxy-auth (if (or (cdr-safe (assoc "Proxy-Authorization"
157 url-request-extra-headers))
158 (not proxy-obj))
159 nil
160 (let ((url-basic-auth-storage
161 'url-http-proxy-basic-auth-storage))
162 (url-get-authentication url nil 'any nil))))
e893ce91 163 (real-fname (url-filename (or proxy-obj url)))
8c8b8430
SM
164 (host (url-host (or proxy-obj url)))
165 (auth (if (cdr-safe (assoc "Authorization" url-request-extra-headers))
166 nil
167 (url-get-authentication (or
168 (and (boundp 'proxy-info)
169 proxy-info)
170 url) nil 'any nil))))
171 (if (equal "" real-fname)
172 (setq real-fname "/"))
173 (setq no-cache (and no-cache (string-match "no-cache" no-cache)))
174 (if auth
175 (setq auth (concat "Authorization: " auth "\r\n")))
176 (if proxy-auth
177 (setq proxy-auth (concat "Proxy-Authorization: " proxy-auth "\r\n")))
178
179 ;; Protection against stupid values in the referer
180 (if (and ref-url (stringp ref-url) (or (string= ref-url "file:nil")
181 (string= ref-url "")))
182 (setq ref-url nil))
183
184 ;; We do not want to expose the referer if the user is paranoid.
185 (if (or (memq url-privacy-level '(low high paranoid))
186 (and (listp url-privacy-level)
187 (memq 'lastloc url-privacy-level)))
188 (setq ref-url nil))
189
190 ;; url-request-extra-headers contains an assoc-list of
191 ;; header/value pairs that we need to put into the request.
192 (setq extra-headers (mapconcat
193 (lambda (x)
194 (concat (car x) ": " (cdr x)))
195 url-request-extra-headers "\r\n"))
196 (if (not (equal extra-headers ""))
197 (setq extra-headers (concat extra-headers "\r\n")))
198
199 ;; This was done with a call to `format'. Concatting parts has
1430e7f9 200 ;; the advantage of keeping the parts of each header together and
8c8b8430
SM
201 ;; allows us to elide null lines directly, at the cost of making
202 ;; the layout less clear.
203 (setq request
1430e7f9
SM
204 ;; We used to concat directly, but if one of the strings happens
205 ;; to being multibyte (even if it only contains pure ASCII) then
206 ;; every string gets converted with `string-MAKE-multibyte' which
207 ;; turns the 127-255 codes into things like latin-1 accented chars
208 ;; (it would work right if it used `string-TO-multibyte' instead).
209 ;; So to avoid the problem we force every string to be unibyte.
210 (mapconcat
211 ;; FIXME: Instead of `string-AS-unibyte' we'd want
212 ;; `string-to-unibyte', so as to properly signal an error if one
213 ;; of the strings contains a multibyte char.
214 'string-as-unibyte
215 (delq nil
216 (list
217 ;; The request
218 (or url-request-method "GET") " "
219 (if proxy-obj (url-recreate-url proxy-obj) real-fname)
220 " HTTP/" url-http-version "\r\n"
221 ;; Version of MIME we speak
222 "MIME-Version: 1.0\r\n"
223 ;; (maybe) Try to keep the connection open
224 "Connection: " (if (or proxy-obj
225 (not url-http-attempt-keepalives))
226 "close" "keep-alive") "\r\n"
227 ;; HTTP extensions we support
228 (if url-extensions-header
229 (format
230 "Extension: %s\r\n" url-extensions-header))
231 ;; Who we want to talk to
232 (if (/= (url-port (or proxy-obj url))
233 (url-scheme-get-property
234 (url-type (or proxy-obj url)) 'default-port))
235 (format
236 "Host: %s:%d\r\n" host (url-port (or proxy-obj url)))
237 (format "Host: %s\r\n" host))
238 ;; Who its from
239 (if url-personal-mail-address
240 (concat
241 "From: " url-personal-mail-address "\r\n"))
242 ;; Encodings we understand
243 (if url-mime-encoding-string
244 (concat
245 "Accept-encoding: " url-mime-encoding-string "\r\n"))
246 (if url-mime-charset-string
247 (concat
248 "Accept-charset: " url-mime-charset-string "\r\n"))
249 ;; Languages we understand
250 (if url-mime-language-string
251 (concat
252 "Accept-language: " url-mime-language-string "\r\n"))
253 ;; Types we understand
254 "Accept: " (or url-mime-accept-string "*/*") "\r\n"
255 ;; User agent
256 (url-http-user-agent-string)
257 ;; Proxy Authorization
258 proxy-auth
259 ;; Authorization
260 auth
261 ;; Cookies
262 (url-cookie-generate-header-lines host real-fname
263 (equal "https" (url-type url)))
264 ;; If-modified-since
265 (if (and (not no-cache)
266 (member url-request-method '("GET" nil)))
267 (let ((tm (url-is-cached (or proxy-obj url))))
268 (if tm
269 (concat "If-modified-since: "
270 (url-get-normalized-date tm) "\r\n"))))
271 ;; Whence we came
272 (if ref-url (concat
273 "Referer: " ref-url "\r\n"))
274 extra-headers
275 ;; Length of data
276 (if url-request-data
277 (concat
278 "Content-length: " (number-to-string
279 (length url-request-data))
280 "\r\n"))
281 ;; End request
282 "\r\n"
283 ;; Any data
284 url-request-data))
285 ""))
8c8b8430
SM
286 (url-http-debug "Request is: \n%s" request)
287 request))
288
289;; Parsing routines
290(defun url-http-clean-headers ()
291 "Remove trailing \r from header lines.
292This allows us to use `mail-fetch-field', etc."
293 (declare (special url-http-end-of-headers))
294 (goto-char (point-min))
295 (while (re-search-forward "\r$" url-http-end-of-headers t)
296 (replace-match "")))
297
298(defun url-http-handle-authentication (proxy)
299 (declare (special status success url-http-method url-http-data
300 url-callback-function url-callback-arguments))
301 (url-http-debug "Handling %s authentication" (if proxy "proxy" "normal"))
302 (let ((auth (or (mail-fetch-field (if proxy "proxy-authenticate" "www-authenticate"))
303 "basic"))
304 (type nil)
305 (url (url-recreate-url url-current-object))
306 (url-basic-auth-storage 'url-http-real-basic-auth-storage)
307 )
308
309 ;; Cheating, but who cares? :)
310 (if proxy
311 (setq url-basic-auth-storage 'url-http-proxy-basic-auth-storage))
312
313 (setq auth (url-eat-trailing-space (url-strip-leading-spaces auth)))
314 (if (string-match "[ \t]" auth)
315 (setq type (downcase (substring auth 0 (match-beginning 0))))
316 (setq type (downcase auth)))
317
318 (if (not (url-auth-registered type))
319 (progn
320 (widen)
321 (goto-char (point-max))
322 (insert "<hr>Sorry, but I do not know how to handle " type
323 " authentication. If you'd like to write it,"
324 " send it to " url-bug-address ".<hr>")
325 (setq status t))
12f1edc8
SM
326 (let* ((args (url-parse-args (subst-char-in-string ?, ?\; auth)))
327 (auth (url-get-authentication url (cdr-safe (assoc "realm" args))
328 type t args)))
8c8b8430
SM
329 (if (not auth)
330 (setq success t)
331 (push (cons (if proxy "Proxy-Authorization" "Authorization") auth)
332 url-http-extra-headers)
333 (let ((url-request-method url-http-method)
334 (url-request-data url-http-data)
335 (url-request-extra-headers url-http-extra-headers))
e817f9a2
TTN
336 (url-retrieve url url-callback-function
337 url-callback-arguments)))))))
8c8b8430
SM
338
339(defun url-http-parse-response ()
340 "Parse just the response code."
341 (declare (special url-http-end-of-headers url-http-response-status))
342 (if (not url-http-end-of-headers)
343 (error "Trying to parse HTTP response code in odd buffer: %s" (buffer-name)))
344 (url-http-debug "url-http-parse-response called in (%s)" (buffer-name))
345 (goto-char (point-min))
346 (skip-chars-forward " \t\n") ; Skip any blank crap
347 (skip-chars-forward "HTTP/") ; Skip HTTP Version
348 (read (current-buffer))
349 (setq url-http-response-status (read (current-buffer))))
350
351(defun url-http-handle-cookies ()
352 "Handle all set-cookie / set-cookie2 headers in an HTTP response.
55d1d8b7 353The buffer must already be narrowed to the headers, so `mail-fetch-field' will
8c8b8430
SM
354work correctly."
355 (let ((cookies (mail-fetch-field "Set-Cookie" nil nil t))
cacfe88b 356 (cookies2 (mail-fetch-field "Set-Cookie2" nil nil t))
12f1edc8 357 (url-current-object url-http-target-url))
8c8b8430
SM
358 (and cookies (url-http-debug "Found %d Set-Cookie headers" (length cookies)))
359 (and cookies2 (url-http-debug "Found %d Set-Cookie2 headers" (length cookies2)))
360 (while cookies
361 (url-cookie-handle-set-cookie (pop cookies)))
362;;; (while cookies2
363;;; (url-cookie-handle-set-cookie2 (pop cookies)))
364 )
365 )
366
367(defun url-http-parse-headers ()
368 "Parse and handle HTTP specific headers.
369Return t if and only if the current buffer is still active and
370should be shown to the user."
371 ;; The comments after each status code handled are taken from RFC
372 ;; 2616 (HTTP/1.1)
373 (declare (special url-http-end-of-headers url-http-response-status
374 url-http-method url-http-data url-http-process
375 url-callback-function url-callback-arguments))
376
377 (url-http-mark-connection-as-free (url-host url-current-object)
378 (url-port url-current-object)
379 url-http-process)
380
381 (if (or (not (boundp 'url-http-end-of-headers))
382 (not url-http-end-of-headers))
383 (error "Trying to parse headers in odd buffer: %s" (buffer-name)))
384 (goto-char (point-min))
385 (url-http-debug "url-http-parse-headers called in (%s)" (buffer-name))
386 (url-http-parse-response)
387 (mail-narrow-to-head)
388 ;;(narrow-to-region (point-min) url-http-end-of-headers)
b43eb9ab 389 (let ((class nil)
8c8b8430
SM
390 (success nil))
391 (setq class (/ url-http-response-status 100))
392 (url-http-debug "Parsed HTTP headers: class=%d status=%d" class url-http-response-status)
393 (url-http-handle-cookies)
394
395 (case class
396 ;; Classes of response codes
397 ;;
398 ;; 5xx = Server Error
399 ;; 4xx = Client Error
400 ;; 3xx = Redirection
401 ;; 2xx = Successful
402 ;; 1xx = Informational
403 (1 ; Information messages
404 ;; 100 = Continue with request
405 ;; 101 = Switching protocols
406 ;; 102 = Processing (Added by DAV)
407 (url-mark-buffer-as-dead (current-buffer))
408 (error "HTTP responses in class 1xx not supported (%d)" url-http-response-status))
409 (2 ; Success
410 ;; 200 Ok
411 ;; 201 Created
412 ;; 202 Accepted
413 ;; 203 Non-authoritative information
414 ;; 204 No content
415 ;; 205 Reset content
416 ;; 206 Partial content
417 ;; 207 Multi-status (Added by DAV)
418 (case url-http-response-status
419 ((204 205)
420 ;; No new data, just stay at the same document
421 (url-mark-buffer-as-dead (current-buffer))
422 (setq success t))
423 (otherwise
424 ;; Generic success for all others. Store in the cache, and
425 ;; mark it as successful.
426 (widen)
76bad534 427 (if (and url-automatic-caching (equal url-http-method "GET"))
8c8b8430
SM
428 (url-store-in-cache (current-buffer)))
429 (setq success t))))
430 (3 ; Redirection
431 ;; 300 Multiple choices
432 ;; 301 Moved permanently
433 ;; 302 Found
434 ;; 303 See other
435 ;; 304 Not modified
436 ;; 305 Use proxy
437 ;; 307 Temporary redirect
438 (let ((redirect-uri (or (mail-fetch-field "Location")
439 (mail-fetch-field "URI"))))
440 (case url-http-response-status
441 (300
442 ;; Quoth the spec (section 10.3.1)
443 ;; -------------------------------
444 ;; The requested resource corresponds to any one of a set of
445 ;; representations, each with its own specific location and
446 ;; agent-driven negotiation information is being provided so
447 ;; that the user can select a preferred representation and
448 ;; redirect its request to that location.
449 ;; [...]
450 ;; If the server has a preferred choice of representation, it
451 ;; SHOULD include the specific URI for that representation in
452 ;; the Location field; user agents MAY use the Location field
453 ;; value for automatic redirection.
454 ;; -------------------------------
455 ;; We do not support agent-driven negotiation, so we just
456 ;; redirect to the preferred URI if one is provided.
457 nil)
458 ((301 302 307)
459 ;; If the 301|302 status code is received in response to a
460 ;; request other than GET or HEAD, the user agent MUST NOT
461 ;; automatically redirect the request unless it can be
462 ;; confirmed by the user, since this might change the
463 ;; conditions under which the request was issued.
464 (if (member url-http-method '("HEAD" "GET"))
465 ;; Automatic redirection is ok
466 nil
467 ;; It is just too big of a pain in the ass to get this
468 ;; prompt all the time. We will just silently lose our
469 ;; data and convert to a GET method.
470 (url-http-debug "Converting `%s' request to `GET' because of REDIRECT(%d)"
471 url-http-method url-http-response-status)
472 (setq url-http-method "GET"
cfdd484f 473 url-http-data nil)))
8c8b8430
SM
474 (303
475 ;; The response to the request can be found under a different
476 ;; URI and SHOULD be retrieved using a GET method on that
477 ;; resource.
478 (setq url-http-method "GET"
479 url-http-data nil))
480 (304
481 ;; The 304 response MUST NOT contain a message-body.
482 (url-http-debug "Extracting document from cache... (%s)"
483 (url-cache-create-filename (url-view-url t)))
484 (url-cache-extract (url-cache-create-filename (url-view-url t)))
485 (setq redirect-uri nil
486 success t))
487 (305
488 ;; The requested resource MUST be accessed through the
489 ;; proxy given by the Location field. The Location field
490 ;; gives the URI of the proxy. The recipient is expected
491 ;; to repeat this single request via the proxy. 305
492 ;; responses MUST only be generated by origin servers.
493 (error "Redirection thru a proxy server not supported: %s"
494 redirect-uri))
495 (otherwise
496 ;; Treat everything like '300'
497 nil))
498 (when redirect-uri
499 ;; Clean off any whitespace and/or <...> cruft.
500 (if (string-match "\\([^ \t]+\\)[ \t]" redirect-uri)
501 (setq redirect-uri (match-string 1 redirect-uri)))
502 (if (string-match "^<\\(.*\\)>$" redirect-uri)
503 (setq redirect-uri (match-string 1 redirect-uri)))
504
505 ;; Some stupid sites (like sourceforge) send a
506 ;; non-fully-qualified URL (ie: /), which royally confuses
507 ;; the URL library.
508 (if (not (string-match url-nonrelative-link redirect-uri))
12f1edc8
SM
509 ;; Be careful to use the real target URL, otherwise we may
510 ;; compute the redirection relative to the URL of the proxy.
511 (setq redirect-uri
512 (url-expand-file-name redirect-uri url-http-target-url)))
513 (let ((url-request-method url-http-method)
8c8b8430
SM
514 (url-request-data url-http-data)
515 (url-request-extra-headers url-http-extra-headers))
55d1d8b7
SM
516 ;; Put in the current buffer a forwarding pointer to the new
517 ;; destination buffer.
518 ;; FIXME: This is a hack to fix url-retrieve-synchronously
519 ;; without changing the API. Instead url-retrieve should
520 ;; either simply not return the "destination" buffer, or it
521 ;; should take an optional `dest-buf' argument.
522 (set (make-local-variable 'url-redirect-buffer)
523 (url-retrieve redirect-uri url-callback-function
524 (cons :redirect
525 (cons redirect-uri
526 url-callback-arguments))))
8c8b8430
SM
527 (url-mark-buffer-as-dead (current-buffer))))))
528 (4 ; Client error
529 ;; 400 Bad Request
530 ;; 401 Unauthorized
531 ;; 402 Payment required
532 ;; 403 Forbidden
533 ;; 404 Not found
534 ;; 405 Method not allowed
535 ;; 406 Not acceptable
536 ;; 407 Proxy authentication required
537 ;; 408 Request time-out
538 ;; 409 Conflict
539 ;; 410 Gone
540 ;; 411 Length required
541 ;; 412 Precondition failed
542 ;; 413 Request entity too large
543 ;; 414 Request-URI too large
544 ;; 415 Unsupported media type
545 ;; 416 Requested range not satisfiable
546 ;; 417 Expectation failed
547 ;; 422 Unprocessable Entity (Added by DAV)
548 ;; 423 Locked
549 ;; 424 Failed Dependency
550 (case url-http-response-status
551 (401
552 ;; The request requires user authentication. The response
553 ;; MUST include a WWW-Authenticate header field containing a
554 ;; challenge applicable to the requested resource. The
555 ;; client MAY repeat the request with a suitable
556 ;; Authorization header field.
557 (url-http-handle-authentication nil))
558 (402
559 ;; This code is reserved for future use
560 (url-mark-buffer-as-dead (current-buffer))
561 (error "Somebody wants you to give them money"))
562 (403
563 ;; The server understood the request, but is refusing to
564 ;; fulfill it. Authorization will not help and the request
565 ;; SHOULD NOT be repeated.
566 (setq success t))
567 (404
568 ;; Not found
569 (setq success t))
570 (405
571 ;; The method specified in the Request-Line is not allowed
572 ;; for the resource identified by the Request-URI. The
573 ;; response MUST include an Allow header containing a list of
574 ;; valid methods for the requested resource.
575 (setq success t))
576 (406
577 ;; The resource identified by the request is only capable of
578 ;; generating response entities which have content
579 ;; characteristics nota cceptable according to the accept
580 ;; headers sent in the request.
581 (setq success t))
582 (407
583 ;; This code is similar to 401 (Unauthorized), but indicates
584 ;; that the client must first authenticate itself with the
585 ;; proxy. The proxy MUST return a Proxy-Authenticate header
586 ;; field containing a challenge applicable to the proxy for
587 ;; the requested resource.
588 (url-http-handle-authentication t))
589 (408
590 ;; The client did not produce a request within the time that
591 ;; the server was prepared to wait. The client MAY repeat
592 ;; the request without modifications at any later time.
593 (setq success t))
594 (409
595 ;; The request could not be completed due to a conflict with
596 ;; the current state of the resource. This code is only
597 ;; allowed in situations where it is expected that the user
598 ;; mioght be able to resolve the conflict and resubmit the
599 ;; request. The response body SHOULD include enough
600 ;; information for the user to recognize the source of the
601 ;; conflict.
602 (setq success t))
603 (410
604 ;; The requested resource is no longer available at the
605 ;; server and no forwarding address is known.
606 (setq success t))
607 (411
608 ;; The server refuses to accept the request without a defined
609 ;; Content-Length. The client MAY repeat the request if it
610 ;; adds a valid Content-Length header field containing the
611 ;; length of the message-body in the request message.
612 ;;
613 ;; NOTE - this will never happen because
614 ;; `url-http-create-request' automatically calculates the
615 ;; content-length.
616 (setq success t))
617 (412
618 ;; The precondition given in one or more of the
619 ;; request-header fields evaluated to false when it was
620 ;; tested on the server.
621 (setq success t))
622 ((413 414)
623 ;; The server is refusing to process a request because the
624 ;; request entity|URI is larger than the server is willing or
625 ;; able to process.
626 (setq success t))
627 (415
628 ;; The server is refusing to service the request because the
629 ;; entity of the request is in a format not supported by the
630 ;; requested resource for the requested method.
631 (setq success t))
632 (416
633 ;; A server SHOULD return a response with this status code if
634 ;; a request included a Range request-header field, and none
635 ;; of the range-specifier values in this field overlap the
636 ;; current extent of the selected resource, and the request
637 ;; did not include an If-Range request-header field.
638 (setq success t))
639 (417
640 ;; The expectation given in an Expect request-header field
641 ;; could not be met by this server, or, if the server is a
642 ;; proxy, the server has unambiguous evidence that the
643 ;; request could not be met by the next-hop server.
644 (setq success t))
645 (otherwise
646 ;; The request could not be understood by the server due to
647 ;; malformed syntax. The client SHOULD NOT repeat the
648 ;; request without modifications.
649 (setq success t))))
650 (5
651 ;; 500 Internal server error
652 ;; 501 Not implemented
653 ;; 502 Bad gateway
654 ;; 503 Service unavailable
655 ;; 504 Gateway time-out
656 ;; 505 HTTP version not supported
657 ;; 507 Insufficient storage
658 (setq success t)
659 (case url-http-response-status
660 (501
661 ;; The server does not support the functionality required to
662 ;; fulfill the request.
663 nil)
664 (502
665 ;; The server, while acting as a gateway or proxy, received
666 ;; an invalid response from the upstream server it accessed
667 ;; in attempting to fulfill the request.
668 nil)
669 (503
670 ;; The server is currently unable to handle the request due
671 ;; to a temporary overloading or maintenance of the server.
672 ;; The implication is that this is a temporary condition
673 ;; which will be alleviated after some delay. If known, the
674 ;; length of the delay MAY be indicated in a Retry-After
675 ;; header. If no Retry-After is given, the client SHOULD
676 ;; handle the response as it would for a 500 response.
677 nil)
678 (504
679 ;; The server, while acting as a gateway or proxy, did not
680 ;; receive a timely response from the upstream server
681 ;; specified by the URI (e.g. HTTP, FTP, LDAP) or some other
682 ;; auxiliary server (e.g. DNS) it needed to access in
683 ;; attempting to complete the request.
684 nil)
685 (505
686 ;; The server does not support, or refuses to support, the
687 ;; HTTP protocol version that was used in the request
688 ;; message.
689 nil)
690 (507 ; DAV
691 ;; The method could not be performed on the resource
692 ;; because the server is unable to store the representation
693 ;; needed to successfully complete the request. This
694 ;; condition is considered to be temporary. If the request
695 ;; which received this status code was the result of a user
696 ;; action, the request MUST NOT be repeated until it is
697 ;; requested by a separate user action.
698 nil)))
699 (otherwise
700 (error "Unknown class of HTTP response code: %d (%d)"
701 class url-http-response-status)))
702 (if (not success)
703 (url-mark-buffer-as-dead (current-buffer)))
704 (url-http-debug "Finished parsing HTTP headers: %S" success)
705 (widen)
706 success))
707
708;; Miscellaneous
709(defun url-http-activate-callback ()
710 "Activate callback specified when this buffer was created."
711 (declare (special url-http-process
712 url-callback-function
713 url-callback-arguments))
714 (url-http-mark-connection-as-free (url-host url-current-object)
715 (url-port url-current-object)
716 url-http-process)
717 (url-http-debug "Activating callback in buffer (%s)" (buffer-name))
718 (apply url-callback-function url-callback-arguments))
719
720;; )
721
722;; These unfortunately cannot be macros... please ignore them!
723(defun url-http-idle-sentinel (proc why)
724 "Remove this (now defunct) process PROC from the list of open connections."
725 (maphash (lambda (key val)
726 (if (memq proc val)
727 (puthash key (delq proc val) url-http-open-connections)))
728 url-http-open-connections))
729
730(defun url-http-end-of-document-sentinel (proc why)
731 ;; Sentinel used for old HTTP/0.9 or connections we know are going
732 ;; to die as the 'end of document' notifier.
733 (url-http-debug "url-http-end-of-document-sentinel in buffer (%s)"
734 (process-buffer proc))
735 (url-http-idle-sentinel proc why)
12f1edc8 736 (with-current-buffer (process-buffer proc)
8c8b8430
SM
737 (goto-char (point-min))
738 (if (not (looking-at "HTTP/"))
739 ;; HTTP/0.9 just gets passed back no matter what
740 (url-http-activate-callback)
741 (if (url-http-parse-headers)
742 (url-http-activate-callback)))))
743
744(defun url-http-simple-after-change-function (st nd length)
745 ;; Function used when we do NOT know how long the document is going to be
746 ;; Just _very_ simple 'downloaded %d' type of info.
747 (declare (special url-http-end-of-headers))
748 (url-lazy-message "Reading %s..." (url-pretty-length nd)))
749
750(defun url-http-content-length-after-change-function (st nd length)
751 "Function used when we DO know how long the document is going to be.
752More sophisticated percentage downloaded, etc.
753Also does minimal parsing of HTTP headers and will actually cause
754the callback to be triggered."
755 (declare (special url-current-object
756 url-http-end-of-headers
757 url-http-content-length
758 url-http-content-type
759 url-http-process))
760 (if url-http-content-type
761 (url-display-percentage
762 "Reading [%s]... %s of %s (%d%%)"
763 (url-percentage (- nd url-http-end-of-headers)
764 url-http-content-length)
765 url-http-content-type
766 (url-pretty-length (- nd url-http-end-of-headers))
767 (url-pretty-length url-http-content-length)
768 (url-percentage (- nd url-http-end-of-headers)
769 url-http-content-length))
770 (url-display-percentage
771 "Reading... %s of %s (%d%%)"
772 (url-percentage (- nd url-http-end-of-headers)
773 url-http-content-length)
774 (url-pretty-length (- nd url-http-end-of-headers))
775 (url-pretty-length url-http-content-length)
776 (url-percentage (- nd url-http-end-of-headers)
777 url-http-content-length)))
778
779 (if (> (- nd url-http-end-of-headers) url-http-content-length)
780 (progn
781 ;; Found the end of the document! Wheee!
782 (url-display-percentage nil nil)
783 (message "Reading... done.")
784 (if (url-http-parse-headers)
785 (url-http-activate-callback)))))
786
787(defun url-http-chunked-encoding-after-change-function (st nd length)
788 "Function used when dealing with 'chunked' encoding.
789Cannot give a sophisticated percentage, but we need a different
790function to look for the special 0-length chunk that signifies
791the end of the document."
792 (declare (special url-current-object
793 url-http-end-of-headers
794 url-http-content-type
795 url-http-chunked-length
796 url-http-chunked-counter
797 url-http-process url-http-chunked-start))
798 (save-excursion
799 (goto-char st)
800 (let ((read-next-chunk t)
801 (case-fold-search t)
802 (regexp nil)
803 (no-initial-crlf nil))
804 ;; We need to loop thru looking for more chunks even within
805 ;; one after-change-function call.
806 (while read-next-chunk
807 (setq no-initial-crlf (= 0 url-http-chunked-counter))
808 (if url-http-content-type
809 (url-display-percentage nil
810 "Reading [%s]... chunk #%d"
811 url-http-content-type url-http-chunked-counter)
812 (url-display-percentage nil
813 "Reading... chunk #%d"
814 url-http-chunked-counter))
815 (url-http-debug "Reading chunk %d (%d %d %d)"
816 url-http-chunked-counter st nd length)
817 (setq regexp (if no-initial-crlf
818 "\\([0-9a-z]+\\).*\r?\n"
819 "\r?\n\\([0-9a-z]+\\).*\r?\n"))
820
821 (if url-http-chunked-start
822 ;; We know how long the chunk is supposed to be, skip over
823 ;; leading crap if possible.
824 (if (> nd (+ url-http-chunked-start url-http-chunked-length))
825 (progn
826 (url-http-debug "Got to the end of chunk #%d!"
827 url-http-chunked-counter)
828 (goto-char (+ url-http-chunked-start
829 url-http-chunked-length)))
830 (url-http-debug "Still need %d bytes to hit end of chunk"
831 (- (+ url-http-chunked-start
832 url-http-chunked-length)
833 nd))
834 (setq read-next-chunk nil)))
835 (if (not read-next-chunk)
836 (url-http-debug "Still spinning for next chunk...")
837 (if no-initial-crlf (skip-chars-forward "\r\n"))
838 (if (not (looking-at regexp))
839 (progn
840 ;; Must not have received the entirety of the chunk header,
841 ;; need to spin some more.
842 (url-http-debug "Did not see start of chunk @ %d!" (point))
843 (setq read-next-chunk nil))
844 (add-text-properties (match-beginning 0) (match-end 0)
845 (list 'start-open t
846 'end-open t
847 'chunked-encoding t
cc38a294 848 'face 'cursor
8c8b8430 849 'invisible t))
216d3806
JB
850 (setq url-http-chunked-length (string-to-number (buffer-substring
851 (match-beginning 1)
852 (match-end 1))
853 16)
8c8b8430
SM
854 url-http-chunked-counter (1+ url-http-chunked-counter)
855 url-http-chunked-start (set-marker
856 (or url-http-chunked-start
857 (make-marker))
858 (match-end 0)))
859; (if (not url-http-debug)
860 (delete-region (match-beginning 0) (match-end 0));)
861 (url-http-debug "Saw start of chunk %d (length=%d, start=%d"
862 url-http-chunked-counter url-http-chunked-length
863 (marker-position url-http-chunked-start))
864 (if (= 0 url-http-chunked-length)
865 (progn
866 ;; Found the end of the document! Wheee!
867 (url-http-debug "Saw end of stream chunk!")
868 (setq read-next-chunk nil)
869 (url-display-percentage nil nil)
870 (goto-char (match-end 1))
871 (if (re-search-forward "^\r*$" nil t)
63db9644 872 (url-http-debug "Saw end of trailers..."))
8c8b8430
SM
873 (if (url-http-parse-headers)
874 (url-http-activate-callback))))))))))
875
876(defun url-http-wait-for-headers-change-function (st nd length)
877 ;; This will wait for the headers to arrive and then splice in the
878 ;; next appropriate after-change-function, etc.
879 (declare (special url-current-object
880 url-http-end-of-headers
881 url-http-content-type
882 url-http-content-length
883 url-http-transfer-encoding
884 url-callback-function
885 url-callback-arguments
886 url-http-process
887 url-http-method
888 url-http-after-change-function
889 url-http-response-status))
890 (url-http-debug "url-http-wait-for-headers-change-function (%s)"
891 (buffer-name))
892 (if (not (bobp))
893 (let ((end-of-headers nil)
894 (old-http nil)
895 (content-length nil))
896 (goto-char (point-min))
897 (if (not (looking-at "^HTTP/[1-9]\\.[0-9]"))
898 ;; Not HTTP/x.y data, must be 0.9
899 ;; God, I wish this could die.
900 (setq end-of-headers t
901 url-http-end-of-headers 0
902 old-http t)
903 (if (re-search-forward "^\r*$" nil t)
904 ;; Saw the end of the headers
905 (progn
906 (url-http-debug "Saw end of headers... (%s)" (buffer-name))
907 (setq url-http-end-of-headers (set-marker (make-marker)
908 (point))
909 end-of-headers t)
910 (url-http-clean-headers))))
911
912 (if (not end-of-headers)
913 ;; Haven't seen the end of the headers yet, need to wait
914 ;; for more data to arrive.
915 nil
916 (if old-http
917 (message "HTTP/0.9 How I hate thee!")
918 (progn
919 (url-http-parse-response)
920 (mail-narrow-to-head)
921 ;;(narrow-to-region (point-min) url-http-end-of-headers)
922 (setq url-http-transfer-encoding (mail-fetch-field
923 "transfer-encoding")
924 url-http-content-type (mail-fetch-field "content-type"))
925 (if (mail-fetch-field "content-length")
926 (setq url-http-content-length
216d3806 927 (string-to-number (mail-fetch-field "content-length"))))
8c8b8430
SM
928 (widen)))
929 (if url-http-transfer-encoding
930 (setq url-http-transfer-encoding
931 (downcase url-http-transfer-encoding)))
932
933 (cond
934 ((or (= url-http-response-status 204)
935 (= url-http-response-status 205))
936 (url-http-debug "%d response must have headers only (%s)."
937 url-http-response-status (buffer-name))
938 (if (url-http-parse-headers)
939 (url-http-activate-callback)))
940 ((string= "HEAD" url-http-method)
941 ;; A HEAD request is _ALWAYS_ terminated by the header
942 ;; information, regardless of any entity headers,
943 ;; according to section 4.4 of the HTTP/1.1 draft.
944 (url-http-debug "HEAD request must have headers only (%s)."
945 (buffer-name))
946 (if (url-http-parse-headers)
947 (url-http-activate-callback)))
948 ((string= "CONNECT" url-http-method)
949 ;; A CONNECT request is finished, but we cannot stick this
950 ;; back on the free connectin list
951 (url-http-debug "CONNECT request must have headers only.")
952 (if (url-http-parse-headers)
953 (url-http-activate-callback)))
954 ((equal url-http-response-status 304)
955 ;; Only allowed to have a header section. We have to handle
956 ;; this here instead of in url-http-parse-headers because if
957 ;; you have a cached copy of something without a known
958 ;; content-length, and try to retrieve it from the cache, we'd
959 ;; fall into the 'being dumb' section and wait for the
960 ;; connection to terminate, which means we'd wait for 10
961 ;; seconds for the keep-alives to time out on some servers.
962 (if (url-http-parse-headers)
963 (url-http-activate-callback)))
964 (old-http
965 ;; HTTP/0.9 always signaled end-of-connection by closing the
966 ;; connection.
967 (url-http-debug
968 "Saw HTTP/0.9 response, connection closed means end of document.")
969 (setq url-http-after-change-function
970 'url-http-simple-after-change-function))
971 ((equal url-http-transfer-encoding "chunked")
972 (url-http-debug "Saw chunked encoding.")
973 (setq url-http-after-change-function
974 'url-http-chunked-encoding-after-change-function)
975 (if (> nd url-http-end-of-headers)
976 (progn
977 (url-http-debug
978 "Calling initial chunked-encoding for extra data at end of headers")
979 (url-http-chunked-encoding-after-change-function
980 (marker-position url-http-end-of-headers) nd
981 (- nd url-http-end-of-headers)))))
982 ((integerp url-http-content-length)
983 (url-http-debug
984 "Got a content-length, being smart about document end.")
985 (setq url-http-after-change-function
986 'url-http-content-length-after-change-function)
987 (cond
988 ((= 0 url-http-content-length)
989 ;; We got a NULL body! Activate the callback
990 ;; immediately!
991 (url-http-debug
992 "Got 0-length content-length, activating callback immediately.")
993 (if (url-http-parse-headers)
994 (url-http-activate-callback)))
995 ((> nd url-http-end-of-headers)
996 ;; Have some leftover data
997 (url-http-debug "Calling initial content-length for extra data at end of headers")
998 (url-http-content-length-after-change-function
999 (marker-position url-http-end-of-headers)
1000 nd
1001 (- nd url-http-end-of-headers)))
1002 (t
1003 nil)))
1004 (t
1005 (url-http-debug "No content-length, being dumb.")
1006 (setq url-http-after-change-function
1007 'url-http-simple-after-change-function)))))
1008 ;; We are still at the beginning of the buffer... must just be
1009 ;; waiting for a response.
1010 (url-http-debug "Spinning waiting for headers..."))
1011 (goto-char (point-max)))
1012
1013;;;###autoload
1014(defun url-http (url callback cbargs)
1015 "Retrieve URL via HTTP asynchronously.
1016URL must be a parsed URL. See `url-generic-parse-url' for details.
1017When retrieval is completed, the function CALLBACK is executed with
1018CBARGS as the arguments."
1019 (check-type url vector "Need a pre-parsed URL.")
1020 (declare (special url-current-object
1021 url-http-end-of-headers
1022 url-http-content-type
1023 url-http-content-length
1024 url-http-transfer-encoding
1025 url-http-after-change-function
1026 url-callback-function
1027 url-callback-arguments
1028 url-http-method
1029 url-http-extra-headers
1030 url-http-data
1031 url-http-chunked-length
1032 url-http-chunked-start
1033 url-http-chunked-counter
1034 url-http-process))
1035 (let ((connection (url-http-find-free-connection (url-host url)
1036 (url-port url)))
1037 (buffer (generate-new-buffer (format " *http %s:%d*"
1038 (url-host url)
1039 (url-port url)))))
1040 (if (not connection)
1041 ;; Failed to open the connection for some reason
1042 (progn
1043 (kill-buffer buffer)
1044 (setq buffer nil)
1045 (error "Could not create connection to %s:%d" (url-host url)
1046 (url-port url)))
12f1edc8 1047 (with-current-buffer buffer
8c8b8430
SM
1048 (mm-disable-multibyte)
1049 (setq url-current-object url
1050 mode-line-format "%b [%s]")
1051
1052 (dolist (var '(url-http-end-of-headers
1053 url-http-content-type
1054 url-http-content-length
1055 url-http-transfer-encoding
1056 url-http-after-change-function
1057 url-http-response-status
1058 url-http-chunked-length
1059 url-http-chunked-counter
1060 url-http-chunked-start
1061 url-callback-function
1062 url-callback-arguments
1063 url-http-process
1064 url-http-method
1065 url-http-extra-headers
cacfe88b 1066 url-http-data
12f1edc8 1067 url-http-target-url))
8c8b8430
SM
1068 (set (make-local-variable var) nil))
1069
1070 (setq url-http-method (or url-request-method "GET")
1071 url-http-extra-headers url-request-extra-headers
1072 url-http-data url-request-data
1073 url-http-process connection
1074 url-http-chunked-length nil
1075 url-http-chunked-start nil
1076 url-http-chunked-counter 0
1077 url-callback-function callback
1078 url-callback-arguments cbargs
cacfe88b 1079 url-http-after-change-function 'url-http-wait-for-headers-change-function
12f1edc8
SM
1080 url-http-target-url (if (boundp 'proxy-object)
1081 proxy-object
1082 url-current-object))
8c8b8430
SM
1083
1084 (set-process-buffer connection buffer)
1085 (set-process-sentinel connection 'url-http-end-of-document-sentinel)
1086 (set-process-filter connection 'url-http-generic-filter)
1087 (process-send-string connection (url-http-create-request url))))
1088 buffer))
1089
1090;; Since Emacs 19/20 does not allow you to change the
1091;; `after-change-functions' hook in the midst of running them, we fake
1092;; an after change by hooking into the process filter and inserting
1093;; the data ourselves. This is slightly less efficient, but there
1094;; were tons of weird ways the after-change code was biting us in the
1095;; shorts.
1096(defun url-http-generic-filter (proc data)
1097 ;; Sometimes we get a zero-length data chunk after the process has
1098 ;; been changed to 'free', which means it has no buffer associated
1099 ;; with it. Do nothing if there is no buffer, or 0 length data.
1100 (declare (special url-http-after-change-function))
1101 (and (process-buffer proc)
1102 (/= (length data) 0)
12f1edc8 1103 (with-current-buffer (process-buffer proc)
8c8b8430
SM
1104 (url-http-debug "Calling after change function `%s' for `%S'" url-http-after-change-function proc)
1105 (funcall url-http-after-change-function
1106 (point-max)
1107 (progn
1108 (goto-char (point-max))
1109 (insert data)
1110 (point-max))
1111 (length data)))))
1112
1113;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1114;;; file-name-handler stuff from here on out
1115;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8dff56de
SM
1116(defalias 'url-http-symbol-value-in-buffer
1117 (if (fboundp 'symbol-value-in-buffer)
1118 'symbol-value-in-buffer
1119 (lambda (symbol buffer &optional unbound-value)
8c8b8430 1120 "Return the value of SYMBOL in BUFFER, or UNBOUND-VALUE if it is unbound."
12f1edc8 1121 (with-current-buffer buffer
8dff56de
SM
1122 (if (not (boundp symbol))
1123 unbound-value
1124 (symbol-value symbol))))))
8c8b8430
SM
1125
1126(defun url-http-head (url)
1127 (let ((url-request-method "HEAD")
1128 (url-request-data nil))
1129 (url-retrieve-synchronously url)))
1130
1131;;;###autoload
1132(defun url-http-file-exists-p (url)
b43eb9ab 1133 (let ((status nil)
8c8b8430
SM
1134 (exists nil)
1135 (buffer (url-http-head url)))
1136 (if (not buffer)
1137 (setq exists nil)
1138 (setq status (url-http-symbol-value-in-buffer 'url-http-response-status
1139 buffer 500)
1140 exists (and (>= status 200) (< status 300)))
1141 (kill-buffer buffer))
1142 exists))
1143
1144;;;###autoload
1145(defalias 'url-http-file-readable-p 'url-http-file-exists-p)
1146
3f19601e 1147(defun url-http-head-file-attributes (url &optional id-format)
8c8b8430
SM
1148 (let ((buffer (url-http-head url))
1149 (attributes nil))
1150 (when buffer
1151 (setq attributes (make-list 11 nil))
1152 (setf (nth 1 attributes) 1) ; Number of links to file
1153 (setf (nth 2 attributes) 0) ; file uid
1154 (setf (nth 3 attributes) 0) ; file gid
1155 (setf (nth 7 attributes) ; file size
1156 (url-http-symbol-value-in-buffer 'url-http-content-length
1157 buffer -1))
1158 (setf (nth 8 attributes) (eval-when-compile (make-string 10 ?-)))
1159 (kill-buffer buffer))
1160 attributes))
1161
1162;;;###autoload
3f19601e 1163(defun url-http-file-attributes (url &optional id-format)
8c8b8430 1164 (if (url-dav-supported-p url)
3f19601e
SM
1165 (url-dav-file-attributes url id-format)
1166 (url-http-head-file-attributes url id-format)))
8c8b8430
SM
1167
1168;;;###autoload
1169(defun url-http-options (url)
55d1d8b7 1170 "Return a property list describing options available for URL.
8c8b8430
SM
1171This list is retrieved using the `OPTIONS' HTTP method.
1172
1173Property list members:
1174
1175methods
1176 A list of symbols specifying what HTTP methods the resource
1177 supports.
1178
1179dav
1180 A list of numbers specifying what DAV protocol/schema versions are
1181 supported.
1182
1183dasl
1184 A list of supported DASL search types supported (string form)
1185
1186ranges
1187 A list of the units available for use in partial document fetches.
1188
1189p3p
1190 The `Platform For Privacy Protection' description for the resource.
1191 Currently this is just the raw header contents. This is likely to
1192 change once P3P is formally supported by the URL package or
55d1d8b7 1193 Emacs/W3."
8c8b8430
SM
1194 (let* ((url-request-method "OPTIONS")
1195 (url-request-data nil)
1196 (buffer (url-retrieve-synchronously url))
1197 (header nil)
1198 (options nil))
1199 (when (and buffer (= 2 (/ (url-http-symbol-value-in-buffer
1200 'url-http-response-status buffer 0) 100)))
1201 ;; Only parse the options if we got a 2xx response code!
12f1edc8 1202 (with-current-buffer buffer
8c8b8430
SM
1203 (save-restriction
1204 (save-match-data
8c8b8430
SM
1205 (mail-narrow-to-head)
1206
1207 ;; Figure out what methods are supported.
1208 (when (setq header (mail-fetch-field "allow"))
1209 (setq options (plist-put
1210 options 'methods
1211 (mapcar 'intern (split-string header "[ ,]+")))))
1212
1213 ;; Check for DAV
1214 (when (setq header (mail-fetch-field "dav"))
1215 (setq options (plist-put
1216 options 'dav
1217 (delq 0
1218 (mapcar 'string-to-number
1219 (split-string header "[, ]+"))))))
1220
1221 ;; Now for DASL
1222 (when (setq header (mail-fetch-field "dasl"))
1223 (setq options (plist-put
1224 options 'dasl
1225 (split-string header "[, ]+"))))
1226
1227 ;; P3P - should get more detailed here. FIXME
1228 (when (setq header (mail-fetch-field "p3p"))
1229 (setq options (plist-put options 'p3p header)))
1230
1231 ;; Check for whether they accept byte-range requests.
1232 (when (setq header (mail-fetch-field "accept-ranges"))
1233 (setq options (plist-put
1234 options 'ranges
1235 (delq 'none
1236 (mapcar 'intern
1237 (split-string header "[, ]+"))))))
1238 ))))
1239 (if buffer (kill-buffer buffer))
1240 options))
1241
1242(provide 'url-http)
1243
b43eb9ab 1244;; arch-tag: ba7c59ae-c0f4-4a31-9617-d85f221732ee
8c8b8430 1245;;; url-http.el ends here