mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-15 07:09:47 +01:00
138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
|
// Package webbrowser provides a simple API for opening web pages on your
|
||
|
// default browser.
|
||
|
package webbrowser
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"os/exec"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
ErrCantOpenBrowser = errors.New("webbrowser: can't open browser")
|
||
|
ErrNoCandidates = errors.New("webbrowser: no browser candidate found for your OS")
|
||
|
)
|
||
|
|
||
|
// Candidates contains a list of registered `Browser`s that will be tried with Open.
|
||
|
var Candidates []Browser
|
||
|
|
||
|
type Browser interface {
|
||
|
// Command returns a ready to be used Cmd that will open an URL.
|
||
|
Command(string) (*exec.Cmd, error)
|
||
|
// Open tries to open a URL in your default browser. NOTE: This may cause
|
||
|
// your program to hang until the browser process is closed in some OSes,
|
||
|
// see https://github.com/toqueteos/webbrowser/issues/4.
|
||
|
Open(string) error
|
||
|
}
|
||
|
|
||
|
// Open tries to open a URL in your default browser ensuring you have a display
|
||
|
// set up and not running this from SSH. NOTE: This may cause your program to
|
||
|
// hang until the browser process is closed in some OSes, see
|
||
|
// https://github.com/toqueteos/webbrowser/issues/4.
|
||
|
func Open(s string) (err error) {
|
||
|
if len(Candidates) == 0 {
|
||
|
return ErrNoCandidates
|
||
|
}
|
||
|
|
||
|
// Try to determine if there's a display available (only linux) and we
|
||
|
// aren't on a terminal (all but windows).
|
||
|
switch runtime.GOOS {
|
||
|
case "linux":
|
||
|
// No display, no need to open a browser. Lynx users **MAY** have
|
||
|
// something to say about this.
|
||
|
if os.Getenv("DISPLAY") == "" {
|
||
|
return fmt.Errorf("webbrowser: tried to open %q, no screen found", s)
|
||
|
}
|
||
|
fallthrough
|
||
|
case "darwin":
|
||
|
// Check SSH env vars.
|
||
|
if os.Getenv("SSH_CLIENT") != "" || os.Getenv("SSH_TTY") != "" {
|
||
|
return fmt.Errorf("webbrowser: tried to open %q, but you are running a shell session", s)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Try all candidates
|
||
|
for _, candidate := range Candidates {
|
||
|
err := candidate.Open(s)
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ErrCantOpenBrowser
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
// Register the default Browser for current OS, if it exists.
|
||
|
if os, ok := osCommand[runtime.GOOS]; ok {
|
||
|
Candidates = append(Candidates, browserCommand{os.cmd, os.args})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
osCommand = map[string]*browserCommand{
|
||
|
"android": &browserCommand{"xdg-open", nil},
|
||
|
"darwin": &browserCommand{"open", nil},
|
||
|
"freebsd": &browserCommand{"xdg-open", nil},
|
||
|
"linux": &browserCommand{"xdg-open", nil},
|
||
|
"netbsd": &browserCommand{"xdg-open", nil},
|
||
|
"openbsd": &browserCommand{"xdg-open", nil}, // It may be open instead
|
||
|
"windows": &browserCommand{"cmd", []string{"/c", "start"}},
|
||
|
}
|
||
|
winSchemes = [3]string{"https", "http", "file"}
|
||
|
)
|
||
|
|
||
|
type browserCommand struct {
|
||
|
cmd string
|
||
|
args []string
|
||
|
}
|
||
|
|
||
|
func (b browserCommand) Command(s string) (*exec.Cmd, error) {
|
||
|
u, err := url.Parse(s)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
validUrl := ensureValidURL(u)
|
||
|
|
||
|
b.args = append(b.args, validUrl)
|
||
|
|
||
|
return exec.Command(b.cmd, b.args...), nil
|
||
|
}
|
||
|
|
||
|
func (b browserCommand) Open(s string) error {
|
||
|
cmd, err := b.Command(s)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return cmd.Run()
|
||
|
}
|
||
|
|
||
|
func ensureScheme(u *url.URL) {
|
||
|
for _, s := range winSchemes {
|
||
|
if u.Scheme == s {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
u.Scheme = "http"
|
||
|
}
|
||
|
|
||
|
func ensureValidURL(u *url.URL) string {
|
||
|
// Enforce a scheme (windows requires scheme to be set to work properly).
|
||
|
ensureScheme(u)
|
||
|
s := u.String()
|
||
|
|
||
|
// Escape characters not allowed by cmd/bash
|
||
|
switch runtime.GOOS {
|
||
|
case "windows":
|
||
|
s = strings.Replace(s, "&", `^&`, -1)
|
||
|
}
|
||
|
|
||
|
return s
|
||
|
}
|