Fehler behoben
This commit is contained in:
parent
832ebf099a
commit
b0c9541c14
628
main.go
628
main.go
@ -18,451 +18,363 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Entwickelt mit Unterstützung von Claude.ai, einem KI-Assistenten von Anthropic, PBC.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/xml"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/transform"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
LDAPServer string
|
||||
BindDN string
|
||||
BindPassword string
|
||||
SearchBase string
|
||||
ServerPort string
|
||||
}
|
||||
|
||||
type AddressBook struct {
|
||||
XMLName xml.Name `xml:"AddressBook"`
|
||||
Version string `xml:"version,attr"`
|
||||
PBGroups []PBGroup `xml:"pbgroup"`
|
||||
Contacts []Contact `xml:"Contact"`
|
||||
}
|
||||
|
||||
type PBGroup struct {
|
||||
ID int `xml:"id,attr"`
|
||||
Name string `xml:"name"`
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
FirstName string `xml:"FirstName"`
|
||||
LastName string `xml:"LastName"`
|
||||
Frequent int `xml:"Frequent"`
|
||||
Phones []Phone `xml:"Phone"`
|
||||
Department string `xml:"Department,omitempty"`
|
||||
Group int `xml:"Group"`
|
||||
type Person struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
Department string
|
||||
Phones []Phone
|
||||
RawPhoneNumber string
|
||||
InternalPhone string
|
||||
IsContact bool // Neues Feld zur Unterscheidung zwischen Benutzern und Kontakten
|
||||
}
|
||||
|
||||
type Phone struct {
|
||||
Type string `xml:"type,attr"`
|
||||
PhoneNumber string `xml:"phonenumber"`
|
||||
AccountIndex int `xml:"accountindex"`
|
||||
PhoneNumber string
|
||||
Type string
|
||||
}
|
||||
|
||||
type YealinkPhoneBook struct {
|
||||
XMLName xml.Name `xml:"YealinkIPPhoneDirectory"`
|
||||
Entries []YealinkEntry `xml:"DirectoryEntry"`
|
||||
type Config struct {
|
||||
PhoneRules struct {
|
||||
Country struct {
|
||||
Prefix string `yaml:"prefix"`
|
||||
AreaCodes []string `yaml:"area_codes"`
|
||||
InternalPrefix string `yaml:"internal_prefix"`
|
||||
} `yaml:"country"`
|
||||
InvalidNumber string `yaml:"invalid_number"`
|
||||
} `yaml:"phone_rules"`
|
||||
}
|
||||
|
||||
type YealinkEntry struct {
|
||||
Name string `xml:"Name"`
|
||||
Telephone string `xml:"Telephone"`
|
||||
Mobile string `xml:"Mobile,omitempty"`
|
||||
OtherMobile string `xml:"Other,omitempty"`
|
||||
}
|
||||
|
||||
// Neue Struktur für das Contact-Format
|
||||
type ContactXML struct {
|
||||
XMLName xml.Name `xml:"contacts"`
|
||||
Contacts []ContactEntry `xml:"contact"`
|
||||
}
|
||||
|
||||
type ContactEntry struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Number string `xml:"number,attr"`
|
||||
FirstName string `xml:"firstname,attr"`
|
||||
LastName string `xml:"lastname,attr"`
|
||||
Phone string `xml:"phone,attr"`
|
||||
Mobile string `xml:"mobile,attr"`
|
||||
Email string `xml:"email,attr"`
|
||||
Address string `xml:"address,attr"`
|
||||
City string `xml:"city,attr"`
|
||||
State string `xml:"state,attr"`
|
||||
Zip string `xml:"zip,attr"`
|
||||
Comment string `xml:"comment,attr"`
|
||||
ID string `xml:"id,attr"`
|
||||
Info string `xml:"info,attr"`
|
||||
Presence string `xml:"presence,attr"`
|
||||
Starred string `xml:"starred,attr"`
|
||||
Directory string `xml:"directory,attr"`
|
||||
type LDAPConfig struct {
|
||||
Server string
|
||||
Port string
|
||||
BindDN string
|
||||
BindPassword string
|
||||
BaseDN string
|
||||
Filter string
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
config Config
|
||||
cache struct {
|
||||
grandstreamFile *os.File
|
||||
yealinkFile *os.File
|
||||
contactFile *os.File
|
||||
hash []byte
|
||||
lastUpdate time.Time
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
cachedData []Person
|
||||
lastUpdate time.Time
|
||||
tmpl *template.Template
|
||||
ldapConfig LDAPConfig
|
||||
serverPort string
|
||||
config Config
|
||||
)
|
||||
|
||||
func init() {
|
||||
loadEnvConfig()
|
||||
loadYAMLConfig()
|
||||
loadTemplate()
|
||||
}
|
||||
|
||||
func loadEnvConfig() {
|
||||
requiredEnvVars := []string{
|
||||
"LDAP_SERVER", "LDAP_PORT", "LDAP_BIND_DN", "LDAP_BIND_PASSWORD",
|
||||
"LDAP_BASE_DN", "LDAP_FILTER", "SERVER_PORT",
|
||||
}
|
||||
|
||||
for _, envVar := range requiredEnvVars {
|
||||
if value := os.Getenv(envVar); value == "" {
|
||||
log.Fatalf("Required environment variable %s is not set", envVar)
|
||||
}
|
||||
}
|
||||
|
||||
ldapConfig = LDAPConfig{
|
||||
Server: os.Getenv("LDAP_SERVER"),
|
||||
Port: os.Getenv("LDAP_PORT"),
|
||||
BindDN: os.Getenv("LDAP_BIND_DN"),
|
||||
BindPassword: os.Getenv("LDAP_BIND_PASSWORD"),
|
||||
BaseDN: os.Getenv("LDAP_BASE_DN"),
|
||||
Filter: os.Getenv("LDAP_FILTER"),
|
||||
}
|
||||
|
||||
serverPort = os.Getenv("SERVER_PORT")
|
||||
}
|
||||
|
||||
func loadYAMLConfig() {
|
||||
configPath := filepath.Join("static", "config.yaml")
|
||||
configFile, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read config file: %v", err)
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(configFile, &config)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse config file: %v", err)
|
||||
}
|
||||
|
||||
validateConfig()
|
||||
}
|
||||
|
||||
func validateConfig() {
|
||||
if config.PhoneRules.Country.Prefix == "" ||
|
||||
len(config.PhoneRules.Country.AreaCodes) == 0 ||
|
||||
config.PhoneRules.Country.InternalPrefix == "" ||
|
||||
config.PhoneRules.InvalidNumber == "" {
|
||||
log.Fatalf("Missing required configuration in config.yaml")
|
||||
}
|
||||
}
|
||||
|
||||
func loadTemplate() {
|
||||
var err error
|
||||
tmpl, err = template.ParseFiles(filepath.Join("static", "index.html"))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
config = Config{
|
||||
LDAPServer: os.Getenv("LDAP_SERVER"),
|
||||
BindDN: os.Getenv("BIND_DN"),
|
||||
BindPassword: os.Getenv("BIND_PASSWORD"),
|
||||
SearchBase: os.Getenv("SEARCH_BASE"),
|
||||
ServerPort: os.Getenv("SERVER_PORT"),
|
||||
}
|
||||
go cacheRefresher()
|
||||
|
||||
if config.ServerPort == "" {
|
||||
config.ServerPort = "8080"
|
||||
}
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||
http.HandleFunc("/", handler)
|
||||
|
||||
// Initialisiere den Cache vor dem Start des Servers
|
||||
err = initializeCache()
|
||||
if err != nil {
|
||||
log.Fatalf("Fehler beim Initialisieren des Caches: %v", err)
|
||||
}
|
||||
|
||||
http.HandleFunc("/phonebook.xml", grandstreamPhoneBookHandler)
|
||||
http.HandleFunc("/yphonebook.xml", yealinkPhoneBookHandler)
|
||||
http.HandleFunc("/Contacts.xml", contactXMLHandler)
|
||||
http.HandleFunc("/phonebook.csv", csvExportHandler)
|
||||
|
||||
log.Printf("Server starting on port %s", config.ServerPort)
|
||||
log.Fatal(http.ListenAndServe(":"+config.ServerPort, nil))
|
||||
log.Printf("Server starting on port %s", serverPort)
|
||||
log.Fatal(http.ListenAndServe(":"+serverPort, nil))
|
||||
}
|
||||
|
||||
func grandstreamPhoneBookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
servePhoneBook(w, r, cache.grandstreamFile)
|
||||
}
|
||||
|
||||
func yealinkPhoneBookHandler(w http.ResponseWriter, r *http.Request) {
|
||||
servePhoneBook(w, r, cache.yealinkFile)
|
||||
}
|
||||
|
||||
func contactXMLHandler(w http.ResponseWriter, r *http.Request) {
|
||||
servePhoneBook(w, r, cache.contactFile)
|
||||
}
|
||||
|
||||
func csvExportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := updateCachedXMLIfNeeded()
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Aktualisieren der Kontakte", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
contacts, err := getADContacts()
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Abrufen der Kontakte", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/csv; charset=windows-1252")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=phonebook.csv")
|
||||
|
||||
if err := exportToCSV(contacts, w); err != nil {
|
||||
http.Error(w, "Fehler beim Exportieren als CSV", http.StatusInternalServerError)
|
||||
return
|
||||
func cacheRefresher() {
|
||||
for {
|
||||
newData, err := fetchDataFromLDAP()
|
||||
if err != nil {
|
||||
log.Printf("Error refreshing cache: %v", err)
|
||||
} else if !dataEqual(cachedData, newData) {
|
||||
cachedData = newData
|
||||
lastUpdate = time.Now()
|
||||
log.Println("Cache updated")
|
||||
}
|
||||
time.Sleep(5 * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
func servePhoneBook(w http.ResponseWriter, r *http.Request, file *os.File) {
|
||||
err := updateCachedXMLIfNeeded()
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Aktualisieren der Kontakte", http.StatusInternalServerError)
|
||||
return
|
||||
func dataEqual(a, b []Person) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
cache.mutex.RLock()
|
||||
defer cache.mutex.RUnlock()
|
||||
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
_, err = file.Seek(0, 0)
|
||||
if err != nil {
|
||||
log.Printf("Fehler beim Zurücksetzen des Datei-Offsets: %v", err)
|
||||
http.Error(w, "Interner Serverfehler", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, file)
|
||||
if err != nil {
|
||||
log.Printf("Fehler beim Kopieren der XML-Datei: %v", err)
|
||||
for i := range a {
|
||||
if !personEqual(a[i], b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func initializeCache() error {
|
||||
log.Println("Initialisiere Cache...")
|
||||
|
||||
// Führen Sie updateCachedXMLIfNeeded aus, um die Caching-Dateien zu erstellen
|
||||
err := updateCachedXMLIfNeeded()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fehler beim Initialisieren des Caches: %v", err)
|
||||
}
|
||||
|
||||
// Überprüfen Sie, ob alle erforderlichen Dateien erstellt wurden
|
||||
if cache.grandstreamFile == nil || cache.yealinkFile == nil || cache.contactFile == nil {
|
||||
return fmt.Errorf("Nicht alle erforderlichen Cache-Dateien wurden erstellt")
|
||||
}
|
||||
|
||||
log.Println("Cache erfolgreich initialisiert")
|
||||
return nil
|
||||
func personEqual(a, b Person) bool {
|
||||
return a.FirstName == b.FirstName &&
|
||||
a.LastName == b.LastName &&
|
||||
a.Email == b.Email &&
|
||||
a.InternalPhone == b.InternalPhone &&
|
||||
a.Department == b.Department &&
|
||||
phonesEqual(a.Phones, b.Phones)
|
||||
}
|
||||
|
||||
func updateCachedXMLIfNeeded() error {
|
||||
cache.mutex.Lock()
|
||||
defer cache.mutex.Unlock()
|
||||
|
||||
contacts, err := getADContacts()
|
||||
if err != nil {
|
||||
return err
|
||||
func phonesEqual(a, b []Phone) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
newHash := calculateHash(contacts)
|
||||
if cache.grandstreamFile != nil && cache.yealinkFile != nil && cache.contactFile != nil && bytes.Equal(newHash, cache.hash) {
|
||||
return nil
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
addressBook := AddressBook{
|
||||
Version: "1.0",
|
||||
PBGroups: []PBGroup{
|
||||
{ID: 1, Name: "Blacklist"},
|
||||
{ID: 2, Name: "Work"},
|
||||
{ID: 3, Name: "Friend"},
|
||||
},
|
||||
Contacts: contacts,
|
||||
}
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("Received request for: %s", r.URL.Path)
|
||||
|
||||
yealinkPhoneBook := YealinkPhoneBook{
|
||||
Entries: make([]YealinkEntry, len(contacts)),
|
||||
}
|
||||
|
||||
for i, c := range contacts {
|
||||
yealinkPhoneBook.Entries[i] = YealinkEntry{
|
||||
Name: c.FirstName + " " + c.LastName,
|
||||
Telephone: getPhoneByType(c.Phones, "Work"),
|
||||
Mobile: getPhoneByType(c.Phones, "Mobile"),
|
||||
OtherMobile: getPhoneByType(c.Phones, "Home"),
|
||||
if time.Since(lastUpdate) > 5*time.Minute {
|
||||
newData, err := fetchDataFromLDAP()
|
||||
if err != nil {
|
||||
log.Printf("Error refreshing cache: %v", err)
|
||||
} else {
|
||||
cachedData = newData
|
||||
lastUpdate = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// Erstellen der Contacts.xml
|
||||
contactXML := ContactXML{
|
||||
Contacts: make([]ContactEntry, len(contacts)),
|
||||
}
|
||||
|
||||
for i, c := range contacts {
|
||||
contactXML.Contacts[i] = ContactEntry{
|
||||
Name: c.FirstName + " " + c.LastName,
|
||||
Number: getPhoneByType(c.Phones, "Work"),
|
||||
FirstName: c.FirstName,
|
||||
LastName: c.LastName,
|
||||
Phone: getPhoneByType(c.Phones, "Work"),
|
||||
Mobile: getPhoneByType(c.Phones, "Mobile"),
|
||||
Presence: "0",
|
||||
Starred: "0",
|
||||
Directory: "0",
|
||||
}
|
||||
}
|
||||
|
||||
if err := saveToTempFile(&addressBook, &cache.grandstreamFile); err != nil {
|
||||
return fmt.Errorf("Fehler beim Speichern der Grandstream-XML: %v", err)
|
||||
}
|
||||
|
||||
if err := saveToTempFile(&yealinkPhoneBook, &cache.yealinkFile); err != nil {
|
||||
return fmt.Errorf("Fehler beim Speichern der Yealink-XML: %v", err)
|
||||
}
|
||||
|
||||
if err := saveToTempFile(&contactXML, &cache.contactFile); err != nil {
|
||||
return fmt.Errorf("Fehler beim Speichern der Contact-XML: %v", err)
|
||||
}
|
||||
|
||||
csvFile, err := os.OpenFile("phonebook.csv", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
err := tmpl.Execute(w, cachedData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fehler beim Erstellen der CSV-Datei: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
defer csvFile.Close()
|
||||
|
||||
if err := exportToCSV(contacts, csvFile); err != nil {
|
||||
return fmt.Errorf("Fehler beim Exportieren als CSV: %v", err)
|
||||
}
|
||||
|
||||
cache.hash = newHash
|
||||
cache.lastUpdate = time.Now()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveToTempFile(data interface{}, file **os.File) error {
|
||||
tempFile, err := os.CreateTemp("", "phonebook_*.xml")
|
||||
func fetchDataFromLDAP() ([]Person, error) {
|
||||
l, err := ldap.DialTLS("tcp", ldapConfig.Server+":"+ldapConfig.Port, &tls.Config{InsecureSkipVerify: true})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fehler beim Erstellen der temporären Datei: %v", err)
|
||||
}
|
||||
|
||||
enc := xml.NewEncoder(tempFile)
|
||||
enc.Indent("", " ")
|
||||
if err := enc.Encode(data); err != nil {
|
||||
tempFile.Close()
|
||||
os.Remove(tempFile.Name())
|
||||
return fmt.Errorf("Fehler beim Kodieren der XML: %v", err)
|
||||
}
|
||||
|
||||
if *file != nil {
|
||||
(*file).Close()
|
||||
os.Remove((*file).Name())
|
||||
}
|
||||
|
||||
*file = tempFile
|
||||
return nil
|
||||
}
|
||||
|
||||
func calculateHash(contacts []Contact) []byte {
|
||||
h := md5.New()
|
||||
for _, contact := range contacts {
|
||||
io.WriteString(h, contact.FirstName)
|
||||
io.WriteString(h, contact.LastName)
|
||||
for _, phone := range contact.Phones {
|
||||
io.WriteString(h, phone.PhoneNumber)
|
||||
}
|
||||
}
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func getADContacts() ([]Contact, error) {
|
||||
l, err := ldap.DialTLS("tcp", config.LDAPServer+":636", &tls.Config{InsecureSkipVerify: true})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LDAP-Verbindung fehlgeschlagen: %v", err)
|
||||
return nil, fmt.Errorf("failed to connect to LDAP server: %v", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
err = l.Bind(config.BindDN, config.BindPassword)
|
||||
err = l.Bind(ldapConfig.BindDN, ldapConfig.BindPassword)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LDAP-Bindung fehlgeschlagen: %v", err)
|
||||
return nil, fmt.Errorf("failed to bind to LDAP server: %v", err)
|
||||
}
|
||||
|
||||
// Erweiterter LDAP-Filter für Benutzer und Kontakte
|
||||
searchFilter := "(&(|(objectClass=user)(objectClass=contact))(|(telephoneNumber=*)(otherTelephone=*)(mobile=*)(homePhone=*))(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
|
||||
// Erweitere den Filter, um sowohl Benutzer als auch Kontakte einzuschließen
|
||||
combinedFilter := fmt.Sprintf("(|(objectClass=user)(objectClass=contact)%s)", ldapConfig.Filter)
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
config.SearchBase,
|
||||
ldapConfig.BaseDN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
searchFilter,
|
||||
[]string{"givenName", "sn", "displayName", "telephoneNumber", "otherTelephone", "mobile", "homePhone"},
|
||||
combinedFilter,
|
||||
[]string{"objectClass", "givenName", "sn", "mail", "telephoneNumber", "mobile", "otherTelephone", "physicalDeliveryOfficeName"},
|
||||
nil,
|
||||
)
|
||||
|
||||
sr, err := l.Search(searchRequest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LDAP-Suche fehlgeschlagen: %v", err)
|
||||
return nil, fmt.Errorf("failed to search LDAP server: %v", err)
|
||||
}
|
||||
|
||||
var contacts []Contact
|
||||
var people []Person
|
||||
for _, entry := range sr.Entries {
|
||||
firstName := entry.GetAttributeValue("givenName")
|
||||
lastName := entry.GetAttributeValue("sn")
|
||||
|
||||
if firstName == "" || lastName == "" {
|
||||
if !isValidEntry(entry) {
|
||||
continue
|
||||
}
|
||||
|
||||
phone := formatPhoneNumber(entry.GetAttributeValue("telephoneNumber"))
|
||||
otherMobile := formatPhoneNumber(entry.GetAttributeValue("otherMobile"))
|
||||
mobile := formatPhoneNumber(entry.GetAttributeValue("mobile"))
|
||||
isContact := isContactObject(entry)
|
||||
|
||||
if isExcludedNumber(phone) || isExcludedNumber(otherMobile) || isExcludedNumber(mobile) {
|
||||
continue
|
||||
person := Person{
|
||||
FirstName: entry.GetAttributeValue("givenName"),
|
||||
LastName: entry.GetAttributeValue("sn"),
|
||||
Email: entry.GetAttributeValue("mail"),
|
||||
Department: entry.GetAttributeValue("physicalDeliveryOfficeName"),
|
||||
IsContact: isContact,
|
||||
}
|
||||
|
||||
if phone != "" || otherMobile != "" || mobile != "" {
|
||||
contact := Contact{
|
||||
FirstName: firstName,
|
||||
LastName: lastName,
|
||||
Phones: []Phone{},
|
||||
Group: 2, // Standardmäßig zur "Work"-Gruppe hinzufügen
|
||||
officePhone := entry.GetAttributeValue("telephoneNumber")
|
||||
mobilePhone := entry.GetAttributeValue("mobile")
|
||||
|
||||
// Normalisiere die Telefonnummern für den Vergleich
|
||||
normalizedOffice := normalizePhoneNumber(officePhone)
|
||||
normalizedMobile := normalizePhoneNumber(mobilePhone)
|
||||
|
||||
if normalizedOffice == normalizedMobile && mobilePhone != "" {
|
||||
formattedMobile := formatPhoneNumber(mobilePhone)
|
||||
person.Phones = append(person.Phones, Phone{PhoneNumber: formattedMobile, Type: "Mobil"})
|
||||
person.RawPhoneNumber = formattedMobile
|
||||
person.InternalPhone = "" // Keine interne Rufnummer für Mobiltelefone
|
||||
} else if officePhone != "" {
|
||||
formattedPhone := formatPhoneNumber(officePhone)
|
||||
person.Phones = append(person.Phones, Phone{PhoneNumber: formattedPhone, Type: "Office"})
|
||||
person.InternalPhone = extractInternalNumber(officePhone)
|
||||
person.RawPhoneNumber = formattedPhone
|
||||
|
||||
if mobilePhone != "" && normalizedOffice != normalizedMobile {
|
||||
formattedMobile := formatPhoneNumber(mobilePhone)
|
||||
person.Phones = append(person.Phones, Phone{PhoneNumber: formattedMobile, Type: "Mobil"})
|
||||
}
|
||||
if phone != "" {
|
||||
contact.Phones = append(contact.Phones, Phone{Type: "Work", PhoneNumber: phone, AccountIndex: 0})
|
||||
}
|
||||
if mobile != "" {
|
||||
contact.Phones = append(contact.Phones, Phone{Type: "Mobile", PhoneNumber: mobile, AccountIndex: 0})
|
||||
}
|
||||
if otherMobile != "" {
|
||||
contact.Phones = append(contact.Phones, Phone{Type: "Home", PhoneNumber: otherMobile, AccountIndex: 0})
|
||||
}
|
||||
contacts = append(contacts, contact)
|
||||
} else if mobilePhone != "" {
|
||||
formattedMobile := formatPhoneNumber(mobilePhone)
|
||||
person.Phones = append(person.Phones, Phone{PhoneNumber: formattedMobile, Type: "Mobil"})
|
||||
person.RawPhoneNumber = formattedMobile
|
||||
person.InternalPhone = "" // Keine interne Rufnummer für Mobiltelefone
|
||||
}
|
||||
|
||||
for _, otherPhone := range entry.GetAttributeValues("otherTelephone") {
|
||||
person.Phones = append(person.Phones, Phone{PhoneNumber: formatPhoneNumber(otherPhone), Type: "Other"})
|
||||
}
|
||||
|
||||
if (len(person.Phones) > 0 && person.Email != "") || isContact {
|
||||
people = append(people, person)
|
||||
}
|
||||
}
|
||||
|
||||
return contacts, nil
|
||||
return people, nil
|
||||
}
|
||||
|
||||
func removeDuplicateNumbers(phones []Phone) []Phone {
|
||||
seen := make(map[string]bool)
|
||||
result := []Phone{}
|
||||
func isValidEntry(entry *ldap.Entry) bool {
|
||||
firstName := entry.GetAttributeValue("givenName")
|
||||
lastName := entry.GetAttributeValue("sn")
|
||||
telephoneNumber := entry.GetAttributeValue("telephoneNumber")
|
||||
mobile := entry.GetAttributeValue("mobile")
|
||||
|
||||
for _, phone := range phones {
|
||||
if !seen[phone.PhoneNumber] {
|
||||
seen[phone.PhoneNumber] = true
|
||||
result = append(result, phone)
|
||||
} else if phone.Type == "Work" {
|
||||
// Wenn die Nummer bereits gesehen wurde und dies die "Work" Nummer ist,
|
||||
// ersetzen wir die vorherige (wahrscheinlich "Mobile") mit dieser.
|
||||
for i, p := range result {
|
||||
if p.PhoneNumber == phone.PhoneNumber {
|
||||
result[i] = phone
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Für Kontakte erlauben wir auch Einträge ohne Telefonnummer
|
||||
isContact := isContactObject(entry)
|
||||
|
||||
return result
|
||||
return (firstName != "" && lastName != "") &&
|
||||
(isContact || telephoneNumber != config.PhoneRules.InvalidNumber || mobile != "")
|
||||
}
|
||||
|
||||
func isExcludedNumber(number string) bool {
|
||||
excludedNumbers := []string{
|
||||
"+49 5331 89",
|
||||
"+49533189",
|
||||
"+ 49 5331 89",
|
||||
}
|
||||
|
||||
// Zuerst prüfen wir auf die ausgeschlossenen Nummern
|
||||
for _, excluded := range excludedNumbers {
|
||||
if strings.Contains(number, excluded) {
|
||||
func isContactObject(entry *ldap.Entry) bool {
|
||||
objectClasses := entry.GetAttributeValues("objectClass")
|
||||
for _, class := range objectClasses {
|
||||
if strings.ToLower(class) == "contact" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Dann prüfen wir, ob die Nummer mit "089" endet
|
||||
if len(number) >= 3 && number[len(number)-3:] == "089" {
|
||||
return true
|
||||
}
|
||||
|
||||
// Wenn keine der obigen Bedingungen zutrifft, ist die Nummer nicht ausgeschlossen
|
||||
return false
|
||||
}
|
||||
|
||||
func
|
||||
func isValidContact(entry *ldap.Entry) bool {
|
||||
firstName := entry.GetAttributeValue("givenName")
|
||||
lastName := entry.GetAttributeValue("sn")
|
||||
telephoneNumber := entry.GetAttributeValue("telephoneNumber")
|
||||
|
||||
return firstName != "" && lastName != "" && telephoneNumber != config.PhoneRules.InvalidNumber
|
||||
}
|
||||
|
||||
func formatPhoneNumber(number string) string {
|
||||
// Entferne alle Nicht-Ziffern
|
||||
digits := strings.Map(func(r rune) rune {
|
||||
if unicode.IsDigit(r) {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, number)
|
||||
|
||||
// Entferne führende Nullen
|
||||
digits = strings.TrimLeft(digits, "0")
|
||||
|
||||
// Entferne den Länderprefix, falls vorhanden
|
||||
countryPrefix := config.PhoneRules.Country.Prefix
|
||||
if strings.HasPrefix(digits, countryPrefix) {
|
||||
digits = digits[len(countryPrefix):]
|
||||
}
|
||||
|
||||
// Füge eine führende "0" hinzu, wenn nicht vorhanden
|
||||
if !strings.HasPrefix(digits, "0") {
|
||||
digits = "0" + digits
|
||||
}
|
||||
|
||||
return digits
|
||||
}
|
||||
|
||||
func extractInternalNumber(phoneNumber string) string {
|
||||
digits := formatPhoneNumber(phoneNumber)
|
||||
|
||||
for _, areaCode := range config.PhoneRules.Country.AreaCodes {
|
||||
if strings.HasPrefix(digits[1:], areaCode) { // Ignoriere die führende 0
|
||||
remaining := digits[1+len(areaCode):]
|
||||
if strings.HasPrefix(remaining, config.PhoneRules.Country.InternalPrefix) {
|
||||
return remaining[len(config.PhoneRules.Country.InternalPrefix):]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func normalizePhoneNumber(number string) string {
|
||||
return formatPhoneNumber(number)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user