diff options
| author | Daiki Ueno | 2013-10-03 16:11:27 +0900 |
|---|---|---|
| committer | Daiki Ueno | 2013-10-03 16:11:27 +0900 |
| commit | acbadd0046cb1643eeaf8595ede1a69cc25d3158 (patch) | |
| tree | f1b7ffc2d37226d1f20d53b879e008b400f545ae /lisp | |
| parent | 0a858ebfc57a072ae8ab65f509d8a4901a2ec073 (diff) | |
| download | emacs-acbadd0046cb1643eeaf8595ede1a69cc25d3158.tar.gz emacs-acbadd0046cb1643eeaf8595ede1a69cc25d3158.zip | |
Add support for package signature checking.
* lisp/emacs-lisp/package.el (url-http-file-exists-p)
(epg-make-context, epg-context-set-home-directory)
(epg-verify-string, epg-context-result-for)
(epg-signature-status, epg-signature-to-string)
(epg-check-configuration, epg-configuration)
(epg-import-keys-from-file): Declare.
(package-check-signature): New user option.
(package-unsigned-archives): New user option.
(package-desc): Add `signed' field.
(package-load-descriptor): Set `signed' field if .signed file exists.
(package--archive-file-exists-p): New function.
(package--check-signature): New function.
(package-install-from-archive): Check package signature.
(package--download-one-archive): Check archive signature.
(package-delete): Remove .signed file.
(package-import-keyring): New command.
(package-refresh-contents): Import default keyring.
(package-desc-status): Add "unsigned" status.
(describe-package-1, package-menu--print-info)
(package-menu-mark-delete, package-menu--find-upgrades)
(package-menu--status-predicate): Support "unsigned" status.
* test/automated/data/package/signed/archive-contents:
* test/automated/data/package/signed/archive-contents.sig:
* test/automated/data/package/signed/signed-good-1.0.el:
* test/automated/data/package/signed/signed-good-1.0.el.sig:
* test/automated/data/package/signed/signed-bad-1.0.el:
* test/automated/data/package/signed/signed-bad-1.0.el.sig:
* test/automated/data/package/key.pub:
* test/automated/data/package/key.sec: New files.
* test/automated/package-test.el (package-test-update-listing)
(package-test-update-archives, package-test-describe-package):
Adjust to package.el change.
(package-test-signed): New test.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/ChangeLog | 25 | ||||
| -rw-r--r-- | lisp/emacs-lisp/package.el | 196 |
2 files changed, 201 insertions, 20 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 90158b85b4d..936f2b1f8e5 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,28 @@ | |||
| 1 | 2013-10-03 Daiki Ueno <ueno@gnu.org> | ||
| 2 | |||
| 3 | Add support for package signature checking. | ||
| 4 | * emacs-lisp/package.el (url-http-file-exists-p) | ||
| 5 | (epg-make-context, epg-context-set-home-directory) | ||
| 6 | (epg-verify-string, epg-context-result-for) | ||
| 7 | (epg-signature-status, epg-signature-to-string) | ||
| 8 | (epg-check-configuration, epg-configuration) | ||
| 9 | (epg-import-keys-from-file): Declare. | ||
| 10 | (package-check-signature): New user option. | ||
| 11 | (package-unsigned-archives): New user option. | ||
| 12 | (package-desc): Add `signed' field. | ||
| 13 | (package-load-descriptor): Set `signed' field if .signed file exists. | ||
| 14 | (package--archive-file-exists-p): New function. | ||
| 15 | (package--check-signature): New function. | ||
| 16 | (package-install-from-archive): Check package signature. | ||
| 17 | (package--download-one-archive): Check archive signature. | ||
| 18 | (package-delete): Remove .signed file. | ||
| 19 | (package-import-keyring): New command. | ||
| 20 | (package-refresh-contents): Import default keyring. | ||
| 21 | (package-desc-status): Add "unsigned" status. | ||
| 22 | (describe-package-1, package-menu--print-info) | ||
| 23 | (package-menu-mark-delete, package-menu--find-upgrades) | ||
| 24 | (package-menu--status-predicate): Support "unsigned" status. | ||
| 25 | |||
| 1 | 2013-10-03 Stefan Monnier <monnier@iro.umontreal.ca> | 26 | 2013-10-03 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 27 | ||
| 3 | * emacs-lisp/cconv.el (cconv-convert, cconv-analyse-form): Adjust for | 28 | * emacs-lisp/cconv.el (cconv-convert, cconv-analyse-form): Adjust for |
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index ec01d16329f..cdf210498ce 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el | |||
| @@ -206,6 +206,7 @@ If VERSION is nil, the package is not loaded (it is \"disabled\")." | |||
| 206 | (defvar Info-directory-list) | 206 | (defvar Info-directory-list) |
| 207 | (declare-function info-initialize "info" ()) | 207 | (declare-function info-initialize "info" ()) |
| 208 | (declare-function url-http-parse-response "url-http" ()) | 208 | (declare-function url-http-parse-response "url-http" ()) |
| 209 | (declare-function url-http-file-exists-p "url-http" (url)) | ||
| 209 | (declare-function lm-header "lisp-mnt" (header)) | 210 | (declare-function lm-header "lisp-mnt" (header)) |
| 210 | (declare-function lm-commentary "lisp-mnt" (&optional file)) | 211 | (declare-function lm-commentary "lisp-mnt" (&optional file)) |
| 211 | (defvar url-http-end-of-headers) | 212 | (defvar url-http-end-of-headers) |
| @@ -285,6 +286,22 @@ contrast, `package-user-dir' contains packages for personal use." | |||
| 285 | :group 'package | 286 | :group 'package |
| 286 | :version "24.1") | 287 | :version "24.1") |
| 287 | 288 | ||
| 289 | (defcustom package-check-signature 'allow-unsigned | ||
| 290 | "Whether to check package signatures when installing." | ||
| 291 | :type '(choice (const nil :tag "Never") | ||
| 292 | (const allow-unsigned :tag "Allow unsigned") | ||
| 293 | (const t :tag "Check always")) | ||
| 294 | :risky t | ||
| 295 | :group 'package | ||
| 296 | :version "24.1") | ||
| 297 | |||
| 298 | (defcustom package-unsigned-archives nil | ||
| 299 | "A list of archives which do not use package signature." | ||
| 300 | :type '(repeat (string :tag "Archive name")) | ||
| 301 | :risky t | ||
| 302 | :group 'package | ||
| 303 | :version "24.1") | ||
| 304 | |||
| 288 | (defvar package--default-summary "No description available.") | 305 | (defvar package--default-summary "No description available.") |
| 289 | 306 | ||
| 290 | (cl-defstruct (package-desc | 307 | (cl-defstruct (package-desc |
| @@ -340,7 +357,9 @@ Slots: | |||
| 340 | `dir' The directory where the package is installed (if installed), | 357 | `dir' The directory where the package is installed (if installed), |
| 341 | `builtin' if it is built-in, or nil otherwise. | 358 | `builtin' if it is built-in, or nil otherwise. |
| 342 | 359 | ||
| 343 | `extras' Optional alist of additional keyword-value pairs." | 360 | `extras' Optional alist of additional keyword-value pairs. |
| 361 | |||
| 362 | `signed' Flag to indicate that the package is signed by provider." | ||
| 344 | name | 363 | name |
| 345 | version | 364 | version |
| 346 | (summary package--default-summary) | 365 | (summary package--default-summary) |
| @@ -348,7 +367,8 @@ Slots: | |||
| 348 | kind | 367 | kind |
| 349 | archive | 368 | archive |
| 350 | dir | 369 | dir |
| 351 | extras) | 370 | extras |
| 371 | signed) | ||
| 352 | 372 | ||
| 353 | ;; Pseudo fields. | 373 | ;; Pseudo fields. |
| 354 | (defun package-desc-full-name (pkg-desc) | 374 | (defun package-desc-full-name (pkg-desc) |
| @@ -428,7 +448,8 @@ This is, approximately, the inverse of `version-to-list'. | |||
| 428 | (defun package-load-descriptor (pkg-dir) | 448 | (defun package-load-descriptor (pkg-dir) |
| 429 | "Load the description file in directory PKG-DIR." | 449 | "Load the description file in directory PKG-DIR." |
| 430 | (let ((pkg-file (expand-file-name (package--description-file pkg-dir) | 450 | (let ((pkg-file (expand-file-name (package--description-file pkg-dir) |
| 431 | pkg-dir))) | 451 | pkg-dir)) |
| 452 | (signed-file (concat pkg-dir ".signed"))) | ||
| 432 | (when (file-exists-p pkg-file) | 453 | (when (file-exists-p pkg-file) |
| 433 | (with-temp-buffer | 454 | (with-temp-buffer |
| 434 | (insert-file-contents pkg-file) | 455 | (insert-file-contents pkg-file) |
| @@ -436,6 +457,8 @@ This is, approximately, the inverse of `version-to-list'. | |||
| 436 | (let ((pkg-desc (package-process-define-package | 457 | (let ((pkg-desc (package-process-define-package |
| 437 | (read (current-buffer)) pkg-file))) | 458 | (read (current-buffer)) pkg-file))) |
| 438 | (setf (package-desc-dir pkg-desc) pkg-dir) | 459 | (setf (package-desc-dir pkg-desc) pkg-dir) |
| 460 | (if (file-exists-p signed-file) | ||
| 461 | (setf (package-desc-signed pkg-desc) t)) | ||
| 439 | pkg-desc))))) | 462 | pkg-desc))))) |
| 440 | 463 | ||
| 441 | (defun package-load-all-descriptors () | 464 | (defun package-load-all-descriptors () |
| @@ -766,13 +789,87 @@ It will move point to somewhere in the headers." | |||
| 766 | (error "Error during download request:%s" | 789 | (error "Error during download request:%s" |
| 767 | (buffer-substring-no-properties (point) (line-end-position)))))) | 790 | (buffer-substring-no-properties (point) (line-end-position)))))) |
| 768 | 791 | ||
| 792 | (defun package--archive-file-exists-p (location file) | ||
| 793 | (let ((http (string-match "\\`https?:" location))) | ||
| 794 | (if http | ||
| 795 | (progn | ||
| 796 | (require 'url-http) | ||
| 797 | (url-http-file-exists-p (concat location file))) | ||
| 798 | (file-exists-p (expand-file-name file location))))) | ||
| 799 | |||
| 800 | (declare-function epg-make-context "epg" | ||
| 801 | (&optional protocol armor textmode include-certs | ||
| 802 | cipher-algorithm | ||
| 803 | digest-algorithm | ||
| 804 | compress-algorithm)) | ||
| 805 | (declare-function epg-context-set-home-directory "epg" (context directory)) | ||
| 806 | (declare-function epg-verify-string "epg" (context signature | ||
| 807 | &optional signed-text)) | ||
| 808 | (declare-function epg-context-result-for "epg" (context name)) | ||
| 809 | (declare-function epg-signature-status "epg" (signature)) | ||
| 810 | (declare-function epg-signature-to-string "epg" (signature)) | ||
| 811 | |||
| 812 | (defun package--check-signature (location file) | ||
| 813 | "Check signature of the current buffer. | ||
| 814 | GnuPG keyring is located under \"gnupg\" in `package-user-dir'." | ||
| 815 | (let ((context (epg-make-context 'OpenPGP)) | ||
| 816 | (homedir (expand-file-name "gnupg" package-user-dir)) | ||
| 817 | (sig-file (concat file ".sig")) | ||
| 818 | sig-content | ||
| 819 | good-signatures) | ||
| 820 | (condition-case-unless-debug error | ||
| 821 | (setq sig-content (package--with-work-buffer location sig-file | ||
| 822 | (buffer-string))) | ||
| 823 | (error "Failed to download %s: %S" sig-file (cdr error))) | ||
| 824 | (epg-context-set-home-directory context homedir) | ||
| 825 | (epg-verify-string context sig-content (buffer-string)) | ||
| 826 | ;; The .sig file may contain multiple signatures. Success if one | ||
| 827 | ;; of the signatures is good. | ||
| 828 | (setq good-signatures | ||
| 829 | (delq nil (mapcar (lambda (sig) | ||
| 830 | (if (eq (epg-signature-status sig) 'good) | ||
| 831 | sig)) | ||
| 832 | (epg-context-result-for context 'verify)))) | ||
| 833 | (if (null good-signatures) | ||
| 834 | (error "Failed to verify signature %s: %S" | ||
| 835 | sig-file | ||
| 836 | (mapcar #'epg-signature-to-string | ||
| 837 | (epg-context-result-for context 'verify))) | ||
| 838 | good-signatures))) | ||
| 839 | |||
| 769 | (defun package-install-from-archive (pkg-desc) | 840 | (defun package-install-from-archive (pkg-desc) |
| 770 | "Download and install a tar package." | 841 | "Download and install a tar package." |
| 771 | (let ((location (package-archive-base pkg-desc)) | 842 | (let* ((location (package-archive-base pkg-desc)) |
| 772 | (file (concat (package-desc-full-name pkg-desc) | 843 | (file (concat (package-desc-full-name pkg-desc) |
| 773 | (package-desc-suffix pkg-desc)))) | 844 | (package-desc-suffix pkg-desc))) |
| 845 | (sig-file (concat file ".sig")) | ||
| 846 | good-signatures pkg-descs) | ||
| 774 | (package--with-work-buffer location file | 847 | (package--with-work-buffer location file |
| 775 | (package-unpack pkg-desc)))) | 848 | (if (and package-check-signature |
| 849 | (not (member (package-desc-archive pkg-desc) | ||
| 850 | package-unsigned-archives))) | ||
| 851 | (if (package--archive-file-exists-p location sig-file) | ||
| 852 | (setq good-signatures (package--check-signature location file)) | ||
| 853 | (unless (eq package-check-signature 'allow-unsigned) | ||
| 854 | (error "Unsigned package: `%s'" | ||
| 855 | (package-desc-name pkg-desc))))) | ||
| 856 | (package-unpack pkg-desc)) | ||
| 857 | ;; Here the package has been installed successfully, mark it as | ||
| 858 | ;; signed if appropriate. | ||
| 859 | (when good-signatures | ||
| 860 | ;; Write out good signatures into NAME-VERSION.signed file. | ||
| 861 | (write-region (mapconcat #'epg-signature-to-string good-signatures "\n") | ||
| 862 | nil | ||
| 863 | (expand-file-name | ||
| 864 | (concat (package-desc-full-name pkg-desc) | ||
| 865 | ".signed") | ||
| 866 | package-user-dir)) | ||
| 867 | ;; Update the old pkg-desc which will be shown on the description buffer. | ||
| 868 | (setf (package-desc-signed pkg-desc) t) | ||
| 869 | ;; Update the new (activated) pkg-desc as well. | ||
| 870 | (setq pkg-descs (cdr (assq (package-desc-name pkg-desc) package-alist))) | ||
| 871 | (if pkg-descs | ||
| 872 | (setf (package-desc-signed (car pkg-descs)) t))))) | ||
| 776 | 873 | ||
| 777 | (defvar package--initialized nil) | 874 | (defvar package--initialized nil) |
| 778 | 875 | ||
| @@ -1104,6 +1201,10 @@ The file can either be a tar file or an Emacs Lisp file." | |||
| 1104 | (error "Package `%s' is a system package, not deleting" | 1201 | (error "Package `%s' is a system package, not deleting" |
| 1105 | (package-desc-full-name pkg-desc)) | 1202 | (package-desc-full-name pkg-desc)) |
| 1106 | (delete-directory dir t t) | 1203 | (delete-directory dir t t) |
| 1204 | ;; Remove NAME-VERSION.signed file. | ||
| 1205 | (let ((signed-file (concat dir ".signed"))) | ||
| 1206 | (if (file-exists-p signed-file) | ||
| 1207 | (delete-file signed-file))) | ||
| 1107 | ;; Update package-alist. | 1208 | ;; Update package-alist. |
| 1108 | (let* ((name (package-desc-name pkg-desc))) | 1209 | (let* ((name (package-desc-name pkg-desc))) |
| 1109 | (delete pkg-desc (assq name package-alist))) | 1210 | (delete pkg-desc (assq name package-alist))) |
| @@ -1118,16 +1219,50 @@ The file can either be a tar file or an Emacs Lisp file." | |||
| 1118 | ARCHIVE should be a cons cell of the form (NAME . LOCATION), | 1219 | ARCHIVE should be a cons cell of the form (NAME . LOCATION), |
| 1119 | similar to an entry in `package-alist'. Save the cached copy to | 1220 | similar to an entry in `package-alist'. Save the cached copy to |
| 1120 | \"archives/NAME/archive-contents\" in `package-user-dir'." | 1221 | \"archives/NAME/archive-contents\" in `package-user-dir'." |
| 1121 | (let* ((dir (expand-file-name (format "archives/%s" (car archive)) | 1222 | (let ((dir (expand-file-name (format "archives/%s" (car archive)) |
| 1122 | package-user-dir))) | 1223 | package-user-dir)) |
| 1224 | (sig-file (concat file ".sig")) | ||
| 1225 | good-signatures) | ||
| 1123 | (package--with-work-buffer (cdr archive) file | 1226 | (package--with-work-buffer (cdr archive) file |
| 1227 | ;; Check signature of archive-contents, if desired. | ||
| 1228 | (if (and package-check-signature | ||
| 1229 | (not (member archive package-unsigned-archives))) | ||
| 1230 | (if (package--archive-file-exists-p (cdr archive) sig-file) | ||
| 1231 | (setq good-signatures (package--check-signature (cdr archive) | ||
| 1232 | file)) | ||
| 1233 | (unless (eq package-check-signature 'allow-unsigned) | ||
| 1234 | (error "Unsigned archive `%s'" | ||
| 1235 | (car archive))))) | ||
| 1124 | ;; Read the retrieved buffer to make sure it is valid (e.g. it | 1236 | ;; Read the retrieved buffer to make sure it is valid (e.g. it |
| 1125 | ;; may fetch a URL redirect page). | 1237 | ;; may fetch a URL redirect page). |
| 1126 | (when (listp (read buffer)) | 1238 | (when (listp (read buffer)) |
| 1127 | (make-directory dir t) | 1239 | (make-directory dir t) |
| 1128 | (setq buffer-file-name (expand-file-name file dir)) | 1240 | (setq buffer-file-name (expand-file-name file dir)) |
| 1129 | (let ((version-control 'never)) | 1241 | (let ((version-control 'never)) |
| 1130 | (save-buffer)))))) | 1242 | (save-buffer)))) |
| 1243 | (when good-signatures | ||
| 1244 | ;; Write out good signatures into archive-contents.signed file. | ||
| 1245 | (write-region (mapconcat #'epg-signature-to-string good-signatures "\n") | ||
| 1246 | nil | ||
| 1247 | (expand-file-name (concat file ".signed") dir))))) | ||
| 1248 | |||
| 1249 | (declare-function epg-check-configuration "epg-config" | ||
| 1250 | (config &optional minimum-version)) | ||
| 1251 | (declare-function epg-configuration "epg-config" ()) | ||
| 1252 | (declare-function epg-import-keys-from-file "epg" (context keys)) | ||
| 1253 | |||
| 1254 | ;;;###autoload | ||
| 1255 | (defun package-import-keyring (&optional file) | ||
| 1256 | "Import keys from FILE." | ||
| 1257 | (interactive "fFile: ") | ||
| 1258 | (setq file (expand-file-name file)) | ||
| 1259 | (let ((context (epg-make-context 'OpenPGP)) | ||
| 1260 | (homedir (expand-file-name "gnupg" package-user-dir))) | ||
| 1261 | (make-directory homedir t) | ||
| 1262 | (epg-context-set-home-directory context homedir) | ||
| 1263 | (message "Importing %s..." (file-name-nondirectory file)) | ||
| 1264 | (epg-import-keys-from-file context file) | ||
| 1265 | (message "Importing %s...done" (file-name-nondirectory file)))) | ||
| 1131 | 1266 | ||
| 1132 | ;;;###autoload | 1267 | ;;;###autoload |
| 1133 | (defun package-refresh-contents () | 1268 | (defun package-refresh-contents () |
| @@ -1138,6 +1273,14 @@ makes them available for download." | |||
| 1138 | ;; FIXME: Do it asynchronously. | 1273 | ;; FIXME: Do it asynchronously. |
| 1139 | (unless (file-exists-p package-user-dir) | 1274 | (unless (file-exists-p package-user-dir) |
| 1140 | (make-directory package-user-dir t)) | 1275 | (make-directory package-user-dir t)) |
| 1276 | (let ((default-keyring (expand-file-name "package-keyring.gpg" | ||
| 1277 | data-directory))) | ||
| 1278 | (if (file-exists-p default-keyring) | ||
| 1279 | (condition-case-unless-debug error | ||
| 1280 | (progn | ||
| 1281 | (epg-check-configuration (epg-configuration)) | ||
| 1282 | (package-import-keyring default-keyring)) | ||
| 1283 | (error (message "Cannot import default keyring: %S" (cdr error)))))) | ||
| 1141 | (dolist (archive package-archives) | 1284 | (dolist (archive package-archives) |
| 1142 | (condition-case-unless-debug nil | 1285 | (condition-case-unless-debug nil |
| 1143 | (package--download-one-archive archive "archive-contents") | 1286 | (package--download-one-archive archive "archive-contents") |
| @@ -1209,7 +1352,8 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages." | |||
| 1209 | (homepage (if desc (cdr (assoc :url (package-desc-extras desc))))) | 1352 | (homepage (if desc (cdr (assoc :url (package-desc-extras desc))))) |
| 1210 | (built-in (eq pkg-dir 'builtin)) | 1353 | (built-in (eq pkg-dir 'builtin)) |
| 1211 | (installable (and archive (not built-in))) | 1354 | (installable (and archive (not built-in))) |
| 1212 | (status (if desc (package-desc-status desc) "orphan"))) | 1355 | (status (if desc (package-desc-status desc) "orphan")) |
| 1356 | (signed (if desc (package-desc-signed desc)))) | ||
| 1213 | (prin1 name) | 1357 | (prin1 name) |
| 1214 | (princ " is ") | 1358 | (princ " is ") |
| 1215 | (princ (if (memq (aref status 0) '(?a ?e ?i ?o ?u)) "an " "a ")) | 1359 | (princ (if (memq (aref status 0) '(?a ?e ?i ?o ?u)) "an " "a ")) |
| @@ -1222,7 +1366,9 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages." | |||
| 1222 | 'font-lock-face 'font-lock-builtin-face) | 1366 | 'font-lock-face 'font-lock-builtin-face) |
| 1223 | ".")) | 1367 | ".")) |
| 1224 | (pkg-dir | 1368 | (pkg-dir |
| 1225 | (insert (propertize (capitalize status) ;FIXME: Why comment-face? | 1369 | (insert (propertize (if (equal status "unsigned") |
| 1370 | "Installed" | ||
| 1371 | (capitalize status)) ;FIXME: Why comment-face? | ||
| 1226 | 'font-lock-face 'font-lock-comment-face)) | 1372 | 'font-lock-face 'font-lock-comment-face)) |
| 1227 | (insert " in `") | 1373 | (insert " in `") |
| 1228 | ;; Todo: Add button for uninstalling. | 1374 | ;; Todo: Add button for uninstalling. |
| @@ -1233,9 +1379,11 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages." | |||
| 1233 | (not (package-built-in-p name version))) | 1379 | (not (package-built-in-p name version))) |
| 1234 | (insert "',\n shadowing a " | 1380 | (insert "',\n shadowing a " |
| 1235 | (propertize "built-in package" | 1381 | (propertize "built-in package" |
| 1236 | 'font-lock-face 'font-lock-builtin-face) | 1382 | 'font-lock-face 'font-lock-builtin-face)) |
| 1237 | ".") | 1383 | (insert "'")) |
| 1238 | (insert "'."))) | 1384 | (if signed |
| 1385 | (insert ".") | ||
| 1386 | (insert " (unsigned)."))) | ||
| 1239 | (installable | 1387 | (installable |
| 1240 | (insert (capitalize status)) | 1388 | (insert (capitalize status)) |
| 1241 | (insert " from " (format "%s" archive)) | 1389 | (insert " from " (format "%s" archive)) |
| @@ -1449,7 +1597,8 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC." | |||
| 1449 | (dir (package-desc-dir pkg-desc)) | 1597 | (dir (package-desc-dir pkg-desc)) |
| 1450 | (lle (assq name package-load-list)) | 1598 | (lle (assq name package-load-list)) |
| 1451 | (held (cadr lle)) | 1599 | (held (cadr lle)) |
| 1452 | (version (package-desc-version pkg-desc))) | 1600 | (version (package-desc-version pkg-desc)) |
| 1601 | (signed (package-desc-signed pkg-desc))) | ||
| 1453 | (cond | 1602 | (cond |
| 1454 | ((eq dir 'builtin) "built-in") | 1603 | ((eq dir 'builtin) "built-in") |
| 1455 | ((and lle (null held)) "disabled") | 1604 | ((and lle (null held)) "disabled") |
| @@ -1463,7 +1612,9 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC." | |||
| 1463 | (dir ;One of the installed packages. | 1612 | (dir ;One of the installed packages. |
| 1464 | (cond | 1613 | (cond |
| 1465 | ((not (file-exists-p (package-desc-dir pkg-desc))) "deleted") | 1614 | ((not (file-exists-p (package-desc-dir pkg-desc))) "deleted") |
| 1466 | ((eq pkg-desc (cadr (assq name package-alist))) "installed") | 1615 | ((eq pkg-desc (cadr (assq name package-alist))) (if signed |
| 1616 | "installed" | ||
| 1617 | "unsigned")) | ||
| 1467 | (t "obsolete"))) | 1618 | (t "obsolete"))) |
| 1468 | (t | 1619 | (t |
| 1469 | (let* ((ins (cadr (assq name package-alist))) | 1620 | (let* ((ins (cadr (assq name package-alist))) |
| @@ -1473,7 +1624,9 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC." | |||
| 1473 | (if (memq name package-menu--new-package-list) | 1624 | (if (memq name package-menu--new-package-list) |
| 1474 | "new" "available")) | 1625 | "new" "available")) |
| 1475 | ((version-list-< version ins-v) "obsolete") | 1626 | ((version-list-< version ins-v) "obsolete") |
| 1476 | ((version-list-= version ins-v) "installed"))))))) | 1627 | ((version-list-= version ins-v) (if signed |
| 1628 | "installed" | ||
| 1629 | "unsigned")))))))) | ||
| 1477 | 1630 | ||
| 1478 | (defun package-menu--refresh (&optional packages) | 1631 | (defun package-menu--refresh (&optional packages) |
| 1479 | "Re-populate the `tabulated-list-entries'. | 1632 | "Re-populate the `tabulated-list-entries'. |
| @@ -1532,6 +1685,7 @@ Return (PKG-DESC [NAME VERSION STATUS DOC])." | |||
| 1532 | (`"held" 'font-lock-constant-face) | 1685 | (`"held" 'font-lock-constant-face) |
| 1533 | (`"disabled" 'font-lock-warning-face) | 1686 | (`"disabled" 'font-lock-warning-face) |
| 1534 | (`"installed" 'font-lock-comment-face) | 1687 | (`"installed" 'font-lock-comment-face) |
| 1688 | (`"unsigned" 'font-lock-warning-face) | ||
| 1535 | (_ 'font-lock-warning-face)))) ; obsolete. | 1689 | (_ 'font-lock-warning-face)))) ; obsolete. |
| 1536 | (list pkg-desc | 1690 | (list pkg-desc |
| 1537 | (vector (list (symbol-name (package-desc-name pkg-desc)) | 1691 | (vector (list (symbol-name (package-desc-name pkg-desc)) |
| @@ -1570,7 +1724,7 @@ If optional arg BUTTON is non-nil, describe its associated package." | |||
| 1570 | (defun package-menu-mark-delete (&optional _num) | 1724 | (defun package-menu-mark-delete (&optional _num) |
| 1571 | "Mark a package for deletion and move to the next line." | 1725 | "Mark a package for deletion and move to the next line." |
| 1572 | (interactive "p") | 1726 | (interactive "p") |
| 1573 | (if (member (package-menu-get-status) '("installed" "obsolete")) | 1727 | (if (member (package-menu-get-status) '("installed" "obsolete" "unsigned")) |
| 1574 | (tabulated-list-put-tag "D" t) | 1728 | (tabulated-list-put-tag "D" t) |
| 1575 | (forward-line))) | 1729 | (forward-line))) |
| 1576 | 1730 | ||
| @@ -1624,7 +1778,7 @@ If optional arg BUTTON is non-nil, describe its associated package." | |||
| 1624 | ;; ENTRY is (PKG-DESC [NAME VERSION STATUS DOC]) | 1778 | ;; ENTRY is (PKG-DESC [NAME VERSION STATUS DOC]) |
| 1625 | (let ((pkg-desc (car entry)) | 1779 | (let ((pkg-desc (car entry)) |
| 1626 | (status (aref (cadr entry) 2))) | 1780 | (status (aref (cadr entry) 2))) |
| 1627 | (cond ((equal status "installed") | 1781 | (cond ((member status '("installed" "unsigned")) |
| 1628 | (push pkg-desc installed)) | 1782 | (push pkg-desc installed)) |
| 1629 | ((member status '("available" "new")) | 1783 | ((member status '("available" "new")) |
| 1630 | (push (cons (package-desc-name pkg-desc) pkg-desc) | 1784 | (push (cons (package-desc-name pkg-desc) pkg-desc) |
| @@ -1738,6 +1892,8 @@ Optional argument NOQUERY non-nil means do not ask the user to confirm." | |||
| 1738 | ((string= sB "available") nil) | 1892 | ((string= sB "available") nil) |
| 1739 | ((string= sA "installed") t) | 1893 | ((string= sA "installed") t) |
| 1740 | ((string= sB "installed") nil) | 1894 | ((string= sB "installed") nil) |
| 1895 | ((string= sA "unsigned") t) | ||
| 1896 | ((string= sB "unsigned") nil) | ||
| 1741 | ((string= sA "held") t) | 1897 | ((string= sA "held") t) |
| 1742 | ((string= sB "held") nil) | 1898 | ((string= sB "held") nil) |
| 1743 | ((string= sA "built-in") t) | 1899 | ((string= sA "built-in") t) |