mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-01 19:18:28 -04:00
core/cli: expose QR code CLI
port 1.5
This commit is contained in:
@@ -759,6 +759,7 @@ func getCommonCommands() []*cobra.Command {
|
||||
greeterCmd,
|
||||
setupCmd,
|
||||
colorCmd,
|
||||
qrCmd,
|
||||
screenshotCmd,
|
||||
notifyActionCmd,
|
||||
notifyCmd,
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/clipboard"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/qrcode"
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
qrEcc string
|
||||
qrVersion int
|
||||
qrOutput string
|
||||
qrStdout bool
|
||||
qrClipboard bool
|
||||
qrCopyText bool
|
||||
qrRender bool
|
||||
qrNoRender bool
|
||||
qrInvert bool
|
||||
qrQuietZone int
|
||||
qrModuleSize int
|
||||
qrFg string
|
||||
qrBg string
|
||||
qrTransparent bool
|
||||
qrBorder int
|
||||
qrShape string
|
||||
qrLogo string
|
||||
qrLogoScale int
|
||||
|
||||
qrWifiPassword string
|
||||
qrWifiSecurity string
|
||||
qrWifiHidden bool
|
||||
)
|
||||
|
||||
var qrCmd = &cobra.Command{
|
||||
Use: "qr [text]",
|
||||
Short: "Generate QR codes",
|
||||
Long: `Generate a QR code from text, stdin, or a WiFi network.
|
||||
|
||||
By default the code is rendered to the terminal when stdout is a TTY, or
|
||||
written as PNG bytes to stdout when piped. Use flags to also copy to the
|
||||
clipboard, save a PNG, or tune encoding and colors.
|
||||
|
||||
Input:
|
||||
dms qr "https://example.com" # encode an argument
|
||||
echo -n "data" | dms qr # encode stdin
|
||||
dms qr - # encode stdin explicitly
|
||||
|
||||
Output (combine freely):
|
||||
dms qr "text" --clipboard # copy PNG image to clipboard
|
||||
dms qr "text" --copy-text # copy the source text to clipboard
|
||||
dms qr "text" -o code.png # save a PNG file
|
||||
dms qr "text" > code.png # PNG to stdout (piped)
|
||||
dms qr "text" --render # force terminal render
|
||||
|
||||
Encoding & style:
|
||||
--ecc L|M|Q|H # error correction (default M)
|
||||
--qr-version 10 # force symbol version (1-40)
|
||||
--module-size 12 --fg '#000' ... # PNG sizing and colors
|
||||
--shape circle # round modules
|
||||
--logo icon.png # center logo (bumps --ecc to H)
|
||||
--invert # flip colors for light terminals
|
||||
|
||||
WiFi:
|
||||
dms qr wifi MySSID -p secret # build from an explicit password
|
||||
dms qr wifi MySSID # pull the saved secret from the shell`,
|
||||
Args: cobra.ArbitraryArgs,
|
||||
Run: runQR,
|
||||
}
|
||||
|
||||
var qrWifiCmd = &cobra.Command{
|
||||
Use: "wifi <ssid>",
|
||||
Short: "Generate a WiFi QR code",
|
||||
Long: `Generate a QR code that joins a WiFi network when scanned.
|
||||
|
||||
With --password the code is built entirely offline. Without it, the saved
|
||||
credentials are fetched from the running DMS shell (like the network panel).`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: runQRWifi,
|
||||
}
|
||||
|
||||
func init() {
|
||||
qrCmd.PersistentFlags().StringVar(&qrEcc, "ecc", "", "Error correction level: L, M, Q, H (default M, or H with --logo)")
|
||||
qrCmd.PersistentFlags().IntVar(&qrVersion, "qr-version", 0, "Force QR symbol version 1-40 (0 = auto)")
|
||||
qrCmd.PersistentFlags().StringVarP(&qrOutput, "output", "o", "", "Write a PNG to this file")
|
||||
qrCmd.PersistentFlags().BoolVar(&qrStdout, "stdout", false, "Write PNG bytes to stdout")
|
||||
qrCmd.PersistentFlags().BoolVar(&qrClipboard, "clipboard", false, "Copy the PNG image to the clipboard")
|
||||
qrCmd.PersistentFlags().BoolVar(&qrCopyText, "copy-text", false, "Copy the source text to the clipboard")
|
||||
qrCmd.PersistentFlags().BoolVar(&qrRender, "render", false, "Force terminal rendering")
|
||||
qrCmd.PersistentFlags().BoolVar(&qrNoRender, "no-render", false, "Never render to the terminal")
|
||||
qrCmd.PersistentFlags().BoolVar(&qrInvert, "invert", false, "Swap colors (for light terminals)")
|
||||
qrCmd.PersistentFlags().IntVar(&qrQuietZone, "quiet-zone", 2, "Terminal margin in modules")
|
||||
qrCmd.PersistentFlags().IntVar(&qrModuleSize, "module-size", 0, "PNG pixels per module (0 = auto)")
|
||||
qrCmd.PersistentFlags().StringVar(&qrFg, "fg", "", "Dark module color (#RGB or #RRGGBB)")
|
||||
qrCmd.PersistentFlags().StringVar(&qrBg, "bg", "", "Light module color (#RGB or #RRGGBB)")
|
||||
qrCmd.PersistentFlags().BoolVar(&qrTransparent, "transparent", false, "Transparent PNG background")
|
||||
qrCmd.PersistentFlags().IntVar(&qrBorder, "border", -1, "PNG border in pixels (-1 = auto)")
|
||||
qrCmd.PersistentFlags().StringVar(&qrShape, "shape", "square", "PNG module shape (square, circle)")
|
||||
qrCmd.PersistentFlags().StringVar(&qrLogo, "logo", "", "Center a PNG/JPEG logo on the PNG output")
|
||||
qrCmd.PersistentFlags().IntVar(&qrLogoScale, "logo-scale", 0, "Max logo size as 1/N of the code (0 = library default of 5)")
|
||||
|
||||
qrWifiCmd.Flags().StringVarP(&qrWifiPassword, "password", "p", "", "WiFi password (offline build)")
|
||||
qrWifiCmd.Flags().StringVar(&qrWifiSecurity, "security", "WPA", "Security type (WPA, WEP, nopass)")
|
||||
qrWifiCmd.Flags().BoolVar(&qrWifiHidden, "hidden", false, "Mark the network as hidden")
|
||||
|
||||
qrCmd.AddCommand(qrWifiCmd)
|
||||
}
|
||||
|
||||
func runQR(cmd *cobra.Command, args []string) {
|
||||
text := strings.Join(args, " ")
|
||||
if text == "" || text == "-" {
|
||||
data, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
fatalf("Error reading stdin: %v", err)
|
||||
}
|
||||
text = strings.TrimRight(string(data), "\n")
|
||||
}
|
||||
if text == "" {
|
||||
fatalf("Error: no input (provide text, pipe stdin, or use a subcommand)")
|
||||
}
|
||||
emitQR(text)
|
||||
}
|
||||
|
||||
func runQRWifi(cmd *cobra.Command, args []string) {
|
||||
ssid := args[0]
|
||||
if qrWifiPassword != "" || strings.EqualFold(qrWifiSecurity, "nopass") {
|
||||
emitQR(qrcode.WiFiString(qrWifiSecurity, ssid, qrWifiPassword, qrWifiHidden))
|
||||
return
|
||||
}
|
||||
|
||||
content, err := fetchWifiQRContent(ssid)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
fmt.Fprintln(os.Stderr, "Hint: pass --password to build the code without the shell.")
|
||||
os.Exit(1)
|
||||
}
|
||||
if qrWifiHidden {
|
||||
content = strings.TrimSuffix(content, ";") + "H:true;;"
|
||||
}
|
||||
emitQR(content)
|
||||
}
|
||||
|
||||
func emitQR(text string) {
|
||||
renderTerm := shouldRenderTerminal()
|
||||
pngToStdout := qrStdout || (!renderTerm && qrOutput == "" && !qrClipboard && !qrCopyText)
|
||||
|
||||
if pngToStdout || qrOutput != "" || qrClipboard {
|
||||
png, err := qrcode.RenderPNG(text, qrcode.ImageOptions{
|
||||
ECC: effectiveEcc(),
|
||||
Version: qrVersion,
|
||||
ModuleSize: qrModuleSize,
|
||||
Fg: qrFg,
|
||||
Bg: qrBg,
|
||||
Transparent: qrTransparent,
|
||||
Border: qrBorder,
|
||||
Shape: qrShape,
|
||||
Logo: qrLogo,
|
||||
LogoScale: qrLogoScale,
|
||||
})
|
||||
if err != nil {
|
||||
fatalf("Error encoding QR: %v", err)
|
||||
}
|
||||
emitPNG(png, pngToStdout)
|
||||
}
|
||||
|
||||
if qrCopyText {
|
||||
if err := clipboard.CopyText(text); err != nil {
|
||||
fatalf("Error copying text: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !renderTerm {
|
||||
return
|
||||
}
|
||||
out, err := qrcode.RenderTerminal(text, qrcode.TermOptions{
|
||||
ECC: effectiveEcc(),
|
||||
Version: qrVersion,
|
||||
QuietZone: qrQuietZone,
|
||||
Invert: qrInvert,
|
||||
Fg: qrFg,
|
||||
Bg: qrBg,
|
||||
})
|
||||
if err != nil {
|
||||
fatalf("Error rendering QR: %v", err)
|
||||
}
|
||||
dst := os.Stdout
|
||||
if pngToStdout {
|
||||
dst = os.Stderr
|
||||
}
|
||||
fmt.Fprint(dst, out)
|
||||
}
|
||||
|
||||
func emitPNG(png []byte, toStdout bool) {
|
||||
if qrOutput != "" {
|
||||
if err := os.WriteFile(qrOutput, png, 0o644); err != nil {
|
||||
fatalf("Error writing file: %v", err)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, qrOutput)
|
||||
}
|
||||
if toStdout {
|
||||
os.Stdout.Write(png)
|
||||
}
|
||||
if qrClipboard {
|
||||
if err := clipboard.Copy(png, "image/png"); err != nil {
|
||||
fatalf("Error copying image: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shouldRenderTerminal() bool {
|
||||
switch {
|
||||
case qrNoRender:
|
||||
return false
|
||||
case qrRender:
|
||||
return true
|
||||
case qrStdout, qrOutput != "", qrClipboard, qrCopyText:
|
||||
return false
|
||||
default:
|
||||
return isatty.IsTerminal(os.Stdout.Fd())
|
||||
}
|
||||
}
|
||||
|
||||
func effectiveEcc() string {
|
||||
switch {
|
||||
case qrEcc != "":
|
||||
return qrEcc
|
||||
case qrLogo != "":
|
||||
return "H"
|
||||
default:
|
||||
return "M"
|
||||
}
|
||||
}
|
||||
|
||||
func fatalf(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func fetchWifiQRContent(ssid string) (string, error) {
|
||||
resp, err := sendServerRequest(models.Request{
|
||||
ID: 1,
|
||||
Method: "network.qrcode-content",
|
||||
Params: map[string]any{"ssid": ssid},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.Error != "" {
|
||||
return "", fmt.Errorf("%s", resp.Error)
|
||||
}
|
||||
if resp.Result == nil {
|
||||
return "", fmt.Errorf("empty response")
|
||||
}
|
||||
content, ok := (*resp.Result).(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unexpected response format")
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
qr "github.com/yeqown/go-qrcode/v2"
|
||||
"github.com/yeqown/go-qrcode/writer/standard"
|
||||
)
|
||||
|
||||
type TermOptions struct {
|
||||
ECC string
|
||||
Version int
|
||||
QuietZone int
|
||||
Invert bool
|
||||
Fg string
|
||||
Bg string
|
||||
}
|
||||
|
||||
type ImageOptions struct {
|
||||
ECC string
|
||||
Version int
|
||||
ModuleSize int
|
||||
Fg string
|
||||
Bg string
|
||||
Transparent bool
|
||||
Border int
|
||||
Shape string
|
||||
Logo string
|
||||
LogoScale int
|
||||
}
|
||||
|
||||
var wifiEscaper = strings.NewReplacer(`\`, `\\`, `;`, `\;`, `,`, `\,`, `:`, `\:`, `"`, `\"`)
|
||||
|
||||
func WiFiString(security, ssid, password string, hidden bool) string {
|
||||
if security == "" {
|
||||
security = "WPA"
|
||||
}
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "WIFI:T:%s;S:%s;", security, wifiEscaper.Replace(ssid))
|
||||
if !strings.EqualFold(security, "nopass") {
|
||||
fmt.Fprintf(&b, "P:%s;", wifiEscaper.Replace(password))
|
||||
}
|
||||
if hidden {
|
||||
b.WriteString("H:true;")
|
||||
}
|
||||
b.WriteString(";")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Colors are painted explicitly on both halves of each ▀ cell so polarity
|
||||
// does not depend on the terminal theme.
|
||||
func RenderTerminal(text string, opt TermOptions) (string, error) {
|
||||
fg, err := parseColor(opt.Fg, color.RGBA{A: 255})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bg, err := parseColor(opt.Bg, color.RGBA{R: 255, G: 255, B: 255, A: 255})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if opt.Invert {
|
||||
fg, bg = bg, fg
|
||||
}
|
||||
|
||||
mat, err := encode(text, opt.ECC, opt.Version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
grid := bitmapWithQuietZone(mat, opt.QuietZone)
|
||||
|
||||
var b strings.Builder
|
||||
for y := 0; y < len(grid); y += 2 {
|
||||
for x := range grid[y] {
|
||||
top := moduleColor(grid[y][x], fg, bg)
|
||||
bottom := bg
|
||||
if y+1 < len(grid) {
|
||||
bottom = moduleColor(grid[y+1][x], fg, bg)
|
||||
}
|
||||
fmt.Fprintf(&b, "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm▀", top.R, top.G, top.B, bottom.R, bottom.G, bottom.B)
|
||||
}
|
||||
b.WriteString("\x1b[0m\n")
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
func RenderPNG(text string, opt ImageOptions) ([]byte, error) {
|
||||
encOpts, err := encodeOptions(opt.ECC, opt.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imgOpts, err := imageOptions(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q, err := qr.NewWith(text, encOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
w := standard.NewWithWriter(nopCloser{&buf}, imgOpts...)
|
||||
if err := q.Save(w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func encodeOptions(ecc string, version int) ([]qr.EncodeOption, error) {
|
||||
eccOpt, err := eccOption(ecc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := []qr.EncodeOption{eccOpt}
|
||||
switch {
|
||||
case version == 0:
|
||||
case version >= 1 && version <= 40:
|
||||
opts = append(opts, qr.WithVersion(version))
|
||||
default:
|
||||
return nil, fmt.Errorf("QR version must be 1-40, got %d", version)
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func eccOption(level string) (qr.EncodeOption, error) {
|
||||
switch strings.ToUpper(level) {
|
||||
case "", "M":
|
||||
return qr.WithErrorCorrectionLevel(qr.ErrorCorrectionMedium), nil
|
||||
case "L":
|
||||
return qr.WithErrorCorrectionLevel(qr.ErrorCorrectionLow), nil
|
||||
case "Q":
|
||||
return qr.WithErrorCorrectionLevel(qr.ErrorCorrectionQuart), nil
|
||||
case "H":
|
||||
return qr.WithErrorCorrectionLevel(qr.ErrorCorrectionHighest), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid error correction level %q (want L, M, Q, or H)", level)
|
||||
}
|
||||
}
|
||||
|
||||
func imageOptions(opt ImageOptions) ([]standard.ImageOption, error) {
|
||||
if opt.ModuleSize < 0 || opt.ModuleSize > 255 {
|
||||
return nil, fmt.Errorf("module size must be 0-255, got %d", opt.ModuleSize)
|
||||
}
|
||||
|
||||
opts := []standard.ImageOption{standard.WithBuiltinImageEncoder(standard.PNG_FORMAT)}
|
||||
if opt.ModuleSize > 0 {
|
||||
opts = append(opts, standard.WithQRWidth(uint8(opt.ModuleSize)))
|
||||
}
|
||||
if opt.Border >= 0 {
|
||||
opts = append(opts, standard.WithBorderWidth(opt.Border))
|
||||
}
|
||||
|
||||
switch {
|
||||
case opt.Transparent:
|
||||
opts = append(opts, standard.WithBgTransparent())
|
||||
case opt.Bg != "":
|
||||
c, err := parseColor(opt.Bg, color.RGBA{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, standard.WithBgColor(c))
|
||||
}
|
||||
if opt.Fg != "" {
|
||||
c, err := parseColor(opt.Fg, color.RGBA{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, standard.WithFgColor(c))
|
||||
}
|
||||
|
||||
switch strings.ToLower(opt.Shape) {
|
||||
case "", "square":
|
||||
case "circle":
|
||||
opts = append(opts, standard.WithCircleShape())
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid shape %q (want square or circle)", opt.Shape)
|
||||
}
|
||||
|
||||
if opt.Logo != "" {
|
||||
img, err := loadImage(opt.Logo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = append(opts, standard.WithLogoImage(img))
|
||||
if opt.LogoScale > 0 {
|
||||
opts = append(opts, standard.WithLogoSizeMultiplier(opt.LogoScale))
|
||||
}
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func encode(text, ecc string, version int) (qr.Matrix, error) {
|
||||
opts, err := encodeOptions(ecc, version)
|
||||
if err != nil {
|
||||
return qr.Matrix{}, err
|
||||
}
|
||||
q, err := qr.NewWith(text, opts...)
|
||||
if err != nil {
|
||||
return qr.Matrix{}, err
|
||||
}
|
||||
mw := &matrixWriter{}
|
||||
if err := q.Save(mw); err != nil {
|
||||
return qr.Matrix{}, err
|
||||
}
|
||||
return mw.mat, nil
|
||||
}
|
||||
|
||||
func bitmapWithQuietZone(mat qr.Matrix, quiet int) [][]bool {
|
||||
if quiet < 0 {
|
||||
quiet = 0
|
||||
}
|
||||
src := mat.Bitmap()
|
||||
h := len(src)
|
||||
w := 0
|
||||
if h > 0 {
|
||||
w = len(src[0])
|
||||
}
|
||||
out := make([][]bool, h+quiet*2)
|
||||
for y := range out {
|
||||
out[y] = make([]bool, w+quiet*2)
|
||||
}
|
||||
for y := range h {
|
||||
for x := range w {
|
||||
out[y+quiet][x+quiet] = src[y][x]
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func moduleColor(dark bool, fg, bg color.RGBA) color.RGBA {
|
||||
if dark {
|
||||
return fg
|
||||
}
|
||||
return bg
|
||||
}
|
||||
|
||||
func parseColor(hex string, def color.RGBA) (color.RGBA, error) {
|
||||
hex = strings.TrimPrefix(strings.TrimSpace(hex), "#")
|
||||
if hex == "" {
|
||||
return def, nil
|
||||
}
|
||||
if len(hex) == 3 {
|
||||
hex = fmt.Sprintf("%c%c%c%c%c%c", hex[0], hex[0], hex[1], hex[1], hex[2], hex[2])
|
||||
}
|
||||
var r, g, b int
|
||||
if len(hex) != 6 {
|
||||
return def, fmt.Errorf("invalid color %q (want #RGB or #RRGGBB)", hex)
|
||||
}
|
||||
if _, err := fmt.Sscanf(hex, "%02x%02x%02x", &r, &g, &b); err != nil {
|
||||
return def, fmt.Errorf("invalid color %q (want #RGB or #RRGGBB)", hex)
|
||||
}
|
||||
return color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255}, nil
|
||||
}
|
||||
|
||||
func loadImage(path string) (image.Image, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode %s: %w", path, err)
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
type matrixWriter struct{ mat qr.Matrix }
|
||||
|
||||
func (w *matrixWriter) Write(m qr.Matrix) error { w.mat = m; return nil }
|
||||
func (w *matrixWriter) Close() error { return nil }
|
||||
|
||||
type nopCloser struct{ *bytes.Buffer }
|
||||
|
||||
func (nopCloser) Close() error { return nil }
|
||||
@@ -43,6 +43,8 @@ func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
|
||||
handleGetNetworkInfo(conn, req, manager)
|
||||
case "network.qrcode":
|
||||
handleGetNetworkQRCode(conn, req, manager)
|
||||
case "network.qrcode-content":
|
||||
handleGetNetworkQRCodeContent(conn, req, manager)
|
||||
case "network.delete-qrcode":
|
||||
handleDeleteQRCode(conn, req, manager)
|
||||
case "network.ethernet.info":
|
||||
@@ -341,6 +343,22 @@ func handleGetNetworkQRCode(conn net.Conn, req models.Request, manager *Manager)
|
||||
models.Respond(conn, req.ID, content)
|
||||
}
|
||||
|
||||
func handleGetNetworkQRCodeContent(conn net.Conn, req models.Request, manager *Manager) {
|
||||
ssid, err := params.String(req.Params, "ssid")
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
content, err := manager.GetWiFiQRContent(ssid)
|
||||
if err != nil {
|
||||
models.RespondError(conn, req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
models.Respond(conn, req.ID, content)
|
||||
}
|
||||
|
||||
func handleDeleteQRCode(conn net.Conn, req models.Request, _ *Manager) {
|
||||
path, err := params.String(req.Params, "path")
|
||||
if err != nil {
|
||||
|
||||
@@ -473,6 +473,10 @@ func (m *Manager) GetNetworkInfoDetailed(ssid string) (*NetworkInfoResponse, err
|
||||
return m.backend.GetWiFiNetworkDetails(ssid)
|
||||
}
|
||||
|
||||
func (m *Manager) GetWiFiQRContent(ssid string) (string, error) {
|
||||
return m.backend.GetWiFiQRCodeContent(ssid)
|
||||
}
|
||||
|
||||
func (m *Manager) GetNetworkQRCode(ssid string) ([2]string, error) {
|
||||
content, err := m.backend.GetWiFiQRCodeContent(ssid)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,12 +5,14 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/qrcode"
|
||||
)
|
||||
|
||||
const qrCodeTmpPrefix = "/tmp/dank-wifi-qrcode-"
|
||||
|
||||
func FormatWiFiQRString(securityType, ssid, password string) string {
|
||||
return fmt.Sprintf("WIFI:T:%s;S:%s;P:%s;;", securityType, ssid, password)
|
||||
return qrcode.WiFiString(securityType, ssid, password, false)
|
||||
}
|
||||
|
||||
func qrCodePaths(ssid string) (themed, normal string) {
|
||||
|
||||
Reference in New Issue
Block a user