131 lines
3.2 KiB
Go
131 lines
3.2 KiB
Go
package ldap
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
var ErrEmptyPostalAddress = errors.New("ldap: postal address cannot be empty")
|
|
|
|
// PostalAddress represents an RFC 4517 Postal Address
|
|
// A postal address is a sequence of strings of one or more arbitrary UCS
|
|
// characters, which form the lines of the address.
|
|
type PostalAddress struct {
|
|
lines []string
|
|
}
|
|
|
|
// NewPostalAddress creates a new PostalAddress by copying non-empty lines from the provided slice of strings.
|
|
func NewPostalAddress(lines []string) (*PostalAddress, error) {
|
|
copiedLines := make([]string, 0, len(lines))
|
|
for _, line := range lines {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
copiedLines = append(copiedLines, line)
|
|
}
|
|
|
|
if len(copiedLines) == 0 {
|
|
return nil, ErrEmptyPostalAddress
|
|
}
|
|
|
|
return &PostalAddress{lines: copiedLines}, nil
|
|
}
|
|
|
|
// Lines returns a copy of the address lines as a slice of strings.
|
|
func (p *PostalAddress) Lines() []string {
|
|
copiedLines := make([]string, len(p.lines))
|
|
copy(copiedLines, p.lines)
|
|
return copiedLines
|
|
}
|
|
|
|
// String returns the postal address as a single string, with lines joined by newline characters.
|
|
func (p *PostalAddress) String() string {
|
|
return strings.Join(p.lines, "\n")
|
|
}
|
|
|
|
// Escape encodes special characters in the PostalAddress lines as per RFC 4517 and appends a `$` at the end of each line.
|
|
func (p *PostalAddress) Escape() string {
|
|
builder := &strings.Builder{}
|
|
|
|
for _, line := range p.lines {
|
|
for _, char := range line {
|
|
switch char {
|
|
case '\\':
|
|
builder.WriteString("\\5C")
|
|
case '$':
|
|
builder.WriteString("\\24")
|
|
default:
|
|
builder.WriteRune(char)
|
|
}
|
|
}
|
|
|
|
builder.WriteRune('$')
|
|
}
|
|
|
|
return builder.String()
|
|
}
|
|
|
|
// ParsePostalAddress parses an RFC 4517 escaped postal address string into a PostalAddress object or returns an error.
|
|
func ParsePostalAddress(escaped string) (*PostalAddress, error) {
|
|
lines := strings.Split(escaped, "$")
|
|
parsedLines := make([]string, 0, len(lines))
|
|
const totalEscapeLen = 3
|
|
|
|
for _, line := range lines {
|
|
if line == "" {
|
|
// Skip empty lines
|
|
continue
|
|
}
|
|
|
|
builder := &strings.Builder{}
|
|
for i := 0; i < len(line); i++ {
|
|
char := line[i]
|
|
if char == '\\' && i+totalEscapeLen <= len(line) {
|
|
escapeSeq := line[i+1 : i+totalEscapeLen]
|
|
switch escapeSeq {
|
|
case "5C", "5c":
|
|
builder.WriteRune('\\')
|
|
i += 2
|
|
case "24":
|
|
builder.WriteRune('$')
|
|
i += 2
|
|
default:
|
|
return nil, fmt.Errorf("invalid escape sequence: \\%s at position %d", escapeSeq, i)
|
|
}
|
|
} else if char == '\\' {
|
|
return nil, fmt.Errorf("incomplete escape sequence at position %d", i)
|
|
} else {
|
|
builder.WriteByte(char)
|
|
}
|
|
}
|
|
parsedLines = append(parsedLines, builder.String())
|
|
}
|
|
|
|
if len(parsedLines) == 0 {
|
|
return nil, ErrEmptyPostalAddress
|
|
}
|
|
|
|
return &PostalAddress{lines: parsedLines}, nil
|
|
}
|
|
|
|
// Equal compares the current PostalAddress with another PostalAddress and returns true if they are identical.
|
|
func (p *PostalAddress) Equal(other *PostalAddress) bool {
|
|
if p == other {
|
|
return true
|
|
}
|
|
if p == nil || other == nil {
|
|
return false
|
|
}
|
|
|
|
if len(p.lines) != len(other.lines) {
|
|
return false
|
|
}
|
|
for i := range p.lines {
|
|
if p.lines[i] != other.lines[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|