| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- package qr
- import (
- "crypto/rsa"
- "fmt"
- "strings"
- "time"
- "github.com/ayn2op/tview"
- "github.com/ayn2op/tview/tabs"
- "github.com/gdamore/tcell/v3"
- "github.com/gorilla/websocket"
- "github.com/skip2/go-qrcode"
- )
- type Model struct {
- *tview.TextView
- conn *websocket.Conn
- heartbeatInterval time.Duration
- privateKey *rsa.PrivateKey
- fingerprint string
- qrCode *qrcode.QRCode
- msg string
- }
- func NewModel() *Model {
- m := &Model{
- TextView: tview.NewTextView(),
- }
- m.
- SetScrollable(true).
- SetWrap(false).
- SetTextAlign(tview.AlignmentCenter)
- m.msg = "Press Ctrl+N to open QR login"
- return m
- }
- var _ tabs.Tab = (*Model)(nil)
- func (m *Model) Label() string {
- return "QR"
- }
- func (m *Model) HandleEvent(event tcell.Event) tview.Command {
- switch event := event.(type) {
- case *tview.InitEvent:
- m.msg = "Connecting to Remote Auth Gateway..."
- return m.connect()
- case *tview.KeyEvent:
- if event.Key() == tcell.KeyEsc {
- m.msg = "Canceled"
- return tview.Batch(m.close(), nil)
- }
- return m.TextView.HandleEvent(event)
- case *connCreateEvent:
- m.conn = event.conn
- m.msg = "Connected. Handshaking..."
- return m.listen()
- case *connCloseEvent:
- m.conn = nil
- return nil
- case *helloEvent:
- m.heartbeatInterval = time.Duration(event.heartbeatInterval) * time.Millisecond
- return tview.Batch(m.listen(), m.heartbeat(), m.generatePrivateKey())
- case *privateKeyEvent:
- m.privateKey = event.privateKey
- return tview.Batch(m.listen(), m.sendInit())
- case *nonceProofEvent:
- return tview.Batch(m.listen(), m.sendNonceProof(event.encryptedNonce))
- case *pendingRemoteInitEvent:
- m.fingerprint = event.fingerprint
- return tview.Batch(m.listen(), m.generateQRCode(event.fingerprint))
- case *qrCodeEvent:
- m.qrCode = event.qrCode
- m.msg = "Scan this with the Discord mobile app to log in instantly."
- return m.listen()
- case *pendingTicketEvent:
- return tview.Batch(m.listen(), m.decryptUserPayload(event.encryptedUserPayload))
- case *userEvent:
- name := event.username
- if event.discriminator != "0" {
- name += "#" + event.discriminator
- }
- m.msg = fmt.Sprintf("Check your phone! Logging in as %s", name)
- return m.listen()
- case *pendingLoginEvent:
- m.msg = "Authenticating..."
- return tview.Batch(m.close(), m.exchangeTicket(event.ticket))
- case *cancelEvent:
- m.msg = "Login canceled on mobile"
- return m.close()
- case *heartbeatTickEvent:
- if m.conn == nil {
- return nil
- }
- return tview.Batch(m.heartbeat(), m.sendHeartbeat())
- case *tcell.EventError:
- m.msg = event.Error()
- return tview.Batch(m.close(), tview.Command(func() tcell.Event { return event }))
- }
- return nil
- }
- func (m *Model) Draw(screen tcell.Screen) {
- var contents []string
- if m.qrCode != nil {
- bitmap := m.qrCode.Bitmap()
- var b strings.Builder
- for y := 0; y < len(bitmap); y += 2 {
- for x := range bitmap[y] {
- top := bitmap[y][x]
- bottom := false
- if y+1 < len(bitmap) {
- bottom = bitmap[y+1][x]
- }
- if top && bottom {
- b.WriteString("█")
- } else if top && !bottom {
- b.WriteString("▀")
- } else if !top && bottom {
- b.WriteString("▄")
- } else {
- b.WriteByte(' ')
- }
- }
- b.WriteByte('\n')
- }
- contents = append(contents, b.String())
- }
- if m.msg != "" {
- contents = append(contents, m.msg)
- }
- builder := tview.NewLineBuilder()
- builder.Write(strings.Join(contents, "\n"), tcell.StyleDefault)
- m.SetLines(m.centerLines(builder.Finish()))
- m.TextView.Draw(screen)
- }
- func (m *Model) centerLines(lines []tview.Line) []tview.Line {
- _, _, _, height := m.InnerRect()
- if height == 0 {
- height = 40
- }
- padding := (height - len(lines)) / 2
- if padding < 0 {
- padding = 0
- } else if padding < 1 && height > len(lines) {
- padding = 1
- }
- if padding == 0 {
- return lines
- }
- centered := make([]tview.Line, 0, padding+len(lines))
- centered = append(centered, make([]tview.Line, padding)...)
- centered = append(centered, lines...)
- return centered
- }
|