package api import ( "bytes" "encoding/base64" "encoding/json" "fmt" "io/ioutil" "math/rand" "net/http" "strings" "time" ) // 初始化随机数生成器 func init() { rand.Seed(time.Now().UnixNano()) } // Client 表示Stalwart邮件API客户端 type Client struct { Config *Config APIBaseURL string } // NewClient 创建新的API客户端 func NewClient(config *Config) *Client { client := &Client{ Config: config, APIBaseURL: config.API.BaseURL, } if config.API.DisableProxy { DisableProxy() } return client } // AuthType 认证类型 type AuthType string const ( // BasicAuth 表示基本认证 BasicAuth AuthType = "basic" // APIKeyAuth 表示API Key认证 APIKeyAuth AuthType = "apikey" ) // User 表示用户信息 type User struct { ID int `json:"id"` Type string `json:"type"` Name string `json:"name"` Description string `json:"description"` Quota int64 `json:"quota"` Emails []string `json:"emails"` MemberOf []string `json:"memberOf"` Roles []string `json:"roles"` Secrets []string `json:"secrets"` } // UserList 表示用户列表响应 type UserList struct { Data struct { Items []User `json:"items"` Total int `json:"total"` } `json:"data"` } // APIResponse 表示通用API响应 type APIResponse struct { Data json.RawMessage `json:"data"` Error string `json:"error"` Message string `json:"message"` Field string `json:"field"` Value string `json:"value"` } // GenerateRandomUsername 生成随机用户名 func GenerateRandomUsername(length int) string { // 常见的英文名 firstNames := []string{ "alex", "bob", "chris", "david", "eric", "frank", "gary", "henry", "ian", "jack", "kevin", "leo", "mike", "nick", "oliver", "peter", "ryan", "sam", "tom", "victor", "william", "zack", "anna", "betty", "cathy", "diana", "emma", "fiona", "grace", "helen", "irene", "jane", "kate", "lily", "mary", "nina", "olivia", "penny", "queen", "rose", "sarah", "tina", "uma", "vicky", "wendy", "zoe", } // 常见的姓氏 lastNames := []string{ "smith", "johnson", "williams", "jones", "brown", "davis", "miller", "wilson", "taylor", "clark", "hall", "lee", "allen", "young", "king", "wright", "hill", "scott", "green", "adams", "baker", "carter", "cook", } // 随机选择名和姓 firstName := firstNames[rand.Intn(len(firstNames))] lastName := lastNames[rand.Intn(len(lastNames))] // 添加1-999的随机数字 number := rand.Intn(999) + 1 // 组合成用户名 - 不使用特殊字符,直接连接 username := fmt.Sprintf("%s%s%d", firstName, lastName, number) // 如果指定了长度且用户名太长,就截断 if length > 0 && len(username) > length { username = username[:length] } return username } // GenerateRandomPassword 生成随机密码 func GenerateRandomPassword(length int) string { if length <= 0 { length = 10 // 默认10位密码 } // 简单词汇列表 words := []string{ "apple", "blue", "cat", "dog", "easy", "fish", "good", "home", "idea", "jump", "king", "love", "moon", "nice", "open", "park", "queen", "red", "star", "time", "user", "view", "work", "year", } // 随机选择一个词 word := words[rand.Intn(len(words))] // 确保词不超过长度限制的一半 if len(word) > length/2 { word = word[:length/2] } // 为剩余长度生成数字和特殊字符 remainingLength := length - len(word) numbers := "0123456789" specials := "@#$%&*!" var password strings.Builder password.WriteString(word) // 添加至少一个特殊字符 password.WriteByte(specials[rand.Intn(len(specials))]) remainingLength-- // 添加至少一个数字 password.WriteByte(numbers[rand.Intn(len(numbers))]) remainingLength-- // 填充剩余长度 for i := 0; i < remainingLength; i++ { // 使用大小写字母、数字组合 allChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" password.WriteByte(allChars[rand.Intn(len(allChars))]) } // 将密码字符打乱顺序 passwordBytes := []byte(password.String()) rand.Shuffle(len(passwordBytes), func(i, j int) { passwordBytes[i], passwordBytes[j] = passwordBytes[j], passwordBytes[i] }) return string(passwordBytes) } // ParseEmail 解析邮箱地址 func (c *Client) ParseEmail(email string) (string, string) { parts := strings.Split(email, "@") if len(parts) == 2 { return parts[0], parts[1] } return email, c.Config.User.DefaultDomain } // PrepareUserData 准备用户数据 func (c *Client) PrepareUserData(email, description string, password string, quota int64) map[string]interface{} { if password == "" { // 不再使用默认密码,而是生成随机密码 password = GenerateRandomPassword(12) } if quota == 0 { quota = c.Config.User.DefaultQuota } if description == "" { description = fmt.Sprintf("%s account", email) } // 准备用户数据 return map[string]interface{}{ "type": "individual", "name": email, "description": description, "quota": quota, "emails": []string{email}, "memberOf": []string{}, "roles": []string{"user"}, "secrets": []string{password}, } } // GetAuthHeaders 获取认证头 func (c *Client) GetAuthHeaders(authType AuthType, username, password, apiKey string) map[string]string { headers := map[string]string{ "Accept": "application/json", "Content-Type": "application/json", } if authType == APIKeyAuth { // API Key认证 if apiKey == "" { apiKey = c.Config.Auth.APIKey } fmt.Printf("使用的API Key: %s\n", apiKey) // 检查是否已经是完整的授权头 if strings.HasPrefix(apiKey, "Bearer ") { headers["Authorization"] = apiKey } else if strings.HasPrefix(apiKey, "api_") { // 使用标准格式 api_XXX headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey) fmt.Println("使用标准API Key格式: Bearer api_XXX") } else { // 可能是纯Token,尝试直接使用 headers["Authorization"] = fmt.Sprintf("Bearer %s", apiKey) fmt.Println("使用Token格式: Bearer XXX") } } else { // 基本认证 if username == "" { username = c.Config.Auth.Basic.AdminUsername } if password == "" { password = c.Config.Auth.Basic.AdminPassword } auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) headers["Authorization"] = fmt.Sprintf("Basic %s", auth) } fmt.Println("使用的认证头:", headers["Authorization"]) return headers } // MakeAPIRequest 发送API请求 func (c *Client) MakeAPIRequest(method, endpoint string, data interface{}, headers map[string]string) (*http.Response, []byte, error) { url := fmt.Sprintf("%s/%s", c.APIBaseURL, strings.TrimPrefix(endpoint, "/")) fmt.Printf("发送请求: %s %s\n", method, url) if headers["Authorization"] != "" { authType := strings.Split(headers["Authorization"], " ")[0] fmt.Printf("认证方式: %s\n", authType) } var reqBody []byte var err error if data != nil { reqBody, err = json.Marshal(data) if err != nil { return nil, nil, fmt.Errorf("序列化请求数据失败: %w", err) } fmt.Printf("请求数据: %s\n", string(reqBody)) } req, err := http.NewRequest(method, url, bytes.NewBuffer(reqBody)) if err != nil { return nil, nil, fmt.Errorf("创建HTTP请求失败: %w", err) } // 设置请求头 for key, value := range headers { req.Header.Set(key, value) } // 发送请求 client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, nil, fmt.Errorf("发送HTTP请求失败: %w", err) } // 读取响应 respBody, err := ioutil.ReadAll(resp.Body) defer resp.Body.Close() if err != nil { return resp, nil, fmt.Errorf("读取响应失败: %w", err) } fmt.Printf("响应状态码: %d\n", resp.StatusCode) fmt.Printf("响应内容: %s\n", string(respBody)) // 检查认证错误 if resp.StatusCode == 401 || resp.StatusCode == 403 { fmt.Println("认证失败,可能需要尝试其他认证方式") } return resp, respBody, nil } // CreateUser 创建邮箱用户 func (c *Client) CreateUser(email, password, description string, quota int64, authType AuthType, username, authPassword, apiKey string) (int, map[string]interface{}, error) { // 准备用户数据 userData := c.PrepareUserData(email, description, password, quota) fmt.Printf("创建用户 %s 并设置密码...\n", email) // 获取认证头 headers := c.GetAuthHeaders(authType, username, authPassword, apiKey) // 创建用户 resp, respBody, err := c.MakeAPIRequest("POST", "/principal", userData, headers) if err != nil { return 0, nil, fmt.Errorf("创建用户请求失败: %w", err) } if resp == nil { return 0, nil, fmt.Errorf("创建用户失败:未收到响应") } // 解析响应 var apiResp APIResponse if err := json.Unmarshal(respBody, &apiResp); err != nil { return 0, nil, fmt.Errorf("解析响应失败: %w", err) } // 检查是否返回错误 if apiResp.Error != "" { // 特殊处理:如果用户已存在,可以尝试获取已存在用户的ID if apiResp.Error == "fieldAlreadyExists" && apiResp.Field == "name" { existingName := apiResp.Value fmt.Printf("用户 %s 已存在,尝试获取现有用户信息...\n", existingName) // 构造一个模拟信息 userInfo := map[string]interface{}{ "name": email, "emails": []string{email}, "description": description, "quota": quota, "_note": "This user already exists", } // 返回一个特殊标记的ID(负数)表示这是一个已存在的用户 return -1, userInfo, nil } return 0, nil, fmt.Errorf("创建用户失败: %s - %s", apiResp.Error, apiResp.Message) } // 获取用户ID var userID int if err := json.Unmarshal(apiResp.Data, &userID); err != nil { // 尝试解析为对象 var objData map[string]interface{} if err := json.Unmarshal(apiResp.Data, &objData); err != nil { return 0, nil, fmt.Errorf("解析用户ID失败: %w", err) } // 检查是否有id字段 if id, ok := objData["id"].(float64); ok { userID = int(id) } else { return 0, nil, fmt.Errorf("未能从响应中获取用户ID") } } if userID == 0 { return 0, nil, fmt.Errorf("创建用户失败:未获取到用户ID") } fmt.Printf("用户创建成功!ID: %d\n", userID) // 尝试获取用户详情进行验证 userInfo, err := c.GetUserDetails(userID, authType, username, authPassword, apiKey) if err != nil || userInfo == nil { fmt.Println("获取用户详情失败,但用户创建成功") // 创建一个包含基本信息的对象 userInfo = map[string]interface{}{ "name": email, "emails": []string{email}, "description": description, "quota": quota, "_note": "This is a synthetic record as actual details could not be retrieved", } } return userID, userInfo, nil } // GetUserDetails 获取用户详情 func (c *Client) GetUserDetails(userID int, authType AuthType, username, authPassword, apiKey string) (map[string]interface{}, error) { fmt.Println("\n获取用户详情...") // 获取认证头 headers := c.GetAuthHeaders(authType, username, authPassword, apiKey) // 获取用户详情 endpoint := fmt.Sprintf("/principal/%d", userID) resp, respBody, err := c.MakeAPIRequest("GET", endpoint, nil, headers) if err != nil { return nil, fmt.Errorf("获取用户详情请求失败: %w", err) } if resp == nil || resp.StatusCode != 200 { return nil, fmt.Errorf("获取用户详情失败") } // 解析响应 var apiResp APIResponse if err := json.Unmarshal(respBody, &apiResp); err != nil { return nil, fmt.Errorf("解析响应失败: %w", err) } // 检查是否返回错误 if apiResp.Error != "" { return nil, fmt.Errorf("获取用户详情失败: %s - %s", apiResp.Error, apiResp.Message) } // 解析用户数据 var userData map[string]interface{} if err := json.Unmarshal(apiResp.Data, &userData); err != nil { // 如果无法解析为对象,直接返回整个响应 var rawResp map[string]interface{} if err := json.Unmarshal(respBody, &rawResp); err != nil { return nil, fmt.Errorf("解析用户数据失败: %w", err) } return rawResp, nil } if userData == nil { return nil, fmt.Errorf("获取用户详情失败:响应中没有用户数据") } // 打印关键信息 fmt.Printf("用户名: %v\n", userData["name"]) emails, _ := userData["emails"].([]interface{}) if len(emails) > 0 { fmt.Printf("邮箱: %v\n", emails[0]) } return userData, nil } // ListUsers 获取用户列表 func (c *Client) ListUsers(authType AuthType, username, authPassword, apiKey string, limit, page int) (*UserList, error) { fmt.Println("获取用户列表...") // 获取认证头 headers := c.GetAuthHeaders(authType, username, authPassword, apiKey) // 构建查询参数 endpoint := fmt.Sprintf("/principal?limit=%d&page=%d", limit, page) // 尝试不同的API端点路径 endpoints := []string{ endpoint, // 根据规范文档的主要端点 "/principals", // 原始尝试 "/users", // 通用users格式 "/user", // 单数形式 "/accounts", // 有些系统用accounts "/directory", // 目录形式 } var resp *http.Response var respBody []byte var err error var success bool for _, ep := range endpoints { fmt.Printf("尝试端点: %s\n", ep) resp, respBody, err = c.MakeAPIRequest("GET", ep, nil, headers) if err == nil && resp != nil && resp.StatusCode == 200 { fmt.Printf("成功的端点: %s\n", ep) success = true break } } if !success { return nil, fmt.Errorf("获取用户列表失败,尝试了所有可能的端点") } // 解析响应 fmt.Println("响应数据结构:") fmt.Println(string(respBody)) var userList UserList if err := json.Unmarshal(respBody, &userList); err != nil { // 尝试解析为不同格式 var rawResp map[string]interface{} if err := json.Unmarshal(respBody, &rawResp); err != nil { return nil, fmt.Errorf("解析用户列表失败: %w", err) } // 尝试不同的数据格式 users := []User{} // 检查data.items格式 if data, ok := rawResp["data"].(map[string]interface{}); ok { if items, ok := data["items"].([]interface{}); ok { for _, item := range items { if user, ok := item.(map[string]interface{}); ok { userObj := parseUserObject(user) users = append(users, userObj) } } userList.Data.Items = users userList.Data.Total = len(users) return &userList, nil } } // 检查data数组格式 if data, ok := rawResp["data"].([]interface{}); ok { for _, item := range data { if user, ok := item.(map[string]interface{}); ok { userObj := parseUserObject(user) users = append(users, userObj) } } userList.Data.Items = users userList.Data.Total = len(users) return &userList, nil } // 检查principals数组格式 if principals, ok := rawResp["principals"].([]interface{}); ok { for _, item := range principals { if user, ok := item.(map[string]interface{}); ok { userObj := parseUserObject(user) users = append(users, userObj) } } userList.Data.Items = users userList.Data.Total = len(users) return &userList, nil } // 检查users数组格式 if usersArray, ok := rawResp["users"].([]interface{}); ok { for _, item := range usersArray { if user, ok := item.(map[string]interface{}); ok { userObj := parseUserObject(user) users = append(users, userObj) } } userList.Data.Items = users userList.Data.Total = len(users) return &userList, nil } return nil, fmt.Errorf("未能从响应中提取用户列表") } fmt.Printf("找到 %d 个用户\n", len(userList.Data.Items)) return &userList, nil } // parseUserObject 解析用户对象 func parseUserObject(user map[string]interface{}) User { userObj := User{} // ID if id, ok := user["id"].(float64); ok { userObj.ID = int(id) } // Type if typ, ok := user["type"].(string); ok { userObj.Type = typ } // Name if name, ok := user["name"].(string); ok { userObj.Name = name } // Description if desc, ok := user["description"].(string); ok { userObj.Description = desc } // Quota if quota, ok := user["quota"].(float64); ok { userObj.Quota = int64(quota) } // Emails if emails, ok := user["emails"].([]interface{}); ok { for _, email := range emails { if e, ok := email.(string); ok { userObj.Emails = append(userObj.Emails, e) } } } else if email, ok := user["emails"].(string); ok { userObj.Emails = []string{email} } // MemberOf if memberOf, ok := user["memberOf"].([]interface{}); ok { for _, member := range memberOf { if m, ok := member.(string); ok { userObj.MemberOf = append(userObj.MemberOf, m) } } } // Roles if roles, ok := user["roles"].([]interface{}); ok { for _, role := range roles { if r, ok := role.(string); ok { userObj.Roles = append(userObj.Roles, r) } } } return userObj } // GetUserByEmail 通过邮箱地址查询用户信息 func (c *Client) GetUserByEmail(email string, authType AuthType, username, authPassword, apiKey string) (map[string]interface{}, error) { fmt.Printf("通过邮箱查询用户: %s\n", email) // 获取所有用户,然后过滤 userList, err := c.ListUsers(authType, username, authPassword, apiKey, 1000, 1) if err != nil { return nil, err } // 尝试在列表中查找匹配的邮箱 for _, user := range userList.Data.Items { // 检查用户邮箱是否匹配 for _, userEmail := range user.Emails { if userEmail == email { fmt.Printf("找到匹配的用户,ID: %d\n", user.ID) // 获取详细信息 return c.GetUserDetails(user.ID, authType, username, authPassword, apiKey) } } if user.Name == email { fmt.Printf("找到匹配的用户,ID: %d\n", user.ID) // 获取详细信息 return c.GetUserDetails(user.ID, authType, username, authPassword, apiKey) } } fmt.Printf("未找到邮箱为 %s 的用户\n", email) return nil, fmt.Errorf("未找到用户") } // FormatUserList 格式化输出用户列表 func FormatUserList(userList *UserList) { if userList == nil || len(userList.Data.Items) == 0 { fmt.Println("没有用户数据可显示") return } fmt.Println("\n=== 用户列表 ===") fmt.Printf("找到 %d 个用户\n", len(userList.Data.Items)) fmt.Println(strings.Repeat("-", 60)) fmt.Printf("%-10s %-30s %-15s %-30s\n", "ID", "名称", "类型", "邮箱") fmt.Println(strings.Repeat("-", 60)) for _, user := range userList.Data.Items { email := "N/A" if len(user.Emails) > 0 { email = user.Emails[0] } // 截断过长的字符串 name := user.Name if len(name) > 28 { name = name[:28] } if len(email) > 28 { email = email[:28] } fmt.Printf("%-10d %-30s %-15s %-30s\n", user.ID, name, user.Type, email) } fmt.Println(strings.Repeat("-", 60)) } // FormatUserDetails 格式化输出单个用户的详细信息 func FormatUserDetails(userInfo map[string]interface{}) { if userInfo == nil { fmt.Println("没有用户数据可显示") return } fmt.Println("\n=== 用户详细信息 ===") fmt.Println(strings.Repeat("-", 60)) // 获取基本信息 id, _ := userInfo["id"] name, _ := userInfo["name"] description, _ := userInfo["description"] typ, _ := userInfo["type"] quota, _ := userInfo["quota"] // 显示类型转换 var quotaFloat float64 switch q := quota.(type) { case float64: quotaFloat = q case int64: quotaFloat = float64(q) case int: quotaFloat = float64(q) } quotaMB := quotaFloat / (1024 * 1024) fmt.Printf("ID: %v\n", id) fmt.Printf("名称: %v\n", name) fmt.Printf("描述: %v\n", description) fmt.Printf("类型: %v\n", typ) fmt.Printf("配额: %.2f MB (%.0f 字节)\n", quotaMB, quotaFloat) // 获取邮箱 if emails, ok := userInfo["emails"].([]interface{}); ok && len(emails) > 0 { fmt.Println("\n邮箱地址:") for _, email := range emails { fmt.Printf(" - %v\n", email) } } else if email, ok := userInfo["emails"].(string); ok { fmt.Println("\n邮箱地址:") fmt.Printf(" - %s\n", email) } // 获取成员组 if memberOf, ok := userInfo["memberOf"].([]interface{}); ok && len(memberOf) > 0 { fmt.Println("\n所属组:") for _, group := range memberOf { fmt.Printf(" - %v\n", group) } } // 其他属性 fmt.Println("\n其他属性:") for key, value := range userInfo { if key != "id" && key != "name" && key != "description" && key != "type" && key != "quota" && key != "emails" && key != "memberOf" { // 跳过密码相关字段 if strings.Contains(strings.ToLower(key), "secret") || strings.Contains(strings.ToLower(key), "password") { continue } fmt.Printf(" - %s: %v\n", key, value) } } fmt.Println(strings.Repeat("-", 60)) } // CreateEmailUser 创建邮箱用户的统一入口 func (c *Client) CreateEmailUser(emailAddress, password, description string, quota int64, authType AuthType, username, authPassword, apiKey string) (int, string, string, string, error) { // 如果没有提供邮箱地址,则生成随机用户名 if emailAddress == "" { randomUsername := GenerateRandomUsername(8) // 确保用户名只包含字母和数字 for i := 0; i < len(randomUsername); i++ { if !((randomUsername[i] >= 'a' && randomUsername[i] <= 'z') || (randomUsername[i] >= '0' && randomUsername[i] <= '9')) { // 替换为字母 randomUsername = randomUsername[:i] + "x" + randomUsername[i+1:] } } emailAddress = fmt.Sprintf("%s@%s", randomUsername, c.Config.User.DefaultDomain) } // 解析邮箱地址 usernameLocal, domain := c.ParseEmail(emailAddress) // 确保用户名只包含字母和数字 cleanUsername := "" for i := 0; i < len(usernameLocal); i++ { if (usernameLocal[i] >= 'a' && usernameLocal[i] <= 'z') || (usernameLocal[i] >= '0' && usernameLocal[i] <= '9') { cleanUsername += string(usernameLocal[i]) } else { cleanUsername += "x" // 替换非法字符为x } } usernameLocal = cleanUsername email := fmt.Sprintf("%s@%s", usernameLocal, domain) // 如果没有提供密码,生成随机密码 if password == "" { password = GenerateRandomPassword(12) fmt.Println("已生成随机密码") } // 设置认证类型消息 if authType == APIKeyAuth { // 从API Key中提取用户名用于显示 if apiKey != "" && strings.HasPrefix(apiKey, "api_") { decoded, err := base64.StdEncoding.DecodeString(apiKey[4:]) if err == nil { decodedStr := string(decoded) if strings.Contains(decodedStr, ":") { parts := strings.SplitN(decodedStr, ":", 2) fmt.Printf("使用API Key认证 (用户: %s)\n", parts[0]) } else { fmt.Println("使用API Key认证") } } else { fmt.Println("使用API Key认证") } } else { fmt.Println("使用API Key认证") } } else { actualUsername := username if actualUsername == "" { actualUsername = c.Config.Auth.Basic.AdminUsername } fmt.Printf("使用基本认证 (用户: %s)\n", actualUsername) } // 创建用户 userID, _, err := c.CreateUser(email, password, description, quota, authType, username, authPassword, apiKey) if err != nil { return 0, "", "", "", err } // 特殊处理:如果用户已存在(用户ID为负数) if userID < 0 { fmt.Printf("用户 %s 已存在,使用现有用户信息\n", email) userID = -userID // 转为正数便于显示 } // 返回用户信息 return userID, usernameLocal, email, password, nil } // PrintUserResult 打印用户创建结果 func PrintUserResult(userID int, username, email, password string) bool { if userID == 0 { fmt.Println("\n创建用户失败") return false } domainParts := strings.Split(email, "@") domain := "" if len(domainParts) > 1 { domain = domainParts[1] } fmt.Println("\n=== 新创建的用户信息 ===") fmt.Printf("用户ID: %d\n", userID) fmt.Printf("登录名: %s\n", username) fmt.Printf("邮箱地址: %s\n", email) fmt.Printf("密码: %s\n", password) fmt.Printf("\n登录信息:\n") fmt.Printf(" - 登录名: %s\n", username) fmt.Printf(" - 密码: %s\n", password) fmt.Printf(" - 邮箱服务器: mail.%s\n", domain) return true }