243 lines
5.5 KiB
Go
243 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"crypto/tls"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type Identities struct {
|
|
ByName map[string]*tls.Config
|
|
ByDomain map[string]*tls.Config
|
|
ByPage map[string]*tls.Config
|
|
ByFolder map[string]*tls.Config
|
|
}
|
|
|
|
func findIdentity(state *BrowserState, prefix string) (string, error) {
|
|
found := 0
|
|
value := ""
|
|
for name := range state.Identities.ByName {
|
|
if strings.HasPrefix(name, prefix) {
|
|
found += 1
|
|
value = name
|
|
}
|
|
}
|
|
|
|
switch found {
|
|
case 0:
|
|
return "", errors.New("no matching identity found")
|
|
case 1:
|
|
return value, nil
|
|
default:
|
|
return "", fmt.Errorf("too ambiguous - that name matched %d identities", found)
|
|
}
|
|
}
|
|
|
|
func (ids Identities) Get(u *url.URL) *tls.Config {
|
|
if conf, ok := ids.ByPage[u.String()]; ok {
|
|
return conf
|
|
}
|
|
|
|
if conf, ok := ids.ByFolder[u.Hostname()+u.Path]; ok {
|
|
return conf
|
|
}
|
|
pathsegments := strings.Split(strings.TrimLeft(u.Path, "/"), "/")
|
|
for len(pathsegments) > 0 {
|
|
pathsegments = pathsegments[0 : len(pathsegments)-1]
|
|
if conf, ok := ids.ByFolder[u.Hostname()+"/"+strings.Join(pathsegments, "/")]; ok {
|
|
return conf
|
|
}
|
|
}
|
|
|
|
if conf, ok := ids.ByDomain[u.Hostname()]; ok {
|
|
return conf
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func IdentityCreate(state *BrowserState, name string) error {
|
|
ident, err := createIdentity(state, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state.Identities.ByName[name] = ident
|
|
if err := saveIdentities(state.Identities); err != nil {
|
|
return err
|
|
}
|
|
|
|
state.Modal = []byte(fmt.Sprintf("Created new identity %s\n", name))
|
|
return Print(state)
|
|
}
|
|
|
|
func IdentityList(state *BrowserState) error {
|
|
buf := &bytes.Buffer{}
|
|
for name, ident := range state.Identities.ByName {
|
|
if _, err := fmt.Fprintf(buf, "%s (%s):\n", name, showIdent(ident)); err != nil {
|
|
return err
|
|
}
|
|
|
|
for domain, id := range state.Identities.ByDomain {
|
|
if id == ident {
|
|
if _, err := fmt.Fprintf(buf, " domain %s\n", domain); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for folder, id := range state.Identities.ByFolder {
|
|
if id == ident {
|
|
if _, err := fmt.Fprintf(buf, " folder %s\n", folder); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for page, id := range state.Identities.ByPage {
|
|
if id == ident {
|
|
if _, err := fmt.Fprintf(buf, " page %s\n", page); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
state.Modal = buf.Bytes()
|
|
if len(state.Modal) == 0 {
|
|
state.Modal = []byte("(no identities)\n")
|
|
}
|
|
return Print(state)
|
|
}
|
|
|
|
func IdentityDelete(state *BrowserState, name string) error {
|
|
name, err := findIdentity(state, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ident := state.Identities.ByName[name]
|
|
delete(state.Identities.ByName, name)
|
|
|
|
for domain, id := range state.Identities.ByDomain {
|
|
if id == ident {
|
|
delete(state.Identities.ByDomain, domain)
|
|
}
|
|
}
|
|
for folder, id := range state.Identities.ByFolder {
|
|
if id == ident {
|
|
delete(state.Identities.ByFolder, folder)
|
|
}
|
|
}
|
|
for page, id := range state.Identities.ByPage {
|
|
if id == ident {
|
|
delete(state.Identities.ByPage, page)
|
|
}
|
|
}
|
|
|
|
if err := removeIdentity(name); err != nil {
|
|
return err
|
|
}
|
|
if err := saveIdentities(state.Identities); err != nil {
|
|
return err
|
|
}
|
|
|
|
state.Modal = []byte(fmt.Sprintf("Removed identity %s\n", name))
|
|
return Print(state)
|
|
}
|
|
|
|
func IdentityUseDomain(state *BrowserState, name string, domain string) error {
|
|
name, err := findIdentity(state, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ident := state.Identities.ByName[name]
|
|
|
|
u, _, err := parseURL(domain, state, "gemini")
|
|
if errors.Is(err, invalidLinkErr("")) {
|
|
u, err = url.Parse(domain)
|
|
if err != nil {
|
|
return ErrInvalidLink(domain)
|
|
}
|
|
if u.Hostname() == "" {
|
|
u.Host = domain
|
|
}
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
state.Identities.ByDomain[u.Hostname()] = ident
|
|
if err := saveIdentities(state.Identities); err != nil {
|
|
return err
|
|
}
|
|
|
|
state.Modal = []byte(fmt.Sprintf("Identity %s will be used across domain %s\n", name, u.Hostname()))
|
|
return Print(state)
|
|
}
|
|
|
|
func IdentityUseFolder(state *BrowserState, name string, domain string) error {
|
|
name, err := findIdentity(state, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ident := state.Identities.ByName[name]
|
|
|
|
u, _, err := parseURL(domain, state, "gemini")
|
|
if errors.Is(err, invalidLinkErr("")) {
|
|
u, err = url.Parse(domain)
|
|
if err != nil {
|
|
return ErrInvalidLink(domain)
|
|
}
|
|
if u.Hostname() == "" {
|
|
u.Host = domain
|
|
}
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
u.Path = strings.TrimRight(u.Path, "/")
|
|
state.Identities.ByFolder[u.Hostname()+u.Path] = ident
|
|
if err := saveIdentities(state.Identities); err != nil {
|
|
return err
|
|
}
|
|
|
|
state.Modal = []byte(fmt.Sprintf("Identity %s will be used within folder %s%s\n", name, u.Hostname(), u.Path))
|
|
return Print(state)
|
|
}
|
|
|
|
func IdentityUsePage(state *BrowserState, name string, domain string) error {
|
|
name, err := findIdentity(state, name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ident := state.Identities.ByName[name]
|
|
|
|
u, _, err := parseURL(domain, state, "gemini")
|
|
if errors.Is(err, invalidLinkErr("")) {
|
|
u, err = url.Parse(domain)
|
|
if err != nil {
|
|
return ErrInvalidLink(domain)
|
|
}
|
|
if u.Hostname() == "" {
|
|
u.Host = domain
|
|
}
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
state.Identities.ByPage[u.String()] = ident
|
|
if err := saveIdentities(state.Identities); err != nil {
|
|
return err
|
|
}
|
|
|
|
state.Modal = []byte(fmt.Sprintf("Identity %s will be used on page %s\n", name, u.String()))
|
|
return Print(state)
|
|
}
|
|
|
|
func showIdent(ident *tls.Config) string {
|
|
hash := sha256.Sum256(ident.Certificates[0].Certificate[0])
|
|
return strings.ToUpper(hex.EncodeToString(hash[:])[:10])
|
|
}
|