diff options
| author | kali kaneko (leap communications) <kali@leap.se> | 2020-06-12 20:00:13 +0200 | 
|---|---|---|
| committer | kali kaneko (leap communications) <kali@leap.se> | 2020-06-12 20:03:03 +0200 | 
| commit | 0ac0afaaf312a02af01d1c307ecf9b5915f40b0d (patch) | |
| tree | b2d0b9a776c47c5c2cac90595a9721730e59a519 /gui/backend.go | |
| parent | 1038fa83b820bbdaa9bcf37118cf23b0e48a86c5 (diff) | |
[refactor] reorganize backend in its own module
Signed-off-by: kali kaneko (leap communications) <kali@leap.se>
Diffstat (limited to 'gui/backend.go')
| -rw-r--r-- | gui/backend.go | 302 | 
1 files changed, 14 insertions, 288 deletions
| diff --git a/gui/backend.go b/gui/backend.go index cf7c0fb..9c65025 100644 --- a/gui/backend.go +++ b/gui/backend.go @@ -1,333 +1,59 @@  package main -/* a wrapper around bitmask that exposes status to a QtQml gui */ +/* a wrapper around bitmask that exposes status to a QtQml gui. +   Have a look at the pkg/backend module for further enlightment. */  import ( -	"bytes" -	"encoding/json" -	"fmt" -	"log" -	"net/http" -	"os" -	"reflect" -	"sync" -	//"time" +	"C"  	"unsafe" -	"0xacab.org/leap/bitmask-vpn/pkg/bitmask" -	"0xacab.org/leap/bitmask-vpn/pkg/pickle" +	"0xacab.org/leap/bitmask-vpn/pkg/backend"  ) -// typedef void (*cb)(); -// inline void _do_callback(cb f) { -// 	f(); -// } -import "C" - -/* callbacks into C-land */ - -var mut sync.Mutex -var stmut sync.Mutex -var cbs = make(map[string](*[0]byte)) -var initOnce sync.Once - -// Events are just a enumeration of all the posible events that C functions can -// be interested in subscribing to. You cannot subscribe to an event that is -// not listed here. -type Events struct { -	OnStatusChanged string -} - -const OnStatusChanged string = "OnStatusChanged" - -// subscribe registers a callback from C-land. -// This callback needs to be passed as a void* C function pointer. -func subscribe(event string, fp unsafe.Pointer) { -	mut.Lock() -	defer mut.Unlock() -	e := &Events{} -	v := reflect.Indirect(reflect.ValueOf(&e)) -	hf := v.Elem().FieldByName(event) -	if reflect.ValueOf(hf).IsZero() { -		fmt.Println("ERROR: not a valid event:", event) -	} else { -		cbs[event] = (*[0]byte)(fp) -	} -} - -// trigger fires a callback from C-land. -func trigger(event string) { -	mut.Lock() -	defer mut.Unlock() -	cb := cbs[event] -	if cb != nil { -		C._do_callback(cb) -	} else { -		fmt.Println("ERROR: this event does not have subscribers:", event) -	} -} - -/* connection status */ - -const ( -	offStr      = "off" -	startingStr = "starting" -	onStr       = "on" -	stoppingStr = "stopping" -	failedStr   = "failed" -) - -// status reflects the current VPN status. Go code is responsible for updating -// it; C-land just watches its changes and pulls its updates via the serialized -// context object. -type status int - -const ( -	off status = iota -	starting -	on -	stopping -	failed -	unknown -) - -func (s status) String() string { -	return [...]string{offStr, startingStr, onStr, stoppingStr, failedStr}[s] -} - -func (s status) MarshalJSON() ([]byte, error) { -	b := bytes.NewBufferString(`"`) -	b.WriteString(s.String()) -	b.WriteString(`"`) -	return b.Bytes(), nil -} - -func (s status) fromString(st string) status { -	switch st { -	case offStr: -		return off -	case startingStr: -		return starting -	case onStr: -		return on -	case stoppingStr: -		return stopping -	case failedStr: -		return failed -	default: -		return unknown -	} -} - -// The connectionCtx keeps the global state that is passed around to C-land. It -// also serves as the primary way of passing requests from the frontend to the -// Go-core, by letting the UI write some of these variables and processing -// them. -type connectionCtx struct { -	AppName  string `json:"appName"` -	Provider string `json:"provider"` -	Donate   bool   `json:"donate"` -	Status   status `json:"status"` -	bm       bitmask.Bitmask -} - -func (c connectionCtx) toJson() ([]byte, error) { -	stmut.Lock() -	defer stmut.Unlock() -	b, err := json.Marshal(c) -	if err != nil { -		log.Println(err) -		return nil, err -	} -	return b, nil -} - -func (c connectionCtx) updateStatus() { -	if stStr, err := c.bm.GetStatus(); err != nil { -		log.Printf("Error getting status: %v", err) -	} else { -		setStatusFromStr(stStr) -	} - -	statusCh := c.bm.GetStatusCh() -	for { -		select { -		case stStr := <-statusCh: -			setStatusFromStr(stStr) -		} -	} -} - -var ctx *connectionCtx - -func setStatus(st status) { -	stmut.Lock() -	defer stmut.Unlock() -	ctx.Status = st -	go trigger(OnStatusChanged) -} - -func toggleDonate() { -	stmut.Lock() -	defer stmut.Unlock() -	ctx.Donate = !ctx.Donate -	go trigger(OnStatusChanged) -} - -func setStatusFromStr(stStr string) { -	setStatus(unknown.fromString(stStr)) -} - -// initializeBitmask instantiates a bitmask connection -func initializeBitmask() { -	if ctx == nil { -		log.Println("error: cannot initialize bitmask, ctx is nil") -		os.Exit(1) -	} -	bitmask.InitializeLogger() - -	b, err := bitmask.InitializeBitmask() -	if err != nil { -		log.Println("error: cannot initialize bitmask") -	} -	ctx.bm = b -} - -func startVPN() { -	err := ctx.bm.StartVPN(ctx.Provider) -	if err != nil { -		log.Println(err) -		os.Exit(1) -	} -} - -func stopVPN() { -	err := ctx.bm.StopVPN() -	if err != nil { -		log.Println(err) -	} -} - -// initializeContext initializes an empty connStatus and assigns it to the -// global ctx holder. This is expected to be called only once, so the public -// api uses the sync.Once primitive to call this. -func initializeContext(provider, appName string) { -	var st status = off -	ctx = &connectionCtx{ -		AppName:  appName, -		Provider: provider, -		Donate:   false, -		Status:   st, -	} -	go trigger(OnStatusChanged) -	initializeBitmask() -} - -/* mock http server: easy way to mocking vpn behavior on ui interaction. This -* should also show a good way of writing functionality tests just for the Qml -* layer */ - -func mockUIOn(w http.ResponseWriter, r *http.Request) { -	log.Println("changing status: on") -	setStatus(on) -} - -func mockUIOff(w http.ResponseWriter, r *http.Request) { -	log.Println("changing status: off") -	setStatus(off) -} - -func mockUIFailed(w http.ResponseWriter, r *http.Request) { -	log.Println("changing status: failed") -	setStatus(failed) -} - -func mockUI() { -	http.HandleFunc("/on", mockUIOn) -	http.HandleFunc("/off", mockUIOff) -	http.HandleFunc("/failed", mockUIFailed) -	http.ListenAndServe(":8080", nil) -} - -/* - -  exported C api - -*/ -  //export SwitchOn  func SwitchOn() { -	go setStatus(starting) -	go startVPN() +	backend.SwitchOn()  }  //export SwitchOff  func SwitchOff() { -	go setStatus(stopping) -	go stopVPN() +	backend.SwitchOff()  }  //export Unblock  func Unblock() { -	fmt.Println("unblock... [not implemented]") +	backend.Unblock()  }  //export Quit  func Quit() { -	if ctx.Status != off { -		go setStatus(stopping) -		stopVPN() -	} +	backend.Quit() +  }  //export ToggleDonate  func ToggleDonate() { -	toggleDonate() +	backend.ToggleDonate()  }  //export SubscribeToEvent  func SubscribeToEvent(event string, f unsafe.Pointer) { -	subscribe(event, f) +	backend.SubscribeToEvent(event, f)  }  //export InitializeBitmaskContext  func InitializeBitmaskContext() { -	pi := bitmask.GetConfiguredProvider() - -	initOnce.Do(func() { -		initializeContext(pi.Provider, pi.AppName) -	}) -	go ctx.updateStatus() - -	/* DEBUG -	timer := time.NewTimer(time.Second * 3) -	go func() { -		<-timer.C -		fmt.Println("donate timer fired") -		toggleDonate() -	}() -	*/ +	backend.InitializeBitmaskContext()  }  //export RefreshContext  func RefreshContext() *C.char { -	c, _ := ctx.toJson() -	return C.CString(string(c)) +	return (*C.char)(backend.RefreshContext())  }  //export InstallHelpers  func InstallHelpers() { -	pickle.InstallHelpers() -} - -/* end of the exposed api */ - -/* we could enable this one optionally for the qt tests */ - -/* uncomment: export MockUIInteraction */ -func MockUIInteraction() { -	log.Println("mocking ui interaction on port 8080. \nTry 'curl localhost:8080/{on|off|failed}' to toggle status.") -	go mockUI() +	backend.InstallHelpers()  }  func main() {} | 
