wifi.scm 8.8 KB

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