Add doc strings for all the thigns

This commit is contained in:
James Mills 2023-01-14 12:46:47 +10:00
parent ae2199bff4
commit 2e3907a3b3
No known key found for this signature in database
GPG Key ID: AC4C014F1440EBD6
8 changed files with 87 additions and 8 deletions

View File

@ -25,19 +25,27 @@ import (
)
const (
// DefaultEnvPath is the default PATH for pre and post hooks that are shelled out to
DefaultEnvPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ServiceUser = "salty"
)
var (
ErrNoMessages = errors.New("error: no messages found")
ErrNoSender = errors.New("error: no sender configured")
ErrNotConnected = errors.New("error: client not connected")
// ErrNoMessages is an error returned when there are no further messages found for an inbox from the broker
ErrNoMessages = errors.New("error: no messages found")
// ErrNoSender is an error returned when the client is not properly configured with a valid sender
ErrNoSender = errors.New("error: no sender configured")
// ErrNotConnected is an error returned when the client is not properly configured or connected to a broker
ErrNotConnected = errors.New("error: client not connected")
// ErrMissingIdentity is an error returned when the client is not properly configured with a valid identity
ErrMissingIdentity = errors.New("error: missing identity")
)
type addrCache map[string]*Addr
// Message contains the plaintext (decrypted) message and the sender's public key
type Message struct {
Text string
Key *keys.EdX25519PublicKey
@ -205,10 +213,19 @@ func (cli *Client) messageHandler(extraenvs, prehook, posthook string, msgs chan
}
}
func (cli *Client) Me() *Addr { return cli.me }
func (cli *Client) Key() *keys.EdX25519PublicKey { return cli.key.PublicKey() }
func (cli *Client) State() *State { return cli.state }
// Me returns our (self) address
func (cli *Client) Me() *Addr { return cli.me }
// Key returns out (self) public key
func (cli *Client) Key() *keys.EdX25519PublicKey { return cli.key.PublicKey() }
// State returns the current state of the client
func (cli *Client) State() *State { return cli.state }
// Env sets up a sensible (and hopefully secure) environment for running pre and post hooks
// Extra environment variables are parsed from extraenvs and some default variables injected
// into the new environment such as PATH, PWD and HOME as well as the current user's Salty address
// (SALTY_USER) and their public key (SALTY_IDENTITY).
func (cli *Client) Env(extraenvs string) []string {
Path := DefaultEnvPath
GoPath := os.Getenv("GOPATH")
@ -242,6 +259,8 @@ func (cli *Client) Env(extraenvs string) []string {
return env
}
// Outbox returns the URL of our (self) outbox for sending copies of our outgoing messages to
// which is later used by the client as a way to track messages sent.
func (cli *Client) Outbox() *url.URL {
// use url struct copy to avoid modifying cli.me.Endpoint().Path
// https://github.com/golang/go/issues/38351
@ -253,6 +272,7 @@ func (cli *Client) Outbox() *url.URL {
return &ep
}
// OutboxAddr returns the address of our (self) outbox
func (cli *Client) OutboxAddr(to *Addr) *Addr {
return &Addr{
User: to.User,
@ -264,6 +284,7 @@ func (cli *Client) OutboxAddr(to *Addr) *Addr {
}
}
// OutboxClient returns a modified client for our (self) outbox
func (cli *Client) OutboxClient(to *Addr) *Client {
if to == nil {
to = cli.me
@ -301,6 +322,8 @@ func (cli *Client) OutboxClient(to *Addr) *Client {
}
}
// String implements the fmt.Stringer interface and outputs who we (self) are,
// what our endpoint is we're connected to (broker), our outbox and our public key.
func (cli *Client) String() string {
b := &bytes.Buffer{}
fmt.Fprintln(b, "Me: ", cli.me)
@ -393,6 +416,7 @@ func (cli *Client) Send(user, msg string) error {
return cli.OutboxClient(addr).SendToAddr(cli.OutboxAddr(addr), msg)
}
// SendToAddr encrypts and sends the message to a specified address
func (cli *Client) SendToAddr(addr *Addr, msg string) error {
if cli.me == nil || cli.me.IsZero() {
return ErrNoSender

View File

@ -13,6 +13,7 @@ import (
)
const (
// DateTimeFormat is the default date and time format used for displaying messages
DateTimeFormat = "2006-01-02 15:04:05"
)
@ -21,6 +22,7 @@ func boundedInt(value, low, high uint8) uint8 {
return (((value - low) % diff) + low)
}
// GetUserColor is used by the TUI (terminal ui) client to compute an appropriate ANSI color for a given user
func GetUserColor(user string, lower, upper uint8) uint8 {
h := fnv.New32()
h.Sum([]byte(user))

View File

@ -144,6 +144,7 @@ type Identity struct {
addr *Addr
}
// Source returns the path of an identity or []byte it if was loaded from a byte string
func (i *Identity) Source() string {
if i != nil && i.path != "" {
return i.path
@ -151,14 +152,17 @@ func (i *Identity) Source() string {
return "[]byte"
}
// Contents returns the contents of the identity as a byte slice
func (i *Identity) Contents() []byte {
return i.contents
}
// Key returns the full private and public Ed25519 key object for this identity
func (i *Identity) Key() *keys.EdX25519Key {
return i.key
}
// Addr returns the Salty Address for this identity
func (i *Identity) Addr() *Addr {
return i.addr
}
@ -189,7 +193,7 @@ func WithIdentityBytes(contents []byte) IdentityOption {
return func(i *Identity) { i.contents = contents }
}
// Handle unix home with `~`
// FixUnixHome handles paths with a UNIX Home (i.e: ~)
func FixUnixHome(p string) string {
// Handle unix home with `~`
if strings.HasPrefix(p, "~/") {

View File

@ -20,6 +20,9 @@ var (
_ Lookuper = (*ProxyLookup)(nil)
)
// Lookuper defines an interface for looking up Salty Addresses, primarily used by the PWA
// and possibly other clients, as a way to either perform direct lookups or to have lookups
// proxied through a broker.
type Lookuper interface {
LookupAddr(user string) (*Addr, error)
}
@ -50,10 +53,12 @@ type Config struct {
Key string `json:"key"`
}
// Capabilities defines optional capabilities of a client
type Capabilities struct {
AcceptEncoding string
}
// String implements the fmt.Stringer interface and formats capabilities as HTTP headers
func (c Capabilities) String() string {
return fmt.Sprint("accept-encoding: ", c.AcceptEncoding)
}
@ -76,6 +81,7 @@ func (a *Addr) IsZero() bool {
return a.User == "" && a.Domain == ""
}
// MarshalJSON implements the json.Marshaller interface
func (a *Addr) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Addr string
@ -94,6 +100,7 @@ func (a *Addr) MarshalJSON() ([]byte, error) {
})
}
// UnmarshalJSON implements the json.Unmarsaller interface
func (a *Addr) UnmarshalJSON(data []byte) error {
res := struct {
Addr string
@ -175,6 +182,7 @@ func (a *Addr) HashURI() string {
return fmt.Sprintf("https://%s/.well-known/salty/%s.json", a.DiscoveredDomain(), a.Hash())
}
// Refresh forces a lookup and configuration fetch for a Salty Address
func (a *Addr) Refresh() error {
log.Debugf("Looking up SRV record for _salty._tcp.%s", a.Domain)
if target, err := resolver.LookupSRV("salty", "tcp", a.Domain); err == nil {
@ -211,6 +219,9 @@ func (a *Addr) Refresh() error {
return nil
}
// Avatar returns the cached avatar service for a Salty Address or performs a DNS lookup
// for the avatar service to use and cached it (if found) and returns that. If there is no
// avatar service found, then a default avatar is used for peers.
func (a *Addr) Avatar() string {
if a.checkedAvatar {
return a.avatar

View File

@ -13,6 +13,9 @@ import (
log "github.com/sirupsen/logrus"
)
// Resolver is an interface for performing DNS lookups and is primarily used by the PWA
// and possibly other clients where direct DNS queries are not always possible and
// instead uses DNS over HTTP to eprform the same functionality.
type Resolver interface {
LookupSRV(service, proto, domain string) (string, error)
}
@ -20,6 +23,7 @@ type Resolver interface {
var (
_ Resolver = (*StandardResolver)(nil)
// ErrSRVRecordNotFound is an error returned by a resolver when there is no SRV record found for the domain of a Salty Address
ErrSRVRecordNotFound = errors.New("error: No SRV records found")
resolver Resolver
@ -29,12 +33,16 @@ func init() {
SetResolver(&StandardResolver{})
}
// SetResolver sets the default resolver used by this package
func SetResolver(r Resolver) {
resolver = r
}
// StandardResolver is a standard resolver that performs direct DNS queries over
// the standard networking protocol using port tcp/53 or udp/53
type StandardResolver struct{}
// LookupSRV implements the Resolver interface
func (r *StandardResolver) LookupSRV(service, proto, domain string) (string, error) {
log.Debugf("Using StandardResolver, looking up SRV _%s._%s.%s", service, proto, domain)
_, records, err := net.LookupSRV(service, proto, domain)
@ -47,8 +55,11 @@ func (r *StandardResolver) LookupSRV(service, proto, domain string) (string, err
return strings.TrimSuffix(records[0].Target, "."), nil
}
// DNSOverHTTPResolver is a resolver that performs DNS queries using a DNS Over HTTP
// service where direct DNS queries are not possible (standard resolver).
type DNSOverHTTPResolver struct{}
// LookupSRV implements the Resolver interface
func (r *DNSOverHTTPResolver) LookupSRV(service, proto, domain string) (string, error) {
name := fmt.Sprintf("_%s._%s.%s", service, proto, domain)

View File

@ -18,6 +18,13 @@ var (
acceptEncodings = []string{"br", "gzip", ""}
)
// Sender defines an interface for sending messages to another Salty Address (user)
// and is primarily used by the PWA and possibly other clients to send outbound
// messages where it may not always be possible to send the message directly and
// instead proxy the message thorugh a broker. Note that even if proxying through
// a broker, the message is already encrypted at the point of proxying, so there
// needs not be any trust between the client and broker as the broker is treated
// as a "dumb" relay.
type Sender interface {
Send(key *keys.EdX25519Key, endpoint, msg string, cap Capabilities) error
}

View File

@ -14,6 +14,9 @@ import (
"go.yarn.social/lextwt"
)
// Service is an object that implements an async responder (bot) that responds to
// textual callbacks (commands) or events. This can be used to implement automated
// users, bots or services.
type Service struct {
mu sync.RWMutex
@ -26,9 +29,13 @@ type Service struct {
eventFns map[string]MessageEventHandlerFunc
}
// MessageTextHandlerFunc defines a function type to handle an inbound message to a service
type MessageTextHandlerFunc func(context.Context, *Service, *keys.EdX25519PublicKey, *lextwt.SaltyText) error
// MessageEventHandlerFunc defines a function type to handle an inbound event to a service
type MessageEventHandlerFunc func(context.Context, *Service, *keys.EdX25519PublicKey, *lextwt.SaltyEvent) error
// NewService constructs a new service with the provided address, identity and state
func NewService(me *Addr, id *Identity, state string) (*Service, error) {
svc := &Service{
me: me,
@ -44,6 +51,7 @@ func NewService(me *Addr, id *Identity, state string) (*Service, error) {
return svc, nil
}
// SetClient sets the client instance to used for this service
func (svc *Service) SetClient(cli *Client) {
svc.mu.Lock()
defer svc.mu.Unlock()
@ -66,6 +74,7 @@ func (svc *Service) String() string {
return buf.String()
}
// Respond is a convenitne method to respond to a user with the provided message
func (svc *Service) Respond(user, msg string) error {
if svc.cli == nil {
return fmt.Errorf("service not connected")
@ -73,6 +82,8 @@ func (svc *Service) Respond(user, msg string) error {
return svc.cli.Send(user, msg)
}
// Run runs the service tunil the context is done, if an error occurred at any point
// an error is returned.
func (svc *Service) Run(ctx context.Context) error {
// create the service user's client in a loop until successful
// TODO: Should this timeout? Use a context?
@ -147,6 +158,7 @@ func (svc *Service) handle(ctx context.Context, msg Message) error {
return err
}
// TextFunc registers a handler for processing textual callbacks (commands)
func (svc *Service) TextFunc(name string, fn MessageTextHandlerFunc) {
svc.mu.Lock()
defer svc.mu.Unlock()
@ -154,6 +166,7 @@ func (svc *Service) TextFunc(name string, fn MessageTextHandlerFunc) {
svc.textFns[strings.ToUpper(name)] = fn
}
// EventFunc registers a handler for processing event callbacks (events)
func (svc *Service) EventFunc(name string, fn MessageEventHandlerFunc) {
svc.mu.Lock()
defer svc.mu.Unlock()

View File

@ -14,18 +14,21 @@ func DefaultState() string {
return os.ExpandEnv("$HOME/.config/salty/state.json")
}
// State represents the state of a client and the indices of its inbox(es)
type State struct {
sync.RWMutex
Indicies map[string]int64
}
// NewState returns a new empty state
func NewState() *State {
return &State{
Indicies: make(map[string]int64),
}
}
// GetIndex returns the current index for the name inbox
func (s *State) GetIndex(name string) int64 {
s.RLock()
defer s.RUnlock()
@ -33,6 +36,7 @@ func (s *State) GetIndex(name string) int64 {
return s.Indicies[name]
}
// SetIndex sets the index for the named inbox
func (s *State) SetIndex(name string, index int64) {
s.Lock()
defer s.Unlock()
@ -40,6 +44,7 @@ func (s *State) SetIndex(name string, index int64) {
s.Indicies[name] = index
}
// Bytes serialises the state into a byte slice
func (s *State) Bytes() ([]byte, error) {
s.Lock()
defer s.Unlock()
@ -52,6 +57,7 @@ func (s *State) Bytes() ([]byte, error) {
return data, nil
}
// Save persists the state to a file on disk
func (s *State) Save(fn string) error {
data, err := s.Bytes()
if err != nil {
@ -65,6 +71,7 @@ func (s *State) Save(fn string) error {
return nil
}
// LoadState loads a state from a file on disk
func LoadState(r io.Reader) (*State, error) {
var state *State