wifi.scm 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2018 Mathieu Othacehe <m.othacehe@gmail.com>
  3. ;;; Copyright © 2019 Meiyo Peng <meiyo@riseup.net>
  4. ;;; Copyright © 2019 Tobias Geerinckx-Rice <me@tobias.gr>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (gnu installer newt wifi)
  21. #:use-module (gnu installer connman)
  22. #:use-module (gnu installer steps)
  23. #:use-module (gnu installer newt utils)
  24. #:use-module (gnu installer newt page)
  25. #:use-module (guix i18n)
  26. #:use-module (guix records)
  27. #:use-module (ice-9 format)
  28. #:use-module (ice-9 popen)
  29. #:use-module (ice-9 receive)
  30. #:use-module (ice-9 regex)
  31. #:use-module (ice-9 rdelim)
  32. #:use-module (srfi srfi-1)
  33. #:use-module (srfi srfi-34)
  34. #:use-module (srfi srfi-35)
  35. #:use-module (newt)
  36. #:export (run-wifi-page))
  37. ;; This record associates a connman service to its key the listbox.
  38. (define-record-type* <service-item>
  39. service-item make-service-item
  40. service-item?
  41. (service service-item-service) ; connman <service>
  42. (key service-item-key)) ; newt listbox-key
  43. (define (strength->string strength)
  44. "Convert STRENGTH as an integer percentage into a text printable strength
  45. bar using unicode characters. Taken from NetworkManager's
  46. nmc_wifi_strength_bars."
  47. (let ((quarter #\x2582)
  48. (half #\x2584)
  49. (three-quarter #\x2586)
  50. (full #\x2588))
  51. (cond
  52. ((> strength 80)
  53. ;; ▂▄▆█
  54. (string quarter half three-quarter full))
  55. ((> strength 55)
  56. ;; ▂▄▆_
  57. (string quarter half three-quarter #\_))
  58. ((> strength 30)
  59. ;; ▂▄__
  60. (string quarter half #\_ #\_))
  61. ((> strength 5)
  62. ;; ▂___
  63. (string quarter #\_ #\_ #\_))
  64. (else
  65. ;; ____
  66. (string quarter #\_ #\_ #\_ #\_)))))
  67. (define (force-wifi-scan)
  68. "Force a wifi scan. Raise a condition if no wifi technology is available."
  69. (let* ((technologies (connman-technologies))
  70. (wifi-technology
  71. (find (lambda (technology)
  72. (string=? (technology-type technology) "wifi"))
  73. technologies)))
  74. (if wifi-technology
  75. (connman-scan-technology wifi-technology)
  76. (raise (condition
  77. (&message
  78. (message (G_ "Unable to find a wifi technology"))))))))
  79. (define (draw-scanning-page)
  80. "Draw a page to indicate a wifi scan in progress."
  81. (draw-info-page (G_ "Scanning wifi for available networks, please wait.")
  82. (G_ "Scan in progress")))
  83. (define (run-wifi-password-page)
  84. "Run a page prompting user for a password and return it."
  85. (run-input-page (G_ "Please enter the wifi password.")
  86. (G_ "Password required")
  87. #:input-visibility-checkbox? #t))
  88. (define (run-wrong-password-page service-name)
  89. "Run a page to inform user of a wrong password input."
  90. (run-error-page
  91. (format #f (G_ "The password you entered for ~a is incorrect.")
  92. service-name)
  93. (G_ "Wrong password")))
  94. (define (run-unknown-error-page service-name)
  95. "Run a page to inform user that a connection error happened."
  96. (run-error-page
  97. (format #f
  98. (G_ "An error occurred while trying to connect to ~a, please retry.")
  99. service-name)
  100. (G_ "Connection error")))
  101. (define (password-callback)
  102. (run-wifi-password-page))
  103. (define (connect-wifi-service listbox service-items)
  104. "Connect to the wifi service selected in LISTBOX. SERVICE-ITEMS is the list
  105. of <service-item> records present in LISTBOX."
  106. (let* ((listbox-key (current-listbox-entry listbox))
  107. (item (find (lambda (item)
  108. (eq? (service-item-key item) listbox-key))
  109. service-items))
  110. (service (service-item-service item))
  111. (service-name (service-name service))
  112. (form (draw-connecting-page service-name)))
  113. (dynamic-wind
  114. (const #t)
  115. (lambda ()
  116. (guard (c ((connman-password-error? c)
  117. (run-wrong-password-page service-name)
  118. #f)
  119. ((connman-already-connected-error? c)
  120. #t)
  121. ((connman-connection-error? c)
  122. (run-unknown-error-page service-name)
  123. #f))
  124. (connman-connect-with-auth service password-callback)))
  125. (lambda ()
  126. (destroy-form-and-pop form)))))
  127. (define (run-wifi-scan-page)
  128. "Force a wifi scan and draw a page during the operation."
  129. (let ((form (draw-scanning-page)))
  130. (force-wifi-scan)
  131. (destroy-form-and-pop form)))
  132. (define (wifi-services)
  133. "Return all the connman services of wifi type."
  134. (let ((services (connman-services)))
  135. (filter (lambda (service)
  136. (and (string=? (service-type service) "wifi")
  137. (service-name service)
  138. (not (string-null? (service-name service)))))
  139. services)))
  140. (define* (fill-wifi-services listbox wifi-services)
  141. "Append all the services in WIFI-SERVICES to the given LISTBOX."
  142. (clear-listbox listbox)
  143. (map (lambda (service)
  144. (let* ((text (service->text service))
  145. (key (append-entry-to-listbox listbox text)))
  146. (service-item
  147. (service service)
  148. (key key))))
  149. wifi-services))
  150. ;; Maximum length of a wifi service name.
  151. (define service-name-max-length (make-parameter 20))
  152. ;; Height of the listbox displaying wifi services.
  153. (define wifi-listbox-height (make-parameter 20))
  154. ;; Information textbox width.
  155. (define info-textbox-width (make-parameter 40))
  156. (define (service->text service)
  157. "Return a string composed of the name and the strength of the given
  158. SERVICE. A '*' preceding the service name indicates that it is connected."
  159. (let* ((name (service-name service))
  160. (padded-name (string-pad-right name
  161. (service-name-max-length)))
  162. (strength (service-strength service))
  163. (strength-string (strength->string strength))
  164. (state (service-state service))
  165. (connected? (or (string=? state "online")
  166. (string=? state "ready"))))
  167. (format #f "~c ~a ~a~%"
  168. (if connected? #\* #\ )
  169. padded-name
  170. strength-string)))
  171. (define (run-wifi-page)
  172. "Run a page displaying available wifi networks in a listbox. Connect to the
  173. network when the corresponding listbox entry is selected. A button allow to
  174. force a wifi scan."
  175. (let* ((listbox (make-listbox
  176. -1 -1
  177. (wifi-listbox-height)
  178. (logior FLAG-SCROLL FLAG-BORDER FLAG-RETURNEXIT)))
  179. (form (make-form))
  180. (buttons-grid (make-grid 1 1))
  181. (middle-grid (make-grid 2 1))
  182. (info-text (G_ "Please select a wifi network."))
  183. (info-textbox
  184. (make-reflowed-textbox -1 -1 info-text
  185. (info-textbox-width)
  186. #:flags FLAG-BORDER))
  187. (exit-button (make-button -1 -1 (G_ "Exit")))
  188. (scan-button (make-button -1 -1 (G_ "Scan")))
  189. (services (wifi-services))
  190. (service-items '()))
  191. (if (null? services)
  192. (append-entry-to-listbox listbox (G_ "No wifi detected"))
  193. (set! service-items (fill-wifi-services listbox services)))
  194. (set-grid-field middle-grid 0 0 GRID-ELEMENT-COMPONENT listbox)
  195. (set-grid-field middle-grid 1 0 GRID-ELEMENT-COMPONENT scan-button
  196. #:anchor ANCHOR-TOP
  197. #:pad-left 2)
  198. (set-grid-field buttons-grid 0 0 GRID-ELEMENT-COMPONENT exit-button)
  199. (add-components-to-form form
  200. info-textbox
  201. listbox scan-button
  202. exit-button)
  203. (make-wrapped-grid-window
  204. (basic-window-grid info-textbox middle-grid buttons-grid)
  205. (G_ "Wifi"))
  206. (receive (exit-reason argument)
  207. (run-form form)
  208. (dynamic-wind
  209. (const #t)
  210. (lambda ()
  211. (when (eq? exit-reason 'exit-component)
  212. (cond
  213. ((components=? argument scan-button)
  214. (run-wifi-scan-page)
  215. (run-wifi-page))
  216. ((components=? argument exit-button)
  217. (raise
  218. (condition
  219. (&installer-step-abort))))
  220. ((components=? argument listbox)
  221. (let ((result (connect-wifi-service listbox service-items)))
  222. (unless result
  223. (run-wifi-page)))))))
  224. (lambda ()
  225. (destroy-form-and-pop form))))))