diff lisp/w3/url-cache.el @ 26:441bb1e64a06 r19-15b96

Import from CVS: tag r19-15b96
author cvs
date Mon, 13 Aug 2007 08:51:32 +0200
parents
children ec9a17fef872
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/w3/url-cache.el	Mon Aug 13 08:51:32 2007 +0200
@@ -0,0 +1,317 @@
+;;; url-cache.el --- Uniform Resource Locator retrieval tool
+;; Author: wmperry
+;; Created: 1997/02/20 15:33:47
+;; Version: 1.3
+;; Keywords: comm, data, processes, hypermedia
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Copyright (c) 1993-1996 by William M. Perry (wmperry@cs.indiana.edu)
+;;; Copyright (c) 1996, 1997 Free Software Foundation, Inc.
+;;;
+;;; This file is not part of GNU Emacs, but the same permissions apply.
+;;;
+;;; GNU Emacs is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2, or (at your option)
+;;; any later version.
+;;;
+;;; GNU Emacs is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;;; Boston, MA 02111-1307, USA.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(require 'md5)
+
+;; Cache manager
+(defun url-cache-file-writable-p (file)
+  "Follows the documentation of file-writable-p, unlike file-writable-p."
+  (and (file-writable-p file)
+       (if (file-exists-p file)
+           (not (file-directory-p file))
+         (file-directory-p (file-name-directory file)))))
+                
+(defun url-prepare-cache-for-file (file)
+  "Makes it possible to cache data in FILE.
+Creates any necessary parent directories, deleting any non-directory files
+that would stop this.  Returns nil if parent directories can not be
+created.  If FILE already exists as a non-directory, it changes
+permissions of FILE or deletes FILE to make it possible to write a new
+version of FILE.  Returns nil if this can not be done.  Returns nil if
+FILE already exists as a directory.  Otherwise, returns t, indicating that
+FILE can be created or overwritten."
+
+  ;; COMMENT: We don't delete directories because that requires
+  ;; recursively deleting the directories's contents, which might
+  ;; eliminate a substantial portion of the cache.
+
+  (cond
+   ((url-cache-file-writable-p file)
+    t)
+   ((file-directory-p file)
+    nil)
+   (t
+    (catch 'upcff-tag
+      (let ((dir (file-name-directory file))
+            dir-parent dir-last-component)
+        (if (string-equal dir file)
+            ;; *** Should I have a warning here?
+            ;; FILE must match a pattern like /foo/bar/, indicating it is a
+            ;; name only suitable for a directory.  So presume we won't be
+            ;; able to overwrite FILE and return nil.
+            (throw 'upcff-tag nil))
+        
+        ;; Make sure the containing directory exists, or throw a failure
+        ;; if we can't create it.
+        (if (file-directory-p dir)
+            nil
+          (or (fboundp 'make-directory)
+              (throw 'upcff-tag nil))
+          (make-directory dir t)
+          ;; make-directory silently fails if there is an obstacle, so
+          ;; we must verify its results.
+          (if (file-directory-p dir)
+              nil
+            ;; Look at prefixes of the path to find the obstacle that is
+            ;; stopping us from making the directory.  Unfortunately, there
+            ;; is no portable function in Emacs to find the parent directory
+            ;; of a *directory*.  So this code may not work on VMS.
+            (while (progn
+                     (if (eq ?/ (aref dir (1- (length dir))))
+                         (setq dir (substring dir 0 -1))
+                       ;; Maybe we're on VMS where the syntax is different.
+                       (throw 'upcff-tag nil))
+                     (setq dir-parent (file-name-directory dir))
+                     (not (file-directory-p dir-parent)))
+              (setq dir dir-parent))
+            ;; We have found the longest path prefix that exists as a
+            ;; directory.  Deal with any obstacles in this directory.
+            (if (file-exists-p dir)
+                (condition-case nil
+                    (delete-file dir)
+                  (error (throw 'upcff-tag nil))))
+            (if (file-exists-p dir)
+                (throw 'upcff-tag nil))
+            ;; Try making the directory again.
+            (setq dir (file-name-directory file))
+            (make-directory dir t)
+            (or (file-directory-p dir)
+                (throw 'upcff-tag nil))))
+
+        ;; The containing directory exists.  Let's see if there is
+        ;; something in the way in this directory.
+        (if (url-cache-file-writable-p file)
+            (throw 'upcff-tag t)
+          (condition-case nil
+              (delete-file file)
+            (error (throw 'upcff-tag nil))))
+
+        ;; The return value, if we get this far.
+        (url-cache-file-writable-p file))))))
+
+(defvar url-cache-ignored-protocols
+  '("www" "about" "https" "mailto")
+  "*A list of protocols that we should never cache.")
+
+(defun url-cache-cachable-p (obj)
+  ;; return t iff the current buffer is cachable
+  (cond
+   ((null obj)				; Something horribly confused
+    nil)
+   ((member (url-type obj) url-cache-ignored-protocols)
+    ;; We have been told to ignore this type of object
+    nil)
+   ((and (member (url-type obj) '("file" "ftp")) (not (url-host obj)))
+    ;; We never want to cache local files... what's the point?
+    nil)
+   ((member (url-type obj) '("http" "https"))
+    (let* ((status (cdr-safe (assoc "status" url-current-mime-headers)))
+	   (class (if status (/ status 100) 0)))
+      (case class
+	(2				; Various 'OK' statuses
+	 (memq status '(200)))
+	(otherwise nil))))
+   (t
+    nil)))
+
+;;;###autoload
+(defun url-store-in-cache (&optional buff)
+  "Store buffer BUFF in the cache"
+  (if (and buff (get-buffer buff))
+      nil
+    (save-excursion
+      (and buff (set-buffer buff))
+      (if (not (url-cache-cachable-p url-current-object))
+	  nil
+	(let* ((fname (url-create-cached-filename (url-view-url t)))
+	       (fname-hdr (concat fname ".hdr"))
+	       (info (mapcar (function (lambda (var)
+					 (cons (symbol-name var)
+					       (symbol-value var))))
+			     '( url-current-content-length
+				url-current-object
+				url-current-isindex
+				url-current-mime-encoding
+				url-current-mime-headers
+				url-current-mime-type
+				))))
+	  (cond ((and (url-prepare-cache-for-file fname)
+		      (url-prepare-cache-for-file fname-hdr))
+		 (write-region (point-min) (point-max) fname nil 5)
+		 (set-buffer (get-buffer-create " *cache-tmp*"))
+		 (erase-buffer)
+		 (insert "(setq ")
+		 (mapcar
+		  (function
+		   (lambda (x)
+		     (insert (car x) " "
+			     (cond ((null (setq x (cdr x))) "nil")
+				   ((stringp x) (prin1-to-string x))
+				   ((listp x) (concat "'" (prin1-to-string x)))
+				   ((vectorp x) (prin1-to-string x))
+				   ((numberp x) (int-to-string x))
+				   (t "'???")) "\n")))
+		  info)
+		 (insert ")\n")
+		 (write-region (point-min) (point-max) fname-hdr nil 5))))))))
+	
+	     
+;;;###autoload
+(defun url-is-cached (url)
+  "Return non-nil if the URL is cached."
+  (let* ((fname (url-create-cached-filename url))
+	 (attribs (file-attributes fname)))
+    (and fname				; got a filename
+	 (file-exists-p fname)		; file exists
+	 (not (eq (nth 0 attribs) t))	; Its not a directory
+	 (nth 5 attribs))))		; Can get last mod-time
+    
+(defun url-create-cached-filename-using-md5 (url)
+  (if url
+      (expand-file-name (md5 url)
+			(concat url-temporary-directory "/"
+				(user-real-login-name)))))
+
+;;;###autoload
+(defun url-create-cached-filename (url)
+  "Return a filename in the local cache for URL"
+  (if url
+      (let* ((url url)
+	     (urlobj (if (vectorp url)
+			 url
+		       (url-generic-parse-url url)))
+	     (protocol (url-type urlobj))
+	     (hostname (url-host urlobj))
+	     (host-components
+	      (cons
+	       (user-real-login-name)
+	       (cons (or protocol "file")
+		     (nreverse
+		      (delq nil
+			    (mm-string-to-tokens
+			     (or hostname "localhost") ?.))))))
+	     (fname    (url-filename urlobj)))
+	(if (and fname (/= (length fname) 0) (= (aref fname 0) ?/))
+	    (setq fname (substring fname 1 nil)))
+	(if fname
+	    (let ((slash nil))
+	      (setq fname
+		    (mapconcat
+		     (function
+		      (lambda (x)
+			(cond
+			 ((and (= ?/ x) slash)
+			  (setq slash nil)
+			  "%2F")
+			 ((= ?/ x)
+			  (setq slash t)
+			  "/")
+			 (t
+			  (setq slash nil)
+			  (char-to-string x))))) fname ""))))
+
+	(if (and fname (memq system-type '(ms-windows ms-dos windows-nt))
+		 (string-match "\\([A-Za-z]\\):[/\\]" fname))
+	    (setq fname (concat (url-match fname 1) "/"
+				(substring fname (match-end 0)))))
+	
+	(setq fname (and fname
+			 (mapconcat
+			  (function (lambda (x)
+				      (if (= x ?~) "" (char-to-string x))))
+			  fname ""))
+	      fname (cond
+		     ((null fname) nil)
+		     ((or (string= "" fname) (string= "/" fname))
+		      url-directory-index-file)
+		     ((= (string-to-char fname) ?/)
+		      (if (string= (substring fname -1 nil) "/")
+			  (concat fname url-directory-index-file)
+			(substring fname 1 nil)))
+		     (t
+		      (if (string= (substring fname -1 nil) "/")
+			  (concat fname url-directory-index-file)
+			fname))))
+
+	;; Honor hideous 8.3 filename limitations on dos and windows
+	;; we don't have to worry about this in Windows NT/95 (or OS/2?)
+	(if (and fname (memq system-type '(ms-windows ms-dos)))
+	    (let ((base (url-file-extension fname t))
+		  (ext  (url-file-extension fname nil)))
+	      (setq fname (concat (substring base 0 (min 8 (length base)))
+				  (substring ext  0 (min 4 (length ext)))))
+	      (setq host-components
+		    (mapcar
+		     (function
+		      (lambda (x)
+			(if (> (length x) 8)
+			    (concat 
+			     (substring x 0 8) "."
+			     (substring x 8 (min (length x) 11)))
+			  x)))
+		     host-components))))
+
+	(and fname
+	     (expand-file-name fname
+			       (expand-file-name
+				(mapconcat 'identity host-components "/")
+				url-temporary-directory))))))
+
+;;;###autoload
+(defun url-extract-from-cache (fnam)
+  "Extract FNAM from the local disk cache"
+  (set-buffer (get-buffer-create url-working-buffer))
+  (erase-buffer)
+  (setq url-current-mime-viewer nil)
+  (insert-file-contents-literally fnam)
+  (load (concat (if (memq system-type '(ms-windows ms-dos os2))
+		    (url-file-extension fnam t)
+		  fnam) ".hdr") t t)) 
+
+;;;###autoload
+(defun url-cache-expired (url mod)
+  "Return t iff a cached file has expired."
+  (if (not (string-match url-nonrelative-link url))
+      t
+    (let* ((urlobj (url-generic-parse-url url))
+	   (type (url-type urlobj)))
+      (cond
+       (url-standalone-mode
+	(not (file-exists-p (url-create-cached-filename urlobj))))
+       ((string= type "http")
+	(if (not url-standalone-mode) t
+	  (not (file-exists-p (url-create-cached-filename urlobj)))))
+       ((not (fboundp 'current-time))
+	t)
+       ((member type '("file" "ftp"))
+	(if (or (equal mod '(0 0)) (not mod))
+	      (return t)
+	    (or (> (nth 0 mod) (nth 0 (current-time)))
+		(> (nth 1 mod) (nth 1 (current-time))))))
+       (t nil)))))
+
+(provide 'url-cache)