download.scm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2012, 2013, 2015, 2016, 2017, 2020 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2021 Simon Tournier <zimon.toutoune@gmail.com>
  4. ;;;
  5. ;;; This file is part of GNU Guix.
  6. ;;;
  7. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  8. ;;; under the terms of the GNU General Public License as published by
  9. ;;; the Free Software Foundation; either version 3 of the License, or (at
  10. ;;; your option) any later version.
  11. ;;;
  12. ;;; GNU Guix is distributed in the hope that it will be useful, but
  13. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;;; GNU General Public License for more details.
  16. ;;;
  17. ;;; You should have received a copy of the GNU General Public License
  18. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  19. (define-module (guix scripts download)
  20. #:use-module (guix ui)
  21. #:use-module (guix scripts)
  22. #:use-module (guix store)
  23. #:use-module (gcrypt hash)
  24. #:use-module (guix base16)
  25. #:use-module (guix base32)
  26. #:autoload (guix base64) (base64-encode)
  27. #:use-module ((guix download) #:hide (url-fetch))
  28. #:use-module ((guix build download)
  29. #:select (url-fetch))
  30. #:use-module ((guix progress)
  31. #:select (current-terminal-columns))
  32. #:use-module ((guix build syscalls)
  33. #:select (terminal-columns))
  34. #:use-module (web uri)
  35. #:use-module (ice-9 match)
  36. #:use-module (srfi srfi-1)
  37. #:use-module (srfi srfi-14)
  38. #:use-module (srfi srfi-26)
  39. #:use-module (srfi srfi-37)
  40. #:use-module (rnrs bytevectors)
  41. #:use-module (ice-9 binary-ports)
  42. #:export (guix-download))
  43. ;;;
  44. ;;; Command-line options.
  45. ;;;
  46. (define (download-to-file url file)
  47. "Download the file at URI to FILE. Return FILE."
  48. (let ((uri (string->uri url)))
  49. (match (uri-scheme uri)
  50. ((or 'file #f)
  51. (copy-file (uri-path uri) file))
  52. (_
  53. (url-fetch url file #:mirrors %mirrors)))
  54. file))
  55. (define (ensure-valid-store-file-name name)
  56. "Replace any character not allowed in a stror name by an underscore."
  57. (define valid
  58. ;; according to nix/libstore/store-api.cc
  59. (string->char-set (string-append "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  60. "abcdefghijklmnopqrstuvwxyz"
  61. "0123456789" "+-._?=")))
  62. (string-map (lambda (c)
  63. (if (char-set-contains? valid c) c #\_))
  64. name))
  65. (define* (download-to-store* url #:key (verify-certificate? #t))
  66. (with-store store
  67. (download-to-store store url
  68. (ensure-valid-store-file-name (basename url))
  69. #:verify-certificate? verify-certificate?)))
  70. (define %default-options
  71. ;; Alist of default option values.
  72. `((format . ,bytevector->nix-base32-string)
  73. (hash-algorithm . ,(hash-algorithm sha256))
  74. (verify-certificate? . #t)
  75. (download-proc . ,download-to-store*)))
  76. (define (show-help)
  77. (display (G_ "Usage: guix download [OPTION] URL
  78. Download the file at URL to the store or to the given file, and print its
  79. file name and the hash of its contents.\n"))
  80. (newline)
  81. (display (G_ "\
  82. Supported formats: 'base64', 'nix-base32' (default), 'base32',
  83. and 'base16' ('hex' and 'hexadecimal' can be used as well).\n"))
  84. (format #t (G_ "
  85. -f, --format=FMT write the hash in the given format"))
  86. (format #t (G_ "
  87. -H, --hash=ALGORITHM use the given hash ALGORITHM"))
  88. (format #t (G_ "
  89. --no-check-certificate
  90. do not validate the certificate of HTTPS servers "))
  91. (format #t (G_ "
  92. -o, --output=FILE download to FILE"))
  93. (newline)
  94. (display (G_ "
  95. -h, --help display this help and exit"))
  96. (display (G_ "
  97. -V, --version display version information and exit"))
  98. (newline)
  99. (show-bug-report-information))
  100. (define %options
  101. ;; Specifications of the command-line options.
  102. (list (option '(#\f "format") #t #f
  103. (lambda (opt name arg result)
  104. (define fmt-proc
  105. (match arg
  106. ("base64"
  107. base64-encode)
  108. ("nix-base32"
  109. bytevector->nix-base32-string)
  110. ("base32"
  111. bytevector->base32-string)
  112. ((or "base16" "hex" "hexadecimal")
  113. bytevector->base16-string)
  114. (x
  115. (leave (G_ "unsupported hash format: ~a~%") arg))))
  116. (alist-cons 'format fmt-proc
  117. (alist-delete 'format result))))
  118. (option '(#\H "hash") #t #f
  119. (lambda (opt name arg result)
  120. (match (lookup-hash-algorithm (string->symbol arg))
  121. (#f
  122. (leave (G_ "~a: unknown hash algorithm~%") arg))
  123. (algo
  124. (alist-cons 'hash-algorithm algo result)))))
  125. (option '("no-check-certificate") #f #f
  126. (lambda (opt name arg result)
  127. (alist-cons 'verify-certificate? #f result)))
  128. (option '(#\o "output") #t #f
  129. (lambda (opt name arg result)
  130. (alist-cons 'download-proc
  131. (lambda* (url #:key verify-certificate?)
  132. (download-to-file url arg))
  133. (alist-delete 'download result))))
  134. (option '(#\h "help") #f #f
  135. (lambda args
  136. (show-help)
  137. (exit 0)))
  138. (option '(#\V "version") #f #f
  139. (lambda args
  140. (show-version-and-exit "guix download")))))
  141. ;;;
  142. ;;; Entry point.
  143. ;;;
  144. (define-command (guix-download . args)
  145. (category packaging)
  146. (synopsis "download a file to the store and print its hash")
  147. (define (parse-options)
  148. ;; Return the alist of option values.
  149. (parse-command-line args %options (list %default-options)
  150. #:build-options? #f
  151. #:argument-handler
  152. (lambda (arg result)
  153. (when (assq 'argument result)
  154. (leave (G_ "~A: extraneous argument~%") arg))
  155. (alist-cons 'argument arg result))))
  156. (with-error-handling
  157. (let* ((opts (parse-options))
  158. (arg (or (assq-ref opts 'argument)
  159. (leave (G_ "no download URI was specified~%"))))
  160. (uri (or (string->uri arg)
  161. (false-if-exception
  162. (string->uri
  163. (string-append "file://" (canonicalize-path arg))))
  164. (leave (G_ "~a: failed to parse URI~%")
  165. arg)))
  166. (fetch (assq-ref opts 'download-proc))
  167. (path (parameterize ((current-terminal-columns
  168. (terminal-columns)))
  169. (fetch (uri->string uri)
  170. #:verify-certificate?
  171. (assq-ref opts 'verify-certificate?))))
  172. (hash (call-with-input-file
  173. (or path
  174. (leave (G_ "~a: download failed~%")
  175. arg))
  176. (cute port-hash (assoc-ref opts 'hash-algorithm) <>)))
  177. (fmt (assq-ref opts 'format)))
  178. (format #t "~a~%~a~%" path (fmt hash))
  179. #t)))