Documentation
¶
Overview ¶
Package cgpapi implements a client for the CommuniGate Pro PWD/CLI protocol: the TCP session used to authenticate against a CommuniGate Pro server and then issue CLI administration commands, documented at https://doc.communigatepro.ru/development/CLI.html (command grammar and semantics). The PWD login layer itself is not covered by that document and is implemented here from empirical observation of a live server.
Connecting ¶
Dial opens a connection, authenticates using PLAIN (USER/PASS), APOP, or CRAM-MD5, and returns a ready-to-use Client:
c, err := cgpapi.Dial(ctx, cgpapi.Options{
Addr: "cgpro.example.com:106",
Login: "admin@example.com",
Password: "hunter2",
})
A connection dropped unexpectedly is transparently reopened and re-authenticated on the next call, using the same Options, unless Client.Close was called explicitly - after Close, every method returns ErrClosed instead of reconnecting.
A Client is not safe for concurrent use by multiple goroutines: the protocol is a single stateful request/response session over one TCP connection. Use a separate Client per goroutine, or synchronize access to a shared Client externally.
Sending commands ¶
Client.Send runs any already-formatted CLI command line - built using github.com/gmyzovsky/go-cgp-data to encode any data-bearing token the command's own grammar requires - and returns its decoded response value, if the command produced one. Send already covers every CLI command, documented or not. Typed wrapper methods for specific commands (e.g. Client.CreateAccount) build on top of Send and are added incrementally, one command category at a time.
Index ¶
- Variables
- type Client
- func (c *Client) ChangePassword(ctx context.Context, newPassword string) error
- func (c *Client) Close(ctx context.Context) error
- func (c *Client) CreateAccount(ctx context.Context, in *CreateAccountInput) (*CreateAccountOutput, error)
- func (c *Client) DeleteAccount(ctx context.Context, in *DeleteAccountInput) (*DeleteAccountOutput, error)
- func (c *Client) GetAccountEffectiveSettings(ctx context.Context, in *GetAccountEffectiveSettingsInput) (*GetAccountEffectiveSettingsOutput, error)
- func (c *Client) GetAccountSettings(ctx context.Context, in *GetAccountSettingsInput) (*GetAccountSettingsOutput, error)
- func (c *Client) LastCommand() string
- func (c *Client) ListAccounts(ctx context.Context, in *ListAccountsInput) (*ListAccountsOutput, error)
- func (c *Client) ListDomainObjects(ctx context.Context, in *ListDomainObjectsInput) (*ListDomainObjectsOutput, error)
- func (c *Client) ListDomainTelnums(ctx context.Context, in *ListDomainTelnumsInput) (*ListDomainTelnumsOutput, error)
- func (c *Client) RenameAccount(ctx context.Context, in *RenameAccountInput) (*RenameAccountOutput, error)
- func (c *Client) Send(ctx context.Context, line string) (cgpdata.Value, error)
- func (c *Client) SetAccountPassword(ctx context.Context, in *SetAccountPasswordInput) (*SetAccountPasswordOutput, error)
- func (c *Client) SetAccountSettings(ctx context.Context, in *SetAccountSettingsInput) (*SetAccountSettingsOutput, error)
- func (c *Client) UpdateAccountSettings(ctx context.Context, in *UpdateAccountSettingsInput) (*UpdateAccountSettingsOutput, error)
- func (c *Client) VerifyAccountPassword(ctx context.Context, in *VerifyAccountPasswordInput) (*VerifyAccountPasswordOutput, error)
- type CreateAccountInput
- type CreateAccountOutput
- type DeleteAccountInput
- type DeleteAccountOutput
- type GetAccountEffectiveSettingsInput
- type GetAccountEffectiveSettingsOutput
- type GetAccountSettingsInput
- type GetAccountSettingsOutput
- type ListAccountsInput
- type ListAccountsOutput
- type ListDomainObjectsInput
- type ListDomainObjectsOutput
- type ListDomainTelnumsInput
- type ListDomainTelnumsOutput
- type Options
- type RenameAccountInput
- type RenameAccountOutput
- type Response
- type ResponseError
- type SecureLogin
- type SetAccountPasswordInput
- type SetAccountPasswordOutput
- type SetAccountSettingsInput
- type SetAccountSettingsOutput
- type TLSMode
- type UpdateAccountSettingsInput
- type UpdateAccountSettingsOutput
- type VerifyAccountPasswordInput
- type VerifyAccountPasswordOutput
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrClosed = errors.New("cgpapi: client is closed")
ErrClosed is returned by every Client method called after Close, instead of the transparent auto-reconnect that would otherwise happen on a dropped connection.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a connection to a CommuniGate Pro PWD/CLI server. Create one with Dial.
A Client is not safe for concurrent use by multiple goroutines: the underlying protocol is a single stateful request/response session over one TCP connection. Use a separate Client per goroutine, or synchronize access to a shared Client externally.
func Dial ¶
Dial connects to and authenticates with the server described by opts, and returns a ready-to-use Client.
Example ¶
// A real program dials a live CommuniGate Pro server; this example
// dials a minimal fake one standing in for it, so it can run
// deterministically without network access.
srv, err := startFakeServer("200 fake PWD Server ready", func(line string) []string {
if r, ok := stdLoginPlain(line); ok {
return r
}
return []string{"200 OK"}
})
if err != nil {
fmt.Println("error:", err)
return
}
defer srv.ln.Close()
ctx := context.Background()
c, err := Dial(ctx, Options{
Addr: srv.Addr(),
Login: "admin@example.com",
Password: "hunter2",
SecureLogin: PlainLogin,
})
if err != nil {
fmt.Println("error:", err)
return
}
defer c.Close(ctx)
fmt.Println("connected")
Output: connected
func (*Client) ChangePassword ¶
ChangePassword changes the password of the currently authenticated account, via the PWD/CLI NEWPASS command.
func (*Client) Close ¶
Close ends the session, sending QUIT if the connection is currently open, and marks the Client closed: subsequent calls return ErrClosed instead of transparently reconnecting. Call Dial again (or build a new Client) to resume use.
func (*Client) CreateAccount ¶
func (c *Client) CreateAccount(ctx context.Context, in *CreateAccountInput) (*CreateAccountOutput, error)
CreateAccount runs the CREATEACCOUNT command.
Example ¶
srv, err := startFakeServer("200 fake PWD Server ready", func(line string) []string {
if r, ok := stdLoginPlain(line); ok {
return r
}
return []string{"200 OK"}
})
if err != nil {
fmt.Println("error:", err)
return
}
defer srv.ln.Close()
ctx := context.Background()
c, err := Dial(ctx, Options{
Addr: srv.Addr(),
Login: "admin@example.com",
Password: "hunter2",
SecureLogin: PlainLogin,
})
if err != nil {
fmt.Println("error:", err)
return
}
defer c.Close(ctx)
_, err = c.CreateAccount(ctx, &CreateAccountInput{
AccountName: "alice@example.com",
AccountType: "MultiMailbox",
})
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("account created")
Output: account created
func (*Client) DeleteAccount ¶
func (c *Client) DeleteAccount(ctx context.Context, in *DeleteAccountInput) (*DeleteAccountOutput, error)
DeleteAccount runs the DELETEACCOUNT command.
func (*Client) GetAccountEffectiveSettings ¶
func (c *Client) GetAccountEffectiveSettings(ctx context.Context, in *GetAccountEffectiveSettingsInput) (*GetAccountEffectiveSettingsOutput, error)
GetAccountEffectiveSettings runs the GETACCOUNTEFFECTIVESETTINGS command.
func (*Client) GetAccountSettings ¶
func (c *Client) GetAccountSettings(ctx context.Context, in *GetAccountSettingsInput) (*GetAccountSettingsOutput, error)
GetAccountSettings runs the GETACCOUNTSETTINGS command.
func (*Client) LastCommand ¶
LastCommand returns the first word of the most recently sent command line, for debugging - never the full line, so it never echoes a credential argument.
func (*Client) ListAccounts ¶
func (c *Client) ListAccounts(ctx context.Context, in *ListAccountsInput) (*ListAccountsOutput, error)
ListAccounts runs the LISTACCOUNTS command.
func (*Client) ListDomainObjects ¶
func (c *Client) ListDomainObjects(ctx context.Context, in *ListDomainObjectsInput) (*ListDomainObjectsOutput, error)
ListDomainObjects runs the LISTDOMAINOBJECTS command.
func (*Client) ListDomainTelnums ¶
func (c *Client) ListDomainTelnums(ctx context.Context, in *ListDomainTelnumsInput) (*ListDomainTelnumsOutput, error)
ListDomainTelnums runs the LISTDOMAINTELNUMS command.
func (*Client) RenameAccount ¶
func (c *Client) RenameAccount(ctx context.Context, in *RenameAccountInput) (*RenameAccountOutput, error)
RenameAccount runs the RENAMEACCOUNT command.
func (*Client) Send ¶
Send runs line - a single, already-formatted CLI command line, using github.com/gmyzovsky/go-cgp-data to encode any data-bearing token it contains - against the server. It returns the decoded response value for a data-bearing (201) response, nil for a plain success (200) with no data, and a non-nil error for anything else, including a connection failure.
Send is a deliberately dumb pipe: it does not parse or generate CLI command grammar itself, since that grammar mixes bare keywords, data tokens, arrays, and dictionaries differently per command (see https://doc.communigatepro.ru/development/CLI.html). Building the right line for a specific command is the caller's job today, and typed wrapper methods' job for the commands that have one.
Example ¶
srv, err := startFakeServer("200 fake PWD Server ready", func(line string) []string {
if r, ok := stdLoginPlain(line); ok {
return r
}
if commandVerb(line) == "GETVERSION" {
return []string{`201 "6.4.1"`}
}
return []string{"200 OK"}
})
if err != nil {
fmt.Println("error:", err)
return
}
defer srv.ln.Close()
ctx := context.Background()
c, err := Dial(ctx, Options{
Addr: srv.Addr(),
Login: "admin@example.com",
Password: "hunter2",
SecureLogin: PlainLogin,
})
if err != nil {
fmt.Println("error:", err)
return
}
defer c.Close(ctx)
v, err := c.Send(ctx, "GETVERSION")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(v)
Output: 6.4.1
func (*Client) SetAccountPassword ¶
func (c *Client) SetAccountPassword(ctx context.Context, in *SetAccountPasswordInput) (*SetAccountPasswordOutput, error)
SetAccountPassword runs the SETACCOUNTPASSWORD command.
func (*Client) SetAccountSettings ¶
func (c *Client) SetAccountSettings(ctx context.Context, in *SetAccountSettingsInput) (*SetAccountSettingsOutput, error)
SetAccountSettings runs the SETACCOUNTSETTINGS command.
func (*Client) UpdateAccountSettings ¶
func (c *Client) UpdateAccountSettings(ctx context.Context, in *UpdateAccountSettingsInput) (*UpdateAccountSettingsOutput, error)
UpdateAccountSettings runs the UPDATEACCOUNTSETTINGS command.
func (*Client) VerifyAccountPassword ¶
func (c *Client) VerifyAccountPassword(ctx context.Context, in *VerifyAccountPasswordInput) (*VerifyAccountPasswordOutput, error)
VerifyAccountPassword runs the VERIFYACCOUNTPASSWORD command; a non-nil error (typically a *ResponseError) means the password did not verify.
type CreateAccountInput ¶
type CreateAccountInput struct {
AccountName string // required
AccountType string // optional bare keyword, e.g. "MultiMailbox"
External bool // optional
Settings cgpdata.Dictionary // optional
}
CreateAccountInput creates a new account.
type CreateAccountOutput ¶
type CreateAccountOutput struct{}
CreateAccountOutput holds the result of CreateAccount.
type DeleteAccountInput ¶
type DeleteAccountInput struct {
AccountName string // required
}
DeleteAccountInput deletes an account.
type DeleteAccountOutput ¶
type DeleteAccountOutput struct{}
DeleteAccountOutput holds the result of DeleteAccount.
type GetAccountEffectiveSettingsInput ¶
type GetAccountEffectiveSettingsInput struct {
AccountName string // required
}
GetAccountEffectiveSettingsInput reads an account's settings merged with every applicable Domain/Cluster/Server default.
type GetAccountEffectiveSettingsOutput ¶
type GetAccountEffectiveSettingsOutput struct {
Settings cgpdata.Dictionary
}
GetAccountEffectiveSettingsOutput holds the result of GetAccountEffectiveSettings.
type GetAccountSettingsInput ¶
type GetAccountSettingsInput struct {
AccountName string // required
}
GetAccountSettingsInput reads an account's own stored settings.
type GetAccountSettingsOutput ¶
type GetAccountSettingsOutput struct {
Settings cgpdata.Dictionary
}
GetAccountSettingsOutput holds the result of GetAccountSettings.
type ListAccountsInput ¶
type ListAccountsInput struct {
DomainName string // optional; empty lists across every accessible domain
}
ListAccountsInput lists accounts, optionally restricted to one domain.
type ListAccountsOutput ¶
ListAccountsOutput holds the result of ListAccounts.
type ListDomainObjectsInput ¶
type ListDomainObjectsInput struct {
DomainName string // required
Limit int // required; the server returns at most this many objects per call
Filter string // optional
What string // optional bare keyword restricting which object kinds are returned
Cookie string // optional; continues a previous paged listing
}
ListDomainObjectsInput lists Directory objects (accounts, forwarders, group objects, ...) within a domain, optionally filtered, in pages bounded by Limit.
type ListDomainObjectsOutput ¶
ListDomainObjectsOutput holds the result of ListDomainObjects.
type ListDomainTelnumsInput ¶
type ListDomainTelnumsInput struct {
DomainName string // required
Limit int // required
Filter string // optional
}
ListDomainTelnumsInput lists telephone numbers registered within a domain, optionally filtered, in pages bounded by Limit.
type ListDomainTelnumsOutput ¶
ListDomainTelnumsOutput holds the result of ListDomainTelnums.
type Options ¶
type Options struct {
// Addr is the "host:port" of the PWD/CLI server. Required.
Addr string
// Login and Password authenticate the session. Both required.
// Password is kept for the Client's whole lifetime (not just
// during the initial handshake) to support transparent
// auto-reconnect.
Login string
Password string
// TLS selects the transport security mode. Defaults to NoTLS.
TLS TLSMode
// TLSConfig is used for both ImplicitTLS and StartTLS connections.
// A nil value uses a zero-value tls.Config. ServerName, when unset
// and Addr's host is not an IP literal, is derived from Addr for
// both modes.
TLSConfig *tls.Config
// SecureLogin selects the authentication method. Defaults to
// AutoSecureLogin.
SecureLogin SecureLogin
// DialTimeout bounds the initial TCP connect (and, for
// ImplicitTLS, the TLS handshake). Zero means no timeout beyond
// what ctx itself imposes.
DialTimeout time.Duration
}
Options configures Dial.
type RenameAccountInput ¶
type RenameAccountInput struct {
OldAccountName string // required
NewAccountName string // required
StoragePath string // optional
}
RenameAccountInput renames an account, optionally relocating its storage.
type RenameAccountOutput ¶
type RenameAccountOutput struct{}
RenameAccountOutput holds the result of RenameAccount.
type Response ¶
Response is a parsed PWD/CLI protocol status line: a three-digit status code (or "+" for a SASL continuation) plus its message text.
type ResponseError ¶
type ResponseError struct {
Code string // e.g. "515"; empty if the server sent an unparseable status line
Message string
Command string
}
ResponseError reports a CommuniGate Pro PWD/CLI server response indicating failure: a status code outside the 2xx/3xx success range.
Command is only the command's first word (e.g. "SetAccountPassword", "PASS"), never the full line - several commands legitimately carry credentials as arguments (PASS, SetAccountPassword, ...), and the full line is deliberately never captured in an error to avoid leaking one into logs.
func (*ResponseError) Error ¶
func (e *ResponseError) Error() string
type SecureLogin ¶
type SecureLogin int
SecureLogin selects the authentication method Dial uses.
const ( // AutoSecureLogin picks APOP when the transport is not (yet) // encrypted, or PLAIN once TLS is already established (ImplicitTLS // or StartTLS): APOP works under any server-side password-storage // policy, and sending PLAIN in the clear is only avoided when the // transport isn't already encrypting it anyway. This is the zero // value/default; an explicit SecureLogin value is always honored // exactly as given, on any transport. AutoSecureLogin SecureLogin = iota PlainLogin APOPLogin CRAMMD5Login )
func (SecureLogin) String ¶
func (s SecureLogin) String() string
type SetAccountPasswordInput ¶
type SetAccountPasswordInput struct {
AccountName string // required
NewPassword string // required
Method string // optional bare keyword, e.g. "CLEAR"
Check bool // optional: verify NewPassword meets the domain's password-strength policy before setting it
}
SetAccountPasswordInput sets an account's password.
type SetAccountPasswordOutput ¶
type SetAccountPasswordOutput struct{}
SetAccountPasswordOutput holds the result of SetAccountPassword.
type SetAccountSettingsInput ¶
type SetAccountSettingsInput struct {
AccountName string // required
Settings cgpdata.Dictionary // required
}
SetAccountSettingsInput replaces an account's entire stored settings with Settings.
type SetAccountSettingsOutput ¶
type SetAccountSettingsOutput struct{}
SetAccountSettingsOutput holds the result of SetAccountSettings.
type TLSMode ¶
type TLSMode int
TLSMode selects how (or whether) a Client establishes transport security with the server.
const ( // NoTLS connects in the clear. This is the zero value/default. NoTLS TLSMode = iota // ImplicitTLS establishes a TLS session before the PWD greeting is // read, for servers that expect TLS from the first byte // (conventionally offered on port 1106). ImplicitTLS // StartTLS connects in the clear, reads the greeting, sends the // STLS command, and upgrades the existing connection to TLS before // authenticating. StartTLS )
type UpdateAccountSettingsInput ¶
type UpdateAccountSettingsInput struct {
AccountName string // required
Settings cgpdata.Dictionary // required
}
UpdateAccountSettingsInput merges Settings into an account's existing stored settings, leaving keys not present in Settings unchanged.
type UpdateAccountSettingsOutput ¶
type UpdateAccountSettingsOutput struct{}
UpdateAccountSettingsOutput holds the result of UpdateAccountSettings.
type VerifyAccountPasswordInput ¶
type VerifyAccountPasswordInput struct {
AccountName string // required
Password string // required
}
VerifyAccountPasswordInput checks a candidate password against an account's actual one, without changing it.
type VerifyAccountPasswordOutput ¶
type VerifyAccountPasswordOutput struct{}
VerifyAccountPasswordOutput holds the result of VerifyAccountPassword.