blob: fa013c904a010df83102cdfd361180f2b39fdd12 [file] [log] [blame]
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"image"
"image/png"
"time"
"gioui.org/app"
"gioui.org/font/gofont"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/unit"
"gioui.org/widget/material"
"github.com/skip2/go-qrcode"
)
var vpnApiAddr = flag.String("vpn-api-addr", "", "VPN API server address")
var p *processor
type processor struct {
vc VPNClient
app App
ui *UI
inviteQrCh chan image.Image
inviteQrScannedCh chan []byte
onConnectCh chan interface{}
onDisconnectCh chan interface{}
}
func newProcessor() *processor {
th := material.NewTheme(gofont.Collection())
return &processor{
vc: NewDirectVPNClient(*vpnApiAddr),
app: createApp(),
ui: NewUI(th),
inviteQrCh: make(chan image.Image, 1),
inviteQrScannedCh: make(chan []byte, 1),
onConnectCh: make(chan interface{}, 1),
onDisconnectCh: make(chan interface{}, 1),
}
}
func (p *processor) InviteQRCodeScanned(code []byte) {
p.inviteQrScannedCh <- code
}
func (p *processor) ConnectRequested(service interface{}) {
go func() {
time.Sleep(1 * time.Second)
p.onConnectCh <- service
}()
}
func (p *processor) DisconnectRequested(service interface{}) {
p.onDisconnectCh <- service
}
func (p *processor) run() error {
w := app.NewWindow(
app.Size(unit.Px(1500), unit.Px(1500)),
app.Title("PCloud"),
)
var ops op.Ops
for {
select {
case e := <-w.Events():
switch e := e.(type) {
case app.ViewEvent:
if err := p.app.OnView(e); err != nil {
return err
} else {
w.Invalidate()
}
case *system.CommandEvent:
if e.Type == system.CommandBack {
if p.ui.OnBack() {
e.Cancel = true
w.Invalidate()
}
}
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)
events := p.ui.Layout(gtx)
e.Frame(&ops)
if err := p.processUIEvents(events); err != nil {
return err
}
}
case img := <-p.inviteQrCh:
p.ui.InviteQRGenerated(img)
w.Invalidate()
case code := <-p.inviteQrScannedCh:
p.JoinNetworkAndConnect(code)
case s := <-p.onConnectCh:
if err := p.app.Connect(s); err != nil {
return err
}
}
}
return nil
}
func (p *processor) processUIEvents(events []UIEvent) error {
for _, e := range events {
switch e.(type) {
case EventGetInviteQRCode:
go func() {
if img, err := p.generateInviteQRCode(); err == nil {
p.inviteQrCh <- img
} else {
// TODO(giolekva): do not panic
panic(err)
}
}()
case EventScanBarcode:
return p.app.LaunchBarcodeScanner()
default:
return fmt.Errorf("Unhandled event: %#v", e)
}
}
return nil
}
type qrCodeData struct {
VPNApiAddr string `json:"vpn_api_addr"`
Message []byte `json:"message"`
Signature []byte `json:"signature"`
}
func (p *processor) generateInviteQRCode() (image.Image, error) {
message := []byte("Hello PCloud")
signature, err := p.vc.Sign(message)
if err != nil {
return nil, err
}
c := qrCodeData{
p.vc.Address(),
message,
signature,
}
var data bytes.Buffer
if err := json.NewEncoder(&data).Encode(c); err != nil {
return nil, err
}
qr, err := qrcode.Encode(data.String(), qrcode.Medium, 1024)
if err != nil {
return nil, err
}
img, err := png.Decode(bytes.NewReader(qr))
if err != nil {
return nil, err
}
return img, nil
}
func (p *processor) JoinNetworkAndConnect(code []byte) {
var invite qrCodeData
if err := json.NewDecoder(bytes.NewReader(code)).Decode(&invite); err != nil {
panic(err)
}
config, err := p.vc.Join(invite.VPNApiAddr, invite.Message, invite.Signature)
if err != nil {
panic(err)
}
if err := p.app.StartVPN(config); err != nil {
panic(err)
}
}
func main() {
flag.Parse()
p = newProcessor()
go func() {
if err := p.run(); err != nil {
panic(err)
}
}()
app.Main()
}
// fmt.Println(m["pki"])
// c := nc.NewC(logrus.StandardLogger())
// if err := c.LoadString(string(tmpl)); err != nil {
// return nil, err
// }
// fmt.Println(c.Settings["pki"])
// return c, nil