github.com/jlowellwofford/u-root@v1.0.0/cmds/wifi/server.go (about)

     1  // Copyright 2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"html/template"
    11  	"io"
    12  	"io/ioutil"
    13  	"log"
    14  	"net/http"
    15  
    16  	"github.com/gorilla/mux"
    17  	"github.com/u-root/u-root/pkg/sos"
    18  	"github.com/u-root/u-root/pkg/wifi"
    19  )
    20  
    21  const (
    22  	DefHtmlPage = `
    23  <head>
    24  <style>
    25  table {
    26      font-family: arial, sans-serif;
    27      border-collapse: collapse;
    28      width: 100%;
    29  }
    30  
    31  td, th {
    32      border: 1px solid #dddddd;
    33      text-align: left;
    34      padding: 8px;
    35  }
    36  
    37  input {
    38  	font-size: 120%;
    39  }
    40  </style>
    41  <script>
    42  function sendConnect(elem, index) {
    43  	replaceWithConnecting(elem);
    44  	disableOtherButtons(elem);
    45  	essid = document.getElementById("essid".concat(index)).innerHTML
    46  	pass = document.getElementById("pass".concat(index)) ? 
    47  		document.getElementById("pass".concat(index)).value : ""
    48  	id = document.getElementById("id".concat(index)) ? 
    49  		document.getElementById("id".concat(index)).value : ""
    50  	fetch("http://localhost:{{.Port}}/connect", {
    51  		method: 'Post',
    52  		headers: {
    53  			'Accept': 'application/json',
    54  			'Content-Type': 'application/json'
    55  		},
    56  		body: JSON.stringify({
    57  			Essid: essid,
    58  			Pass: pass,
    59  			Id: id
    60  		})
    61  	})
    62  	.then(r => r.json())
    63  	.then( s => {
    64  		if (s !== null) {
    65  			alert(s.Error);
    66  			window.location.reload();
    67  		}
    68  		else {
    69  			window.location.reload();
    70  		}
    71  	})
    72  	.catch(err => alert(err))
    73  }
    74  
    75  function replaceWithConnecting(elem) {
    76      connectingTxt = document.createTextNode("Connecting...");
    77      elem.style.display = "none";
    78      elem.parentNode.appendChild(connectingTxt);
    79  }
    80  
    81  function sendRefresh(elem) {
    82  	elem.setAttribute("disabled", "true");
    83  	elem.setAttribute("value","Refreshing");
    84  	disableOtherButtons(elem);
    85  	fetch("http://localhost:{{.Port}}/refresh", {
    86  		method: 'Post'
    87  	})
    88  	.then(r => r.json())
    89  	.then( s => {
    90  		if (s !== null) {
    91  			alert(s.Error);
    92  			window.location.reload();
    93  		}
    94  		else {
    95  			window.location.reload();
    96  		}
    97  	})
    98  	.catch(err => alert(err))
    99  }
   100  
   101  function disableOtherButtons(elem) {
   102      btns = document.getElementsByClassName("btn");
   103      for (let btn of btns) {
   104      	if (btn === elem) {
   105      		continue;
   106      	}
   107      	btn.setAttribute("disabled", "true");
   108      }	
   109  }
   110  </script>
   111  </head>
   112  <body>
   113  {{$NoEnc := 0}}
   114  {{$WpaPsk := 1}}
   115  {{$WpaEap := 2}}
   116  {{$connectedEssid := .ConnectedEssid}}
   117  {{$connectingEssid := .ConnectingEssid}}
   118  <h1>Please choose your Wifi</h1> 
   119  <table style="width:100%">
   120  	<tr>
   121      	<th>Essid</th>
   122      	<th>Identity</th>
   123      	<th>Password / Passphrase</th>
   124      	<th><input type="submit" class="btn" onclick=sendRefresh(this) value="Refresh"></th>
   125    	</tr>
   126  	{{range $idx, $opt := .WifiOpts}}
   127  		{{if eq $opt.AuthSuite $NoEnc}}
   128  			<tr>
   129      			<td id="essid{{$idx}}">{{$opt.Essid}}</td>
   130      			<td></td>
   131      			<td></td>
   132      			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
   133      				<td>Connected</td>
   134  				{{else if and (and (eq $connectingEssid $opt.Essid) (ne $connectingEssid "")) (ne $connectingEssid $connectedEssid) }}
   135      				<td>Connecting...</td>
   136      			{{else}}
   137      				<td><input type="submit" class="btn" onclick="sendConnect(this, {{$idx}})" value="Connect"></td>
   138      			{{end}}
   139    			</tr>
   140  		{{else if eq $opt.AuthSuite $WpaPsk}}
   141  			<tr>
   142      			<td id="essid{{$idx}}">{{$opt.Essid}}</td>
   143      			<td></td>
   144      			<td><input type="password" id="pass{{$idx}}"></td>
   145      			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
   146      				<td>Connected</td>
   147  				{{else if and (and (eq $connectingEssid $opt.Essid) (ne $connectingEssid "")) (ne $connectingEssid $connectedEssid) }}
   148      				<td>Connecting...</td>
   149      			{{else}}
   150      				<td><input type="submit" class="btn" onclick="sendConnect(this, {{$idx}})" value="Connect"></td>
   151      			{{end}}
   152         		</tr>
   153  		{{else if eq $opt.AuthSuite $WpaEap}}
   154  			<tr>
   155      			<td id="essid{{$idx}}">{{$opt.Essid}}</td>
   156      			<td><input type="text" id="id{{$idx}}"></td>
   157      			<td><input type="password" id="pass{{$idx}}"></td>
   158      			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
   159      				<td>Connected</td>
   160  				{{else if and (and (eq $connectingEssid $opt.Essid) (ne $connectingEssid "")) (ne $connectingEssid $connectedEssid) }}
   161      				<td>Connecting...</td>
   162      			{{else}}
   163      				<td><input type="submit" class="btn" onclick="sendConnect(this, {{$idx}})" value="Connect"></td>
   164      			{{end}}
   165    			</tr>
   166  		{{else}}
   167  			<tr>
   168      			<td id="essid{{$idx}}">{{$opt.Essid}}</td>
   169      			<td colspan="3">Not a supported protocol</td>
   170    			</tr>
   171  		{{end}}
   172  	{{else}}
   173  		<td colspan="4">No essids found</td>
   174  	{{end}}
   175  </table>
   176  
   177  {{if and (ne $connectingEssid "") (ne $connectingEssid $connectedEssid) }}
   178  	<script>disableOtherButtons(null)</script>
   179  {{end}}
   180  </body>
   181  `
   182  )
   183  
   184  var (
   185  	Port uint
   186  )
   187  
   188  type WifiServer struct {
   189  	service *WifiService
   190  }
   191  
   192  func userInputValidation(essid, pass, id string) ([]string, error) {
   193  	switch {
   194  	case essid != "" && pass != "" && id != "":
   195  		return []string{essid, pass, id}, nil
   196  	case essid != "" && pass != "" && id == "":
   197  		return []string{essid, pass}, nil
   198  	case essid != "" && pass == "" && id == "":
   199  		return []string{essid}, nil
   200  	default:
   201  		return nil, fmt.Errorf("Invalid user input")
   202  	}
   203  }
   204  
   205  func (ws WifiServer) refreshHandle(w http.ResponseWriter, r *http.Request) {
   206  	if err := ws.service.Refresh(); err != nil {
   207  		w.WriteHeader(http.StatusInternalServerError)
   208  		json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
   209  		return
   210  	}
   211  	json.NewEncoder(w).Encode(nil)
   212  }
   213  
   214  type ConnectJsonMsg struct {
   215  	Essid string
   216  	Pass  string
   217  	Id    string
   218  }
   219  
   220  func (ws WifiServer) connectHandle(w http.ResponseWriter, r *http.Request) {
   221  	var msg ConnectJsonMsg
   222  	decoder := json.NewDecoder(r.Body)
   223  	defer r.Body.Close()
   224  	if err := decoder.Decode(&msg); err != nil {
   225  		log.Printf("error: %v", err)
   226  		w.WriteHeader(http.StatusBadRequest)
   227  		json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
   228  		return
   229  	}
   230  	a, err := userInputValidation(msg.Essid, msg.Pass, msg.Id)
   231  	if err != nil {
   232  		log.Printf("error: %v", err)
   233  		w.WriteHeader(http.StatusBadRequest)
   234  		json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
   235  		return
   236  	}
   237  
   238  	if err := ws.service.Connect(a); err != nil {
   239  		log.Printf("error: %v", err)
   240  		w.WriteHeader(http.StatusInternalServerError)
   241  		json.NewEncoder(w).Encode(struct{ Error string }{err.Error()})
   242  		return
   243  	}
   244  	// Connect Successful
   245  	json.NewEncoder(w).Encode(nil)
   246  }
   247  
   248  func (ws WifiServer) displayStateHandle(w http.ResponseWriter, r *http.Request) {
   249  	s := ws.service.GetState()
   250  	displayWifi(w, s.NearbyWifis, s.CurEssid, s.ConnectingEssid)
   251  }
   252  
   253  func (ws WifiServer) buildRouter() *mux.Router {
   254  	r := mux.NewRouter()
   255  	r.HandleFunc("/", ws.displayStateHandle).Methods("GET")
   256  	r.HandleFunc("/refresh", ws.refreshHandle).Methods("POST")
   257  	r.HandleFunc("/connect", ws.connectHandle).Methods("POST")
   258  	r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir(sos.HTMLPath("css")))))
   259  	return r
   260  }
   261  
   262  func (ws WifiServer) Start() {
   263  	defer ws.service.Shutdown()
   264  	listener, port, err := sos.GetListener()
   265  	if err != nil {
   266  		log.Fatalf("error: %v", err)
   267  	}
   268  	Port = port
   269  	fmt.Println(sos.StartServiceServer(ws.buildRouter(), "wifi", listener, Port))
   270  }
   271  
   272  func displayWifi(wr io.Writer, wifiOpts []wifi.Option, connectedEssid, connectingEssid string) error {
   273  	wifiData := struct {
   274  		WifiOpts        []wifi.Option
   275  		ConnectedEssid  string
   276  		ConnectingEssid string
   277  		Port            uint
   278  	}{wifiOpts, connectedEssid, connectingEssid, Port}
   279  
   280  	var tmpl *template.Template
   281  	file, err := ioutil.ReadFile(sos.HTMLPath("wifi.html"))
   282  	if err == nil {
   283  		html := string(file)
   284  		tmpl = template.Must(template.New("name").Parse(html))
   285  	} else {
   286  		tmpl = template.Must(template.New("name").Parse(DefHtmlPage))
   287  	}
   288  	return tmpl.Execute(wr, wifiData)
   289  }
   290  
   291  func NewWifiServer(service *WifiService) *WifiServer {
   292  	return &WifiServer{
   293  		service: service,
   294  	}
   295  }