main
Raw Download raw file
  1package cli
  2
  3import (
  4	"flag"
  5	"fmt"
  6	"os"
  7	"strings"
  8	"time"
  9)
 10
 11// headerList is a flag.Value that collects multiple -H flags.
 12type headerList []string
 13
 14func (h *headerList) String() string {
 15	return strings.Join(*h, ", ")
 16}
 17
 18func (h *headerList) Set(value string) error {
 19	*h = append(*h, value)
 20	return nil
 21}
 22
 23// ParseFlags parses command-line flags and returns a Config.
 24func ParseFlags(args []string) (*Config, error) {
 25	cfg := DefaultConfig()
 26
 27	fs := flag.NewFlagSet("govnc", flag.ContinueOnError)
 28
 29	// Custom usage
 30	fs.Usage = func() {
 31		fmt.Fprint(os.Stderr, usage)
 32	}
 33
 34	// Connection flags
 35	var headers headerList
 36	fs.Var(&headers, "H", "Add custom header (repeatable)")
 37	fs.Var(&headers, "header", "Add custom header (repeatable)")
 38	fs.StringVar(&cfg.Connection.Cookie, "b", "", "Send cookies")
 39	fs.StringVar(&cfg.Connection.Cookie, "cookie", "", "Send cookies")
 40	fs.StringVar(&cfg.Connection.UserAgent, "A", "govnc/1.0", "User-Agent header")
 41	fs.StringVar(&cfg.Connection.UserAgent, "user-agent", "govnc/1.0", "User-Agent header")
 42	fs.StringVar(&cfg.Connection.Proxy, "x", "", "Use proxy (http, socks5)")
 43	fs.StringVar(&cfg.Connection.Proxy, "proxy", "", "Use proxy (http, socks5)")
 44
 45	var connectTimeout int
 46	fs.IntVar(&connectTimeout, "connect-timeout", 30, "Connection timeout in seconds")
 47	fs.BoolVar(&cfg.Connection.Insecure, "k", false, "Skip TLS verification")
 48	fs.BoolVar(&cfg.Connection.Insecure, "insecure", false, "Skip TLS verification")
 49
 50	// Authentication flags
 51	fs.StringVar(&cfg.VNC.User, "u", "", "VNC authentication (user:password)")
 52	fs.StringVar(&cfg.VNC.User, "user", "", "VNC authentication (user:password)")
 53	fs.BoolVar(&cfg.VNC.AuthNone, "auth-none", false, "Force no authentication")
 54
 55	// VNC flags
 56	fs.BoolVar(&cfg.VNC.Shared, "shared", true, "Request shared session")
 57	fs.BoolVar(&cfg.VNC.Exclusive, "exclusive", false, "Request exclusive session")
 58
 59	var encodings string
 60	fs.StringVar(&encodings, "encoding", "", "Comma-separated encodings")
 61
 62	// Input flags
 63	fs.StringVar(&cfg.Input.InputFile, "i", "", "Read input from file (- for stdin)")
 64	fs.StringVar(&cfg.Input.InputFile, "input", "", "Read input from file (- for stdin)")
 65	fs.StringVar(&cfg.Input.TypeText, "type", "", "Type string and exit")
 66	fs.IntVar(&cfg.Input.KeyDelay, "delay", 0, "Delay between keystrokes in ms")
 67
 68	// Clipboard flags
 69	fs.StringVar(&cfg.Clipboard.ClipInDir, "clip-in", "", "Watch directory for outgoing clipboard")
 70	fs.StringVar(&cfg.Clipboard.ClipOutDir, "clip-out", "", "Save incoming clipboard to directory")
 71	fs.StringVar(&cfg.Clipboard.ClipSend, "clip-send", "", "Send clipboard text and exit")
 72
 73	// Server flags
 74	fs.StringVar(&cfg.Server.HTTPAddr, "http", "", "Start HTTP/WS server on address")
 75	fs.StringVar(&cfg.Server.ProxyMode, "proxy-mode", "shared", "Client session mode (shared|isolated)")
 76	fs.IntVar(&cfg.Server.MaxClients, "max-clients", 10, "Max concurrent WebSocket clients")
 77	fs.StringVar(&cfg.Server.NoVNCPath, "novnc-path", "", "Path to noVNC static files")
 78	fs.StringVar(&cfg.Server.APIPrefix, "api-prefix", "/api", "API route prefix")
 79	fs.StringVar(&cfg.Server.CORSOrigin, "cors-origin", "*", "CORS allowed origin")
 80
 81	// Output flags
 82	fs.IntVar(&cfg.Output.Verbose, "v", 0, "Verbose output (use multiple times for more)")
 83	fs.BoolVar(&cfg.Output.Silent, "s", false, "Silent mode")
 84	fs.BoolVar(&cfg.Output.Silent, "silent", false, "Silent mode")
 85	fs.StringVar(&cfg.Output.OutputFile, "o", "", "Write output to file")
 86	fs.StringVar(&cfg.Output.OutputFile, "output", "", "Write output to file")
 87
 88	// Help flags
 89	var showHelp bool
 90	fs.BoolVar(&showHelp, "h", false, "Show help")
 91	fs.BoolVar(&showHelp, "help", false, "Show help")
 92
 93	var showVersion bool
 94	fs.BoolVar(&showVersion, "version", false, "Show version")
 95
 96	// Parse flags
 97	if err := fs.Parse(args); err != nil {
 98		return nil, err
 99	}
100
101	if showHelp {
102		fs.Usage()
103		os.Exit(0)
104	}
105
106	if showVersion {
107		fmt.Println("govnc version 1.0.0")
108		os.Exit(0)
109	}
110
111	// Process headers
112	for _, h := range headers {
113		parts := strings.SplitN(h, ":", 2)
114		if len(parts) == 2 {
115			cfg.Connection.Headers.Add(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]))
116		}
117	}
118
119	// Process timeout (connectTimeout is in seconds, convert to time.Duration)
120	if connectTimeout > 0 {
121		cfg.Connection.ConnectTimeout = time.Duration(connectTimeout) * time.Second
122	}
123
124	// Process user:password
125	if cfg.VNC.User != "" {
126		parts := strings.SplitN(cfg.VNC.User, ":", 2)
127		if len(parts) == 2 {
128			cfg.VNC.Password = parts[1]
129		}
130		cfg.VNC.User = parts[0]
131	}
132
133	// Process encodings
134	if encodings != "" {
135		cfg.VNC.Encodings = parseEncodings(encodings)
136	}
137
138	// Get URL from remaining args
139	remaining := fs.Args()
140	if len(remaining) > 0 {
141		cfg.URL = remaining[0]
142	}
143
144	// Check for URL in environment
145	if cfg.URL == "" {
146		cfg.URL = os.Getenv("VNC_URL")
147	}
148
149	// Check for cookies in environment
150	if cfg.Connection.Cookie == "" {
151		cfg.Connection.Cookie = os.Getenv("VNC_COOKIES")
152	}
153
154	// Check for password in environment
155	if cfg.VNC.Password == "" {
156		cfg.VNC.Password = os.Getenv("VNC_PASSWORD")
157	}
158
159	return cfg, nil
160}
161
162// parseEncodings parses a comma-separated list of encoding names.
163func parseEncodings(s string) []int32 {
164	names := strings.Split(s, ",")
165	var encodings []int32
166	for _, name := range names {
167		name = strings.TrimSpace(strings.ToLower(name))
168		switch name {
169		case "raw":
170			encodings = append(encodings, 0)
171		case "copyrect":
172			encodings = append(encodings, 1)
173		case "rre":
174			encodings = append(encodings, 2)
175		case "hextile":
176			encodings = append(encodings, 5)
177		case "tight":
178			encodings = append(encodings, 7)
179		case "zrle":
180			encodings = append(encodings, 16)
181		}
182	}
183	return encodings
184}
185
186const usage = `govnc - VNC over WebSocket client and proxy
187
188USAGE:
189    govnc [OPTIONS] <URL>
190
191CONNECTION OPTIONS:
192    -H, --header <Header: Value>    Add custom header (repeatable)
193    -b, --cookie <data|@file>       Send cookies
194    -A, --user-agent <string>       User-Agent header (default: govnc/1.0)
195    -x, --proxy <host:port>         Use proxy (http, socks5)
196    --connect-timeout <seconds>     Connection timeout (default: 30)
197    -k, --insecure                  Skip TLS verification
198
199AUTHENTICATION:
200    -u, --user <user:password>      VNC authentication
201    --auth-none                     Force no authentication
202
203VNC OPTIONS:
204    --shared                        Request shared session (default: true)
205    --exclusive                     Request exclusive session
206    --encoding <list>               Comma-separated encodings (tight,zrle,raw,...)
207
208INPUT OPTIONS:
209    -i, --input <file|->            Read input from file (- for stdin)
210    --type <string>                 Type string and exit
211    --delay <ms>                    Delay between keystrokes
212
213CLIPBOARD OPTIONS:
214    --clip-in <dir>                 Watch directory for outgoing clipboard
215    --clip-out <dir>                Save incoming clipboard to directory
216    --clip-send <text|@file>        Send clipboard text and exit
217
218SERVER MODE:
219    --http <addr:port>              Start HTTP/WS server
220    --proxy-mode <shared|isolated>  Client session mode (default: shared)
221    --max-clients <n>               Max concurrent WebSocket clients
222    --novnc-path <path>             Path to noVNC static files
223    --api-prefix <prefix>           API route prefix (default: /api)
224    --cors-origin <origin>          CORS allowed origin (default: *)
225
226OUTPUT OPTIONS:
227    -v                              Verbose output (use multiple times)
228    -s, --silent                    Silent mode
229    -o, --output <file>             Write output to file
230
231OTHER:
232    -h, --help                      Show help
233    --version                       Show version
234
235ENVIRONMENT VARIABLES:
236    VNC_URL         Default WebSocket URL
237    VNC_COOKIES     Default cookies
238    VNC_PASSWORD    VNC password
239
240EXAMPLES:
241    # Basic interactive connection
242    govnc wss://vnc.example.com/websockify
243
244    # With auth headers
245    govnc -b "session=abc" -H "X-Auth: token" wss://vnc.example.com/ws
246
247    # Type command and exit
248    govnc --type "ls -la\n" wss://vnc.example.com/ws
249
250    # Start proxy server with noVNC web UI
251    govnc --http :8080 wss://vnc.example.com/ws
252    # Then: http://localhost:8080/noVNC/vnc.html
253
254    # Use REST API
255    curl -X PUT localhost:8080/api/keys -d '{"text":"hello\n"}'
256    curl -X PUT localhost:8080/api/clipboard -d '{"text":"copied"}'
257    curl localhost:8080/api/session
258`