Initial QSfera import
This commit is contained in:
+130
@@ -0,0 +1,130 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user