From e32cb25074d6458e79ff1ee66a60acd068af2490 Mon Sep 17 00:00:00 2001 From: Robert Thomas <31854736+wolveix@users.noreply.github.com> Date: Wed, 29 May 2024 23:09:19 +0100 Subject: [PATCH] Refactored all HTTP logic (cleaned up debug logic, log body if smaller than 4096 bytes) Implemented support for Softaculous (for WordPress) Implemented support for PHP version retrieval and modification Implemented support for downloading files Implemented support for creating, retrieving, and restoring backups Implemented support for retrieving messages --- admin.go | 20 ++-- auth.go | 10 +- backup.go | 110 ++++++++++++++++++ database.go | 49 +++++--- directadmin.go | 11 -- dns.go | 8 +- domain.go | 18 +-- email.go | 34 +++++- email_forwarder.go | 11 +- file.go | 28 ++++- go.mod | 4 +- go.sum | 8 +- http.go | 256 ++++++++++++++++++----------------------- license.go | 2 +- messages.go | 28 +++++ package.go | 10 +- package_reseller.go | 10 +- php.go | 76 ++++++++++++ plugins_softaculous.go | 179 ++++++++++++++++++++++++++++ reseller.go | 12 +- session.go | 135 ++++++++++++++++++++++ ssl.go | 2 +- subdomain.go | 29 +---- system.go | 10 +- user.go | 7 +- wordpress.go | 17 +-- 26 files changed, 806 insertions(+), 278 deletions(-) create mode 100644 backup.go create mode 100644 messages.go create mode 100644 php.go create mode 100644 plugins_softaculous.go create mode 100644 session.go diff --git a/admin.go b/admin.go index f05f5a8..a94b1c1 100644 --- a/admin.go +++ b/admin.go @@ -17,7 +17,7 @@ type convertAccount struct { } func (c *AdminContext) ConvertResellerToUser(username string, reseller string) error { - if _, err := c.api.makeRequestN(http.MethodPost, "convert-reseller-to-user", c.credentials, convertAccount{Account: username, Creator: reseller}, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "convert-reseller-to-user", convertAccount{Account: username, Creator: reseller}, nil); err != nil { return err } @@ -25,7 +25,7 @@ func (c *AdminContext) ConvertResellerToUser(username string, reseller string) e } func (c *AdminContext) ConvertUserToReseller(username string) error { - if _, err := c.api.makeRequestN(http.MethodPost, "convert-user-to-reseller", c.credentials, convertAccount{Account: username}, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "convert-user-to-reseller", convertAccount{Account: username}, nil); err != nil { return err } @@ -35,7 +35,7 @@ func (c *AdminContext) ConvertUserToReseller(username string) error { func (c *AdminContext) DisableRedis() error { var response apiGenericResponseN - if _, err := c.api.makeRequestN(http.MethodPost, "redis/disable", c.credentials, nil, &response); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "redis/disable", nil, &response); err != nil { return err } @@ -45,7 +45,7 @@ func (c *AdminContext) DisableRedis() error { func (c *AdminContext) EnableRedis() error { var response apiGenericResponseN - if _, err := c.api.makeRequestN(http.MethodPost, "redis/enable", c.credentials, nil, &response); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "redis/enable", nil, &response); err != nil { return err } @@ -56,7 +56,7 @@ func (c *AdminContext) EnableRedis() error { func (c *AdminContext) GetAllUsers() ([]string, error) { var users []string - if _, err := c.api.makeRequest(http.MethodGet, "API_SHOW_ALL_USERS", c.credentials, nil, &users); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_SHOW_ALL_USERS", nil, &users); err != nil { return nil, err } @@ -67,7 +67,7 @@ func (c *AdminContext) GetAllUsers() ([]string, error) { func (c *AdminContext) GetResellers() ([]string, error) { var users []string - if _, err := c.api.makeRequest(http.MethodGet, "API_SHOW_RESELLERS", c.credentials, nil, &users); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_SHOW_RESELLERS", nil, &users); err != nil { return nil, err } @@ -78,7 +78,7 @@ func (c *AdminContext) GetResellers() ([]string, error) { func (c *AdminContext) GetResellersWithUsage() ([]string, error) { var users []string - if _, err := c.api.makeRequest(http.MethodGet, "RESELLER_SHOW", c.credentials, nil, &users); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "RESELLER_SHOW", nil, &users); err != nil { return nil, err } @@ -86,7 +86,7 @@ func (c *AdminContext) GetResellersWithUsage() ([]string, error) { } func (c *AdminContext) MoveUserToReseller(username string, reseller string) error { - if _, err := c.api.makeRequestN(http.MethodPost, "change-user-creator", c.credentials, convertAccount{Account: username, Creator: reseller}, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "change-user-creator", convertAccount{Account: username, Creator: reseller}, nil); err != nil { return err } @@ -96,7 +96,7 @@ func (c *AdminContext) MoveUserToReseller(username string, reseller string) erro func (c *AdminContext) RestartDirectAdmin() error { var response apiGenericResponseN - if _, err := c.api.makeRequestN(http.MethodPost, "restart", c.credentials, nil, &response); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "restart", nil, &response); err != nil { return err } @@ -106,7 +106,7 @@ func (c *AdminContext) RestartDirectAdmin() error { func (c *AdminContext) UpdateDirectAdmin() error { var response apiGenericResponseN - if _, err := c.api.makeRequestN(http.MethodPost, "version/update", c.credentials, nil, &response); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "version/update", nil, &response); err != nil { return err } diff --git a/auth.go b/auth.go index 315fead..b73d7c6 100644 --- a/auth.go +++ b/auth.go @@ -36,7 +36,7 @@ func (c *UserContext) CreateLoginURL(loginKeyURL *LoginKeyURL) error { return errors.New("failed to create login key URL: loginKeyURL is nil") } - if _, err := c.api.makeRequestN(http.MethodPost, "login-keys/urls", c.credentials, loginKeyURL, loginKeyURL); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "login-keys/urls", loginKeyURL, loginKeyURL); err != nil { return fmt.Errorf("failed to create login URL: %w", err) } @@ -46,7 +46,7 @@ func (c *UserContext) CreateLoginURL(loginKeyURL *LoginKeyURL) error { func (c *UserContext) GetLoginURLs() ([]*LoginKeyURL, error) { var loginKeyURLs []*LoginKeyURL - if _, err := c.api.makeRequestN(http.MethodGet, "login-keys/urls", c.credentials, nil, &loginKeyURLs); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "login-keys/urls", nil, &loginKeyURLs); err != nil { return nil, fmt.Errorf("failed to get login URLs: %w", err) } @@ -56,7 +56,7 @@ func (c *UserContext) GetLoginURLs() ([]*LoginKeyURL, error) { func (c *AdminContext) GetLoginHistory() ([]*LoginHistory, error) { var loginHistory []*LoginHistory - if _, err := c.api.makeRequestN(http.MethodGet, "login-history", c.credentials, nil, &loginHistory); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "login-history", nil, &loginHistory); err != nil { return nil, fmt.Errorf("failed to get login history: %w", err) } @@ -81,12 +81,12 @@ func (c *UserContext) GetMyUsername() string { func (c *UserContext) Login() error { var response apiGenericResponse - if _, err := c.api.makeRequest(http.MethodGet, "API_LOGIN_TEST", c.credentials, nil, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_LOGIN_TEST", nil, &response); err != nil { return err } if response.Success != "Login OK" { - return errors.New("login failed") + return fmt.Errorf("login failed: %v", response) } return nil diff --git a/backup.go b/backup.go new file mode 100644 index 0000000..42719f1 --- /dev/null +++ b/backup.go @@ -0,0 +1,110 @@ +package directadmin + +import ( + "fmt" + "net/http" + "net/url" +) + +// CreateBackup (user) creates an account backup for the given domain, and the given items +func (c *UserContext) CreateBackup(domain string, backupItems ...string) error { + var response apiGenericResponse + + body := url.Values{} + body.Set("action", "backup") + body.Set("domain", domain) + body.Set("form_version", "4") + + for index, backupItem := range backupItems { + body.Set(fmt.Sprintf("select%d", index), backupItem) + } + + if _, err := c.makeRequestOld(http.MethodPost, "SITE_BACKUP", body, &response); err != nil { + return err + } + + if response.Success != "Backup creation added to queue" { + return fmt.Errorf("failed to create backup: %v", response.Result) + } + + return nil +} + +// CreateBackupAllItems (user) wraps around CreateBackup and provides all available backup items +func (c *UserContext) CreateBackupAllItems(domain string) error { + return c.CreateBackup( + domain, + "domain", + "subdomain", + "email", + "email_data", + "emailsettings", + "forwarder", + "autoresponder", + "vacation", + "list", + "ftp", + "ftpsettings", + "database", + "database_data", + "trash", + ) +} + +// GetBackups (user) returns an array of the session user's backups for the given domain +func (c *UserContext) GetBackups(domain string) ([]string, error) { + var backups []string + + if _, err := c.makeRequestOld(http.MethodGet, "SITE_BACKUP?domain="+domain+"&ipp=50", nil, &backups); err != nil { + return nil, err + } + + return backups, nil +} + +// RestoreBackup (user) restores an account backup for the given domain, and the given items +func (c *UserContext) RestoreBackup(domain string, backupFilename string, backupItems ...string) error { + var response apiGenericResponse + + body := url.Values{} + body.Set("action", "restore") + body.Set("domain", domain) + body.Set("file", backupFilename) + body.Set("form_version", "3") + + for index, backupItem := range backupItems { + body.Set(fmt.Sprintf("select%d", index), backupItem) + } + + if _, err := c.makeRequestOld(http.MethodPost, "SITE_BACKUP", body, &response); err != nil { + return err + } + + if response.Success != "Restore will run in the background" { + return fmt.Errorf("failed to restore backup: %v", response.Result) + } + + return nil +} + +// RestoreBackupAllItems (user) wraps around RestoreBackup and provides all available backup items +func (c *UserContext) RestoreBackupAllItems(domain string, backupFilename string) error { + return c.RestoreBackup( + domain, + backupFilename, + "domain", + "subdomain", + "email", + "email_data", + "emailsettings", + "forwarder", + "autoresponder", + "vacation", + "list", + "ftp", + "ftpsettings", + "database", + "database_data", + "trash", + ) +} diff --git a/database.go b/database.go index 37caf59..a5f5c11 100644 --- a/database.go +++ b/database.go @@ -1,7 +1,9 @@ package directadmin import ( + "bytes" "fmt" + "mime/multipart" "net/http" "os" "strconv" @@ -57,7 +59,7 @@ type ( func (c *UserContext) CreateDatabase(database *Database) error { database.Name = c.addUsernamePrefix(database.Name) - if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/create-db", c.credentials, database, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "db-manage/create-db", database, nil); err != nil { return err } @@ -68,7 +70,7 @@ func (c *UserContext) CreateDatabaseWithUser(database *DatabaseWithUser) error { database.Name = c.addUsernamePrefix(database.Name) database.User = c.addUsernamePrefix(database.User) - if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/create-db-with-user", c.credentials, database, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "db-manage/create-db-with-user", database, nil); err != nil { return err } @@ -78,7 +80,7 @@ func (c *UserContext) CreateDatabaseWithUser(database *DatabaseWithUser) error { func (c *UserContext) CreateDatabaseUser(databaseUser *DatabaseUser) error { databaseUser.User = c.addUsernamePrefix(databaseUser.User) - if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/create-user", c.credentials, databaseUser, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "db-manage/create-user", databaseUser, nil); err != nil { return err } @@ -88,7 +90,7 @@ func (c *UserContext) CreateDatabaseUser(databaseUser *DatabaseUser) error { func (c *UserContext) DeleteDatabase(databaseName string) error { databaseName = c.addUsernamePrefix(databaseName) - if _, err := c.api.makeRequestN(http.MethodDelete, "db-manage/databases/"+databaseName, c.credentials, nil, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodDelete, "db-manage/databases/"+databaseName, nil, nil); err != nil { return err } @@ -96,8 +98,6 @@ func (c *UserContext) DeleteDatabase(databaseName string) error { } func (c *UserContext) DownloadDatabase(name string, format DatabaseFormat, filePath string) error { - var response apiGenericResponse - name = name + "." + string(format) if !strings.Contains(name, c.GetMyUsername()+"_") { @@ -119,10 +119,15 @@ func (c *UserContext) DownloadDatabase(name string, format DatabaseFormat, fileP } } - if _, err := c.api.makeRequest(http.MethodPost, "DB/"+name, c.credentials, nil, &response, file); err != nil { + resp, err := c.makeRequestOld(http.MethodPost, "DB/"+name, nil, nil) + if err != nil { return fmt.Errorf("failed to download database: %v", err) } + if _, err = file.Write(resp); err != nil { + return fmt.Errorf("error writing to file: %w", err) + } + return nil } @@ -130,7 +135,7 @@ func (c *UserContext) DownloadDatabase(name string, format DatabaseFormat, fileP func (c *UserContext) ExportDatabase(databaseName string, gzip bool) ([]byte, error) { databaseName = c.addUsernamePrefix(databaseName) - export, err := c.api.makeRequestN(http.MethodGet, "db-manage/databases/"+databaseName+"/export?gzip="+strconv.FormatBool(gzip), c.credentials, nil, nil) + export, err := c.makeRequestNew(http.MethodGet, "db-manage/databases/"+databaseName+"/export?gzip="+strconv.FormatBool(gzip), nil, nil) if err != nil { return nil, err } @@ -144,7 +149,7 @@ func (c *UserContext) GetDatabase(databaseName string) (*Database, error) { var database Database - if _, err := c.api.makeRequestN(http.MethodGet, "db-show/databases/"+databaseName, c.credentials, nil, &database); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "db-show/databases/"+databaseName, nil, &database); err != nil { return nil, err } @@ -155,7 +160,7 @@ func (c *UserContext) GetDatabase(databaseName string) (*Database, error) { func (c *UserContext) GetDatabases() ([]*Database, error) { var databases []*Database - if _, err := c.api.makeRequestN(http.MethodGet, "db-show/databases", c.credentials, nil, &databases); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "db-show/databases", nil, &databases); err != nil { return nil, err } @@ -166,7 +171,7 @@ func (c *UserContext) GetDatabases() ([]*Database, error) { func (c *UserContext) GetDatabaseProcesses() ([]*DatabaseProcess, error) { var databaseProcesses []*DatabaseProcess - if _, err := c.api.makeRequestN(http.MethodGet, "db-monitor/processes", c.credentials, nil, &databaseProcesses); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "db-monitor/processes", nil, &databaseProcesses); err != nil { return nil, err } @@ -177,7 +182,23 @@ func (c *UserContext) GetDatabaseProcesses() ([]*DatabaseProcess, error) { func (c *UserContext) ImportDatabase(databaseName string, emptyExistingDatabase bool, sql []byte) error { databaseName = c.addUsernamePrefix(databaseName) - if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/databases/"+databaseName+"/import?clean="+strconv.FormatBool(emptyExistingDatabase), c.credentials, sql, nil, true); err != nil { + var byteBuffer bytes.Buffer + multipartWriter := multipart.NewWriter(&byteBuffer) + + formFile, err := multipartWriter.CreateFormFile("sqlfile", "filename") + if err != nil { + return fmt.Errorf("failed to create form file: %w", err) + } + + if _, err = formFile.Write(sql); err != nil { + return fmt.Errorf("failed to write to form file: %w", err) + } + + if err = multipartWriter.Close(); err != nil { + return fmt.Errorf("failed to close multipart writer: %w", err) + } + + if _, err = c.uploadFile(http.MethodPost, "/api/db-manage/databases/"+databaseName+"/import?clean="+strconv.FormatBool(emptyExistingDatabase), byteBuffer.Bytes(), nil, multipartWriter.FormDataContentType()); err != nil { return err } @@ -188,7 +209,7 @@ func (c *UserContext) ImportDatabase(databaseName string, emptyExistingDatabase func (c *UserContext) UpdateDatabaseUserHosts(username string, hosts []string) error { username = c.addUsernamePrefix(username) - if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/users/"+username+"/change-hosts", c.credentials, hosts, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "db-manage/users/"+username+"/change-hosts", hosts, nil); err != nil { return err } @@ -205,7 +226,7 @@ func (c *UserContext) UpdateDatabaseUserPassword(username string, password strin password, } - if _, err := c.api.makeRequestN(http.MethodPost, "db-manage/users/"+username+"/change-password", c.credentials, newPassword, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "db-manage/users/"+username+"/change-password", newPassword, nil); err != nil { return err } diff --git a/directadmin.go b/directadmin.go index 12bc029..2c5ab71 100644 --- a/directadmin.go +++ b/directadmin.go @@ -2,7 +2,6 @@ package directadmin import ( "errors" - "fmt" "net/http" "net/url" "sync" @@ -29,7 +28,6 @@ type API struct { cacheEnabled bool debug bool httpClient *http.Client - ssl bool timeout time.Duration url string } @@ -74,15 +72,6 @@ func New(serverUrl string, timeout time.Duration, cacheEnabled bool, debug bool) api.cache.users = make(map[string]User) } - switch parsedUrl.Scheme { - case "http": - api.ssl = false - case "https": - api.ssl = true - default: - return nil, fmt.Errorf("invalid url scheme: %v", parsedUrl.Scheme) - } - api.httpClient = &http.Client{Timeout: timeout} return &api, nil diff --git a/dns.go b/dns.go index 4f79c07..fa5c336 100644 --- a/dns.go +++ b/dns.go @@ -51,7 +51,7 @@ func (c *UserContext) CreateDNSRecord(domain string, dnsRecord DNSRecord) error "value": {rawDNSRecordData.Value}, } - if _, err := c.api.makeRequest(http.MethodPost, "API_DNS_CONTROL?action=add&action_pointers=yes", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_DNS_CONTROL?action=add&action_pointers=yes", body, &response); err != nil { return err } @@ -79,7 +79,7 @@ func (c *UserContext) DeleteDNSRecords(dnsRecords ...DNSRecord) error { } } - if _, err := c.api.makeRequest(http.MethodPost, "API_DNS_CONTROL?action=select&delete=yes", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_DNS_CONTROL?action=select&delete=yes", body, &response); err != nil { return err } @@ -97,7 +97,7 @@ func (c *UserContext) GetDNSRecords(domain string) ([]DNSRecord, error) { DNSRecords []rawDNSRecord `json:"records"` }{} - if _, err := c.api.makeRequest(http.MethodGet, "API_DNS_CONTROL?domain="+domain, c.credentials, nil, &rawDNSRecords); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_DNS_CONTROL?domain="+domain, nil, &rawDNSRecords); err != nil { return nil, err } @@ -127,7 +127,7 @@ func (c *UserContext) UpdateDNSRecord(domain string, originalDNSRecord DNSRecord strings.ToLower(originalDNSRecord.Type) + "recs0": {fmt.Sprintf("name=%v&value=%v", originalDNSRecord.Name, originalDNSRecord.Value)}, } - if _, err := c.api.makeRequest(http.MethodPost, "API_DNS_CONTROL?action=edit&action_pointers=yes", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_DNS_CONTROL?action=edit&action_pointers=yes", body, &response); err != nil { return err } diff --git a/domain.go b/domain.go index c5f2a59..5d06971 100644 --- a/domain.go +++ b/domain.go @@ -56,7 +56,7 @@ func (c *UserContext) CreateDomain(domain Domain) error { body.Set("php", rawDomainData.PhpEnabled) body.Set("ssl", rawDomainData.SslEnabled) - if _, err := c.api.makeRequest(http.MethodPost, "API_DOMAIN?action=create", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_DOMAIN?action=create", body, &response); err != nil { return err } @@ -106,7 +106,7 @@ func (c *UserContext) DeleteDomains(deleteData bool, domains ...string) error { body.Set("select"+cast.ToString(index), domain) } - if _, err := c.api.makeRequest(http.MethodPost, "API_DOMAIN?action=select", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_DOMAIN?action=select", body, &response); err != nil { return err } @@ -137,7 +137,7 @@ func (c *UserContext) GetDomain(domainName string) (Domain, error) { var rawDomains map[string]rawDomain - if _, err := c.api.makeRequest(http.MethodGet, "API_ADDITIONAL_DOMAINS?bytes=yes&domain="+domainName, c.credentials, nil, &rawDomains); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_ADDITIONAL_DOMAINS?bytes=yes&domain="+domainName, nil, &rawDomains); err != nil { return Domain{}, err } @@ -154,7 +154,7 @@ func (c *UserContext) GetDomain(domainName string) (Domain, error) { rawDomainData.Subdomains = []string{} } - if _, err := c.api.makeRequest(http.MethodGet, "API_ADDITIONAL_DOMAINS?bytes=yes&action=view&domain="+domainName, c.credentials, nil, &rawDomainData.ExtraData); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_ADDITIONAL_DOMAINS?bytes=yes&action=view&domain="+domainName, nil, &rawDomainData.ExtraData); err != nil { return Domain{}, err } @@ -166,7 +166,7 @@ func (c *UserContext) GetDomains() ([]Domain, error) { var domains []Domain var rawDomains map[string]rawDomain - if _, err := c.api.makeRequest(http.MethodGet, "API_ADDITIONAL_DOMAINS?bytes=yes", c.credentials, nil, &rawDomains); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_ADDITIONAL_DOMAINS?bytes=yes", nil, &rawDomains); err != nil { return nil, fmt.Errorf("failed to get domains: %v", err) } @@ -201,7 +201,7 @@ func (c *UserContext) GetDomains() ([]Domain, error) { go func(rawDomainData rawDomain) { defer wg.Done() - if _, err := c.api.makeRequest(http.MethodGet, "API_ADDITIONAL_DOMAINS?action=view&bytes=yes&domain="+rawDomainData.Domain, c.credentials, nil, &rawDomainData.ExtraData); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_ADDITIONAL_DOMAINS?action=view&bytes=yes&domain="+rawDomainData.Domain, nil, &rawDomainData.ExtraData); err != nil { mu.Lock() errs = append(errs, err) mu.Unlock() @@ -260,7 +260,7 @@ func (c *UserContext) GetDomains() ([]Domain, error) { // ListDomains (user) returns an array of all domains for the session user func (c *UserContext) ListDomains() (domainList []string, err error) { - if _, err = c.api.makeRequest(http.MethodGet, "API_SHOW_DOMAINS?bytes=yes", c.credentials, nil, &domainList); err != nil { + if _, err = c.makeRequestOld(http.MethodGet, "API_SHOW_DOMAINS?bytes=yes", nil, &domainList); err != nil { return nil, err } @@ -275,7 +275,7 @@ func (c *UserContext) SetDefaultDomain(domain string) error { body.Set("select0", domain) body.Set("default", "yes") - if _, err := c.api.makeRequest(http.MethodPost, "API_DOMAIN?action=select", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_DOMAIN?action=select", body, &response); err != nil { return err } @@ -300,7 +300,7 @@ func (c *UserContext) UpdateDomain(domain Domain) error { body.Set("php", rawDomainData.PhpEnabled) body.Set("ssl", rawDomainData.SslEnabled) - if _, err := c.api.makeRequest(http.MethodPost, "API_DOMAIN?action=modify", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_DOMAIN?action=modify", body, &response); err != nil { return err } diff --git a/email.go b/email.go index c9838da..fa9e9b5 100644 --- a/email.go +++ b/email.go @@ -31,7 +31,7 @@ func (c *UserContext) CreateEmailAccount(emailAccount EmailAccount) error { body.Set("quota", cast.ToString(emailAccount.DiskQuota)) body.Set("limit", cast.ToString(emailAccount.SendQuota)) - if _, err := c.api.makeRequest(http.MethodPost, "API_POP?action=create", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_POP?action=create", body, &response); err != nil { return err } @@ -49,7 +49,7 @@ func (c *UserContext) DeleteEmailAccount(domain string, name string) error { body.Set("domain", domain) body.Set("user", name) - if _, err := c.api.makeRequest(http.MethodPost, "API_POP?action=delete", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_POP?action=delete", body, &response); err != nil { return err } @@ -75,7 +75,7 @@ func (c *UserContext) GetEmailAccounts(domain string) ([]EmailAccount, error) { } `json:"emails"` }{} - if _, err := c.api.makeRequest(http.MethodGet, "EMAIL_POP?bytes=yes&domain="+domain, c.credentials, nil, &rawEmailAccounts); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "EMAIL_POP?bytes=yes&domain="+domain, nil, &rawEmailAccounts); err != nil { return nil, err } @@ -108,6 +108,30 @@ func (c *UserContext) GetEmailAccounts(domain string) ([]EmailAccount, error) { return emailAccounts, nil } +func (c *UserContext) ToggleDKIM(domain string, status bool) error { + var response apiGenericResponse + + body := url.Values{} + body.Set("action", "set_dkim") + body.Set("domain", domain) + + if status { + body.Set("enable", "yes") + } else { + body.Set("disable", "yes") + } + + if _, err := c.makeRequestOld(http.MethodPost, "API_EMAIL_POP", body, &response); err != nil { + return err + } + + if response.Success != "Success" { + return fmt.Errorf("failed to toggle DKIM state: %v", response.Result) + } + + return nil +} + func (c *UserContext) UpdateEmailAccount(emailAccount EmailAccount) error { var response apiGenericResponse @@ -119,7 +143,7 @@ func (c *UserContext) UpdateEmailAccount(emailAccount EmailAccount) error { body.Set("quota", cast.ToString(emailAccount.DiskQuota)) body.Set("limit", cast.ToString(emailAccount.SendQuota)) - if _, err := c.api.makeRequest(http.MethodPost, "API_POP?action=modify", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_POP?action=modify", body, &response); err != nil { return err } @@ -138,7 +162,7 @@ func (c *UserContext) VerifyEmailAccount(address string, password string) error body.Set("email", address) body.Set("passwd", password) - if _, err := c.api.makeRequest(http.MethodPost, "API_EMAIL_AUTH", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_EMAIL_AUTH", body, &response); err != nil { return err } diff --git a/email_forwarder.go b/email_forwarder.go index 441595a..4068ad0 100644 --- a/email_forwarder.go +++ b/email_forwarder.go @@ -2,10 +2,11 @@ package directadmin import ( "fmt" - "github.com/spf13/cast" "net/http" "net/url" "strings" + + "github.com/spf13/cast" ) func (c *UserContext) CreateEmailForwarder(domain string, user string, emails ...string) error { @@ -16,7 +17,7 @@ func (c *UserContext) CreateEmailForwarder(domain string, user string, emails .. body.Set("email", strings.Join(emails, ",")) body.Set("user", user) - if _, err := c.api.makeRequest(http.MethodPost, "API_EMAIL_FORWARDERS?action=create", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_EMAIL_FORWARDERS?action=create", body, &response); err != nil { return err } @@ -31,7 +32,7 @@ func (c *UserContext) CreateEmailForwarder(domain string, user string, emails .. func (c *UserContext) GetEmailForwarders(domain string) (map[string][]string, error) { var emailForwarders = make(map[string][]string) - if _, err := c.api.makeRequest(http.MethodGet, "API_EMAIL_FORWARDERS?domain="+domain, c.credentials, nil, &emailForwarders); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_EMAIL_FORWARDERS?domain="+domain, nil, &emailForwarders); err != nil { return nil, err } @@ -48,7 +49,7 @@ func (c *UserContext) DeleteEmailForwarders(domain string, names ...string) erro body.Set("select"+cast.ToString(index), name) } - if _, err := c.api.makeRequest(http.MethodPost, "API_EMAIL_FORWARDERS?action=delete", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_EMAIL_FORWARDERS?action=delete", body, &response); err != nil { return err } @@ -67,7 +68,7 @@ func (c *UserContext) UpdateEmailForwarder(domain string, user string, emails .. body.Set("email", strings.Join(emails, ",")) body.Set("user", user) - if _, err := c.api.makeRequest(http.MethodPost, "API_EMAIL_FORWARDERS?action=modify", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_EMAIL_FORWARDERS?action=modify", body, &response); err != nil { return err } diff --git a/file.go b/file.go index 3b13512..d11bd11 100644 --- a/file.go +++ b/file.go @@ -17,7 +17,7 @@ import ( func (c *UserContext) CheckFileExists(filePath string) error { var response apiGenericResponse - if _, err := c.api.makeRequest(http.MethodGet, "FILE_MANAGER?action=exists&path="+filePath, c.credentials, nil, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "FILE_MANAGER?action=exists&path="+filePath, nil, &response); err != nil { return err } @@ -71,7 +71,7 @@ func (c *UserContext) CreateFiles(uploadToPath string, filePaths ...string) erro uploadToPath = "/" + uploadToPath } - if _, err := c.api.uploadFile("FILE_MANAGER?action=upload&path="+uploadToPath, c.credentials, body, &response, writer.FormDataContentType()); err != nil { + if _, err := c.uploadFile(http.MethodPost, "/CMD_FILE_MANAGER?action=upload&path="+uploadToPath, body.Bytes(), &response, writer.FormDataContentType()); err != nil { return err } @@ -104,7 +104,7 @@ func (c *UserContext) DeleteFiles(skipTrash bool, files ...string) error { body.Set("select"+cast.ToString(index), file) } - if _, err := c.api.makeRequest(http.MethodPost, "FILE_MANAGER?action=multiple", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "FILE_MANAGER?action=multiple", body, &response); err != nil { return err } @@ -115,6 +115,26 @@ func (c *UserContext) DeleteFiles(skipTrash bool, files ...string) error { return nil } +// DownloadFile (user) downloads the given file path from the server +func (c *UserContext) DownloadFile(filePath string) ([]byte, error) { + return c.makeRequestNew(http.MethodGet, "filemanager/download?path="+filePath, nil, nil) +} + +// DownloadFileToDisk (user) wraps DownloadFile and writes the output to the given path +func (c *UserContext) DownloadFileToDisk(filePath string, outputPath string) error { + if outputPath == "" { + return fmt.Errorf("no file path provided") + } + + response, err := c.makeRequestNew(http.MethodGet, "filemanager/download?path="+filePath, nil, nil) + if err != nil { + return err + } + + return os.WriteFile(outputPath, response, 0644) +} + +// ExtractFile unzips the given file path on the server func (c *UserContext) ExtractFile(filePath string, file string) error { var response apiGenericResponse @@ -133,7 +153,7 @@ func (c *UserContext) ExtractFile(filePath string, file string) error { body.Set("path", file) body.Set("page", "2") - if _, err := c.api.makeRequest(http.MethodPost, "FILE_MANAGER?action=extract", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "FILE_MANAGER?action=extract", body, &response); err != nil { return err } diff --git a/go.mod b/go.mod index 980cbd9..310a375 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/levelzerotechnology/directadmin-go go 1.22 require ( - github.com/goccy/go-json v0.10.2 + github.com/goccy/go-json v0.10.3 github.com/google/go-querystring v1.1.0 github.com/spf13/cast v1.6.0 - golang.org/x/text v0.14.0 + golang.org/x/text v0.15.0 ) diff --git a/go.sum b/go.sum index 0690cc2..e907265 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -15,6 +15,6 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/http.go b/http.go index 5e326c6..1109540 100644 --- a/http.go +++ b/http.go @@ -5,164 +5,114 @@ import ( "errors" "fmt" "io" - "mime/multipart" "net/http" "net/url" - "os" "strings" "time" "github.com/goccy/go-json" ) -func (a *API) makeRequest(method string, endpoint string, accountCredentials credentials, body url.Values, object any, writeToFile ...*os.File) ([]byte, error) { - defer a.queryTime(endpoint, time.Now()) - - var responseBytes []byte +type httpDebug struct { + Body string + Code int + Endpoint string + Start time.Time +} - req, err := http.NewRequest(strings.ToUpper(method), a.url+"/CMD_"+endpoint, strings.NewReader(body.Encode())) - if err != nil { - return nil, fmt.Errorf("error creating request: %w", err) +// makeRequest is the underlying function for HTTP requests. It handles debugging statements, and simple error handling +func (c *UserContext) makeRequest(req *http.Request) ([]byte, error) { + debug := httpDebug{ + Endpoint: req.URL.Path, + Start: time.Now(), } + defer c.api.printDebugHTTP(&debug) - query := req.URL.Query() - query.Add("json", "yes") - - req.Header.Set("Content-Type", "application/json") - req.SetBasicAuth(accountCredentials.username, accountCredentials.passkey) - req.URL.RawQuery = query.Encode() - - resp, err := a.httpClient.Do(req) + resp, err := c.api.httpClient.Do(req) if err != nil { return nil, err } - defer resp.Body.Close() - if a.debug { - fmt.Printf("HTTP STATUS: %v\n", resp.Status) + if c.api.debug { + debug.Code = resp.StatusCode } - if resp.Body != nil { - defer resp.Body.Close() + // exists solely for user session switching when logging in as a user under a reseller + for _, cookie := range resp.Cookies() { + if cookie.Name == "session" { + c.sessionID = cookie.Value + break + } + } + var responseBytes []byte + + if resp.Body != nil { responseBytes, err = io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("error reading response body: %w", err) } - if a.debug { - print(string(responseBytes)) + if c.api.debug { + if len(responseBytes) > 4096 { + debug.Body = "body too long for debug" + } else { + debug.Body = string(responseBytes) + } } } - // unmarshal object into generic response object to check if there's an error - var genericResponse apiGenericResponse - - if err = json.Unmarshal(responseBytes, &genericResponse); err == nil { - if genericResponse.Error != "" { - return nil, errors.New(genericResponse.Error + ": " + genericResponse.Result) - } - } - - if len(responseBytes) > 0 { - if len(writeToFile) > 0 { - if _, err = writeToFile[0].Write(responseBytes); err != nil { - return nil, fmt.Errorf("error writing to file: %w", err) - } - } else if object != nil { - if err = json.Unmarshal(responseBytes, &object); err != nil { - return nil, fmt.Errorf("error unmarshaling response: %w", err) - } - } + if resp.StatusCode/100 != 2 { + return responseBytes, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } return responseBytes, nil } -// makeRequestN supports DA's new API -func (a *API) makeRequestN(method string, endpoint string, accountCredentials credentials, body any, object any, uploadFile ...bool) ([]byte, error) { - defer a.queryTime(endpoint, time.Now()) - - var err error - var requestBytes, responseBytes []byte - - contentType := "application/json" +// makeRequestNew supports DirectAdmin's new API +func (c *UserContext) makeRequestNew(method string, endpoint string, body any, object any) ([]byte, error) { + var bodyBytes []byte if body != nil { - if len(uploadFile) != 1 { - requestBytes, err = json.Marshal(body) - if err != nil { - return nil, fmt.Errorf("error marshalling body: %w", err) - } - } else { - var byteBuffer bytes.Buffer - multipartWriter := multipart.NewWriter(&byteBuffer) + var err error - formFile, err := multipartWriter.CreateFormFile("sqlfile", "filename") - if err != nil { - return nil, err - } - - if _, err = formFile.Write(body.([]byte)); err != nil { - return nil, err - } - - if err = multipartWriter.Close(); err != nil { - return nil, err - } - - requestBytes = byteBuffer.Bytes() - contentType = multipartWriter.FormDataContentType() + bodyBytes, err = json.Marshal(body) + if err != nil { + return nil, fmt.Errorf("error serializing body: %w", err) } } - req, err := http.NewRequest(strings.ToUpper(method), a.url+"/api/"+endpoint, bytes.NewBuffer(requestBytes)) + req, err := http.NewRequest(strings.ToUpper(method), c.api.url+"/api/"+endpoint, bytes.NewBuffer(bodyBytes)) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } + query := req.URL.Query() req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", contentType) - req.SetBasicAuth(accountCredentials.username, accountCredentials.passkey) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Referer", c.api.url) + req.URL.RawQuery = query.Encode() - resp, err := a.httpClient.Do(req) - if err != nil { - return nil, err + if c.sessionID != "" { + req.AddCookie(&http.Cookie{Name: "session", Value: c.sessionID}) + } else { + req.SetBasicAuth(c.credentials.username, c.credentials.passkey) } - defer resp.Body.Close() - - if a.debug { - fmt.Printf("HTTP STATUS: %v\n", resp.Status) + resp, err := c.makeRequest(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) } - if resp.Body != nil { - defer resp.Body.Close() - - responseBytes, err = io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error reading response body: %w", err) - } + var genericResponse apiGenericResponseN - if a.debug { - print(string(responseBytes)) - } - } - - // ignore unmarshal error, as DA's responses are inconsistent across endpoints - if resp.StatusCode/100 == 2 { - // unmarshal to object if one is provided - if object != nil && len(responseBytes) > 0 { - if err = json.Unmarshal(responseBytes, &object); err != nil { - return nil, fmt.Errorf("error unmarshaling response: %w", err) + if resp != nil { + if object != nil { + if err = json.Unmarshal(resp, &object); err != nil { + return nil, fmt.Errorf("error unmarshalling response: %w", err) } - } - } else { - // unmarshal object into generic response object to check if there's an error - var genericResponse apiGenericResponseN - - if err = json.Unmarshal(responseBytes, &genericResponse); err == nil { + } else if err = json.Unmarshal(resp, &genericResponse); err == nil { if genericResponse.Message != "" { return nil, errors.New(genericResponse.Type + ": " + genericResponse.Message) } @@ -170,70 +120,84 @@ func (a *API) makeRequestN(method string, endpoint string, accountCredentials cr } } - return responseBytes, nil + return resp, nil } -func (a *API) uploadFile(endpoint string, accountCredentials credentials, body *bytes.Buffer, object any, contentType string) ([]byte, error) { - defer a.queryTime(endpoint, time.Now()) - - var responseBytes []byte - - req, err := http.NewRequest(http.MethodPost, a.url+"/CMD_"+endpoint, body) +// makeRequestOld supports DirectAdmin's old API +func (c *UserContext) makeRequestOld(method string, endpoint string, body url.Values, object any) ([]byte, error) { + req, err := http.NewRequest(strings.ToUpper(method), c.api.url+"/CMD_"+endpoint, strings.NewReader(body.Encode())) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } query := req.URL.Query() query.Add("json", "yes") - - req.Header.Set("Content-Type", contentType) - req.SetBasicAuth(accountCredentials.username, accountCredentials.passkey) + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Referer", c.api.url) req.URL.RawQuery = query.Encode() - resp, err := a.httpClient.Do(req) - if err != nil { - return nil, err + if c.sessionID != "" { + req.AddCookie(&http.Cookie{Name: "session", Value: c.sessionID}) + } else { + req.SetBasicAuth(c.credentials.username, c.credentials.passkey) } - defer resp.Body.Close() + var genericResponse apiGenericResponse - if a.debug { - fmt.Printf("HTTP STATUS: %v\n", resp.Status) - } + resp, err := c.makeRequest(req) + if err != nil { + jsonErr := json.Unmarshal(resp, &genericResponse) + if jsonErr != nil { + return nil, fmt.Errorf("error making request: %w", err) + } - if resp.Body != nil { - defer resp.Body.Close() + return nil, errors.New(genericResponse.Error + ": " + genericResponse.Result) + } - responseBytes, err = io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("error reading response body: %w", err) + if resp != nil { + if object != nil { + if err = json.Unmarshal(resp, &object); err != nil { + return nil, fmt.Errorf("error unmarshalling response: %w", err) + } + } else if err = json.Unmarshal(resp, &genericResponse); err == nil && genericResponse.Error != "" { + return nil, errors.New(genericResponse.Error + ": " + genericResponse.Result) } + } - if a.debug { - print(string(responseBytes)) - } + return resp, nil +} + +// uploadFile functions for either the old or new DA API +func (c *UserContext) uploadFile(method string, endpoint string, data []byte, object any, contentType string) ([]byte, error) { + req, err := http.NewRequest(strings.ToUpper(method), c.api.url+endpoint, bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) } - // unmarshal to object if one is provided - if object != nil && len(responseBytes) > 0 { - if err = json.Unmarshal(responseBytes, &object); err != nil { - return nil, fmt.Errorf("error unmarshaling response: %w", err) - } + query := req.URL.Query() + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", contentType) + req.Header.Set("Referer", c.api.url) + req.SetBasicAuth(c.credentials.username, c.credentials.passkey) + req.URL.RawQuery = query.Encode() + + resp, err := c.makeRequest(req) + if err != nil { + return nil, fmt.Errorf("error making request: %w", err) } - // unmarshal object into generic response object to check if there's an error - var genericResponse apiGenericResponse - if err = json.Unmarshal(responseBytes, &genericResponse); err == nil { - if genericResponse.Error != "" { - return nil, errors.New(genericResponse.Error + ": " + genericResponse.Result) + if resp != nil && object != nil { + if err = json.Unmarshal(resp, &object); err != nil { + return nil, fmt.Errorf("error unmarshalling response: %w", err) } } - return responseBytes, nil + return resp, nil } -func (a *API) queryTime(endpoint string, startTime time.Time) { +func (a *API) printDebugHTTP(debug *httpDebug) { if a.debug { - fmt.Printf("ENDPOINT: %v\nTIME STARTED: %v\nTIME ENDED: %v\nTIME TAKEN: %v\n\n", endpoint, startTime, time.Now(), time.Since(startTime)) + fmt.Printf("\nENDPOINT: %v\nSTATUS CODE: %v\nTIME STARTED: %v\nTIME ENDED: %v\nTIME TAKEN: %v\nBODY: %v\n", debug.Endpoint, debug.Code, debug.Start, time.Now(), time.Since(debug.Start), debug.Body) } } diff --git a/license.go b/license.go index cfa0e5d..29c91ff 100644 --- a/license.go +++ b/license.go @@ -32,7 +32,7 @@ type License struct { func (c *AdminContext) GetLicense() (*License, error) { var license License - if _, err := c.api.makeRequestN(http.MethodGet, "license", c.credentials, nil, &license); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "license", nil, &license); err != nil { return nil, fmt.Errorf("failed to get license: %v", err) } diff --git a/messages.go b/messages.go new file mode 100644 index 0000000..fb56489 --- /dev/null +++ b/messages.go @@ -0,0 +1,28 @@ +package directadmin + +import ( + "net/http" + "time" +) + +type Message struct { + From string `json:"from"` + FromName string `json:"fromName"` + Id int `json:"id"` + LegacyID string `json:"legacyID"` + Message string `json:"message"` + Subject string `json:"subject"` + Timestamp time.Time `json:"timestamp"` + Unread bool `json:"unread"` +} + +// GetMessages (user) returns an array of the session user's backups +func (c *UserContext) GetMessages() ([]*Message, error) { + var messages []*Message + + if _, err := c.makeRequestNew(http.MethodGet, "messages", nil, &messages); err != nil { + return nil, err + } + + return messages, nil +} diff --git a/package.go b/package.go index 8713019..e8122e7 100644 --- a/package.go +++ b/package.go @@ -64,7 +64,7 @@ func (c *ResellerContext) CreatePackage(pack Package) error { return err } - if _, err = c.api.makeRequest(http.MethodPost, "MANAGE_USER_PACKAGES?add=yes", c.credentials, body, &response); err != nil { + if _, err = c.makeRequestOld(http.MethodPost, "MANAGE_USER_PACKAGES?add=yes", body, &response); err != nil { return err } @@ -87,7 +87,7 @@ func (c *ResellerContext) DeletePackages(packs ...string) error { body.Set("select"+cast.ToString(index), pack) } - if _, err := c.api.makeRequest(http.MethodPost, "MANAGE_USER_PACKAGES", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "MANAGE_USER_PACKAGES", body, &response); err != nil { return err } @@ -102,7 +102,7 @@ func (c *ResellerContext) DeletePackages(packs ...string) error { func (c *ResellerContext) GetPackage(packageName string) (Package, error) { var rawPack rawPackage - if _, err := c.api.makeRequest(http.MethodGet, "API_PACKAGES_USER?package="+packageName, c.credentials, nil, &rawPack); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_PACKAGES_USER?package="+packageName, nil, &rawPack); err != nil { return Package{}, fmt.Errorf("failed to get package info for %v: %v", packageName, err) } @@ -116,7 +116,7 @@ func (c *ResellerContext) GetPackages() ([]Package, error) { var packageList []string var packages []Package - if _, err := c.api.makeRequest(http.MethodGet, "API_PACKAGES_USER", c.credentials, nil, &packageList); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_PACKAGES_USER", nil, &packageList); err != nil { return nil, err } @@ -175,7 +175,7 @@ func (c *ResellerContext) RenamePackage(oldPackName string, newPackName string) body.Set("old_package", oldPackName) body.Set("new_package", newPackName) - if _, err := c.api.makeRequest(http.MethodPost, "MANAGE_USER_PACKAGES?action=rename", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "MANAGE_USER_PACKAGES?action=rename", body, &response); err != nil { return err } diff --git a/package_reseller.go b/package_reseller.go index ec8f9d8..8e66baf 100644 --- a/package_reseller.go +++ b/package_reseller.go @@ -27,7 +27,7 @@ func (c *AdminContext) CreateResellerPackage(pack ResellerPackage) error { return err } - if _, err = c.api.makeRequest(http.MethodPost, "MANAGE_RESELLER_PACKAGES?add=yes", c.credentials, body, &response); err != nil { + if _, err = c.makeRequestOld(http.MethodPost, "MANAGE_RESELLER_PACKAGES?add=yes", body, &response); err != nil { return err } @@ -50,7 +50,7 @@ func (c *AdminContext) DeleteResellerPackages(packs ...string) error { body.Set("select"+cast.ToString(index), pack) } - if _, err := c.api.makeRequest(http.MethodPost, "MANAGE_USER_PACKAGES", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "MANAGE_USER_PACKAGES", body, &response); err != nil { return err } @@ -65,7 +65,7 @@ func (c *AdminContext) DeleteResellerPackages(packs ...string) error { func (c *AdminContext) GetResellerPackage(packageName string) (ResellerPackage, error) { var rawPack rawResellerPackage - if _, err := c.api.makeRequest(http.MethodGet, "API_PACKAGES_USER?package="+packageName, c.credentials, nil, &rawPack); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_PACKAGES_USER?package="+packageName, nil, &rawPack); err != nil { return ResellerPackage{}, fmt.Errorf("failed to get package info for %v: %v", packageName, err) } @@ -79,7 +79,7 @@ func (c *AdminContext) GetResellerPackages() ([]ResellerPackage, error) { var packageList []string var packages []ResellerPackage - if _, err := c.api.makeRequest(http.MethodGet, "API_PACKAGES_USER", c.credentials, nil, &packageList); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_PACKAGES_USER", nil, &packageList); err != nil { return nil, err } @@ -138,7 +138,7 @@ func (c *AdminContext) RenameResellerPackage(oldPackName string, newPackName str body.Set("old_package", oldPackName) body.Set("new_package", newPackName) - if _, err := c.api.makeRequest(http.MethodPost, "MANAGE_USER_PACKAGES?action=rename", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "MANAGE_USER_PACKAGES?action=rename", body, &response); err != nil { return err } diff --git a/php.go b/php.go new file mode 100644 index 0000000..fdc19d2 --- /dev/null +++ b/php.go @@ -0,0 +1,76 @@ +package directadmin + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/spf13/cast" +) + +type PHPVersion struct { + ID string `json:"value"` + Selected bool `json:"selected"` + Text string `json:"text"` + Version string `json:"version"` +} + +// GetPHPVersions (user) returns an array of the available PHP versions +func (c *UserContext) GetPHPVersions(domainName string) ([]*PHPVersion, error) { + var rawPHPVersions struct { + PHPSelect map[string]struct { + Selected string `json:"selected"` + Text string `json:"text"` + Value string `json:"value"` + } `json:"php1_select"` + PHPVersion1 string `json:"php1_ver"` + PHPVersion2 string `json:"php2_ver"` + PHPVersion3 string `json:"php3_ver"` + PHPVersion4 string `json:"php4_ver"` + } + + if _, err := c.makeRequestOld(http.MethodGet, "API_ADDITIONAL_DOMAINS?domain="+domainName+"&action=view", nil, &rawPHPVersions); err != nil { + return nil, err + } + + versionMap := map[string]string{ + "0": rawPHPVersions.PHPVersion1, + "1": rawPHPVersions.PHPVersion2, + "2": rawPHPVersions.PHPVersion3, + "3": rawPHPVersions.PHPVersion4, + } + + versions := make([]*PHPVersion, 0, len(rawPHPVersions.PHPSelect)) + + for index, rawVersion := range rawPHPVersions.PHPSelect { + versions = append(versions, &PHPVersion{ + ID: cast.ToString(cast.ToInt(index) + 1), // TODO: refactor this + Text: rawVersion.Text, + Selected: rawVersion.Selected == "yes", + Version: versionMap[index], + }) + } + + return versions, nil +} + +// SetPHPVersion (user) sets the PHP version for the given domain to the given version ID +func (c *UserContext) SetPHPVersion(domain string, versionID string) error { + var response apiGenericResponse + + body := url.Values{} + body.Set("action", "php_selector") + body.Set("domain", domain) + body.Set("php1_select", versionID) + body.Set("save", "yes") + + if _, err := c.makeRequestOld(http.MethodPost, "API_DOMAIN", body, &response); err != nil { + return err + } + + if response.Success != "PHP versions saved" { + return fmt.Errorf("failed to set PHP version: %v", response.Result) + } + + return nil +} diff --git a/plugins_softaculous.go b/plugins_softaculous.go new file mode 100644 index 0000000..b4de5e4 --- /dev/null +++ b/plugins_softaculous.go @@ -0,0 +1,179 @@ +package directadmin + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/spf13/cast" +) + +const ( + SoftaculousProtocolHTTP = "1" + SoftaculousProtocolHTTPWWW = "2" + SoftaculousProtocolHTTPS = "3" + SoftaculousProtocolHTTPSWWW = "4" + SoftaculousScriptIDWordPress = 26 +) + +type SoftaculousScript struct { + AdminEmail string `url:"admin_email"` + AdminPassword string `url:"admin_pass"` + AdminUsername string `url:"admin_username"` + AutoUpgrade bool `url:"en_auto_upgrade"` + AutoUpgradePlugins bool `url:"auto_upgrade_plugins"` + AutoUpgradeThemes bool `url:"auto_upgrade_plugins"` + DatabaseName string `url:"softdb"` + DatabasePrefix string `url:"dbprefix"` // optional + Directory string `url:"softdirectory"` + Domain string `url:"softdomain"` + Language string `url:"language"` + NotifyOnInstall bool `url:"noemail"` + NotifyOnUpdate bool `url:"disable_notify_update"` + OverwriteExisting bool `url:"overwrite_existing"` + Protocol string `url:"softproto"` + SiteDescription string `url:"site_desc"` + SiteName string `json:"site_name"` +} + +func (s *SoftaculousScript) Parse() (url.Values, error) { + if err := s.Validate(); err != nil { + return nil, err + } + + values := url.Values{} + values.Add("admin_email", s.AdminEmail) + values.Add("admin_pass", s.AdminPassword) + values.Add("admin_username", s.AdminUsername) + + if s.AutoUpgrade { + values.Add("en_auto_upgrade", "1") + } + + if s.AutoUpgradePlugins { + values.Add("auto_upgrade_plugins", "1") + } + + if s.AutoUpgradeThemes { + values.Add("auto_upgrade_themes", "1") + } + + if s.DatabaseName != "" { + values.Add("softdb", s.DatabaseName) + + if s.DatabasePrefix != "" { + values.Add("dbprefix", s.DatabasePrefix) + } + } + + if s.Directory != "" { + values.Add("softdirectory", s.Directory) + } + + values.Add("softdomain", s.Domain) + values.Add("language", s.Language) + + if !s.NotifyOnInstall { + values.Add("noemail", "1") + } + + if !s.NotifyOnUpdate { + values.Add("disable_notify_update", "1") + } + + if s.OverwriteExisting { + values.Add("overwrite_existing", "1") + } + + values.Add("softproto", s.Protocol) + values.Add("site_desc", s.SiteDescription) + values.Add("site_name", s.SiteName) + + return values, nil +} + +func (s *SoftaculousScript) Validate() error { + if s.AdminEmail == "" { + return errors.New("admin email is required") + } + + if s.AdminPassword == "" { + return errors.New("admin password is required") + } + + if s.AdminUsername == "" { + return errors.New("admin username is required") + } + + if s.DatabasePrefix != "" && !strings.HasSuffix(s.DatabasePrefix, "_") { + return errors.New("database prefix missing trailing underscore") + } + + if s.Language == "" { + return errors.New("language is required") + } + + switch s.Protocol { + case SoftaculousProtocolHTTP, SoftaculousProtocolHTTPWWW, SoftaculousProtocolHTTPS, SoftaculousProtocolHTTPSWWW: + default: + return errors.New("invalid protocol") + } + + if s.SiteDescription == "" { + return errors.New("site description is required") + } + + if s.SiteName == "" { + return errors.New("site name is required") + } + + return nil +} + +func SoftaculousScriptWithDefaults() *SoftaculousScript { + return &SoftaculousScript{ + Language: "en", + Protocol: SoftaculousProtocolHTTPS, + } +} + +// SoftaculousInstallScript calls Softaculous's install script API endpoint. +// +// Docs: https://www.softaculous.com/docs/api/remote-api/#install-a-script +func (c *UserContext) SoftaculousInstallScript(script *SoftaculousScript, scriptID int) error { + response := struct { + Error map[string]string `json:"error"` + }{ + Error: make(map[string]string), + } + + if scriptID == 0 { + return errors.New("missing script id") + } + + body, err := script.Parse() + if err != nil { + return err + } + + body.Set("softsubmit", "1") + + // Softaculous requires a genuine session ID + if c.sessionID == "" { + if err = c.CreateSession(); err != nil { + return fmt.Errorf("failed to create user session: %w", err) + } + } + + if _, err = c.makeRequestOld(http.MethodPost, "PLUGINS/softaculous/index.raw?act=software&soft="+cast.ToString(scriptID)+"&multi_ver=1&api=json", body, &response); err != nil { + return err + } + + if len(response.Error) > 0 { + return fmt.Errorf("failed to install script: %v", response.Error) + } + + return nil +} diff --git a/reseller.go b/reseller.go index 38a02eb..4d3efbe 100644 --- a/reseller.go +++ b/reseller.go @@ -51,7 +51,7 @@ func (c *ResellerContext) CreateUser(user UserConfig, password string, emailUser body.Set("notify", "no") } - if _, err := c.api.makeRequest(http.MethodPost, "API_ACCOUNT_USER?action=create", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_ACCOUNT_USER?action=create", body, &response); err != nil { return fmt.Errorf("failed to create user account: %v", err) } @@ -74,7 +74,7 @@ func (c *ResellerContext) DeleteUsers(usernames ...string) error { body.Set("select"+cast.ToString(index), username) } - if _, err := c.api.makeRequest(http.MethodPost, "API_SELECT_USERS", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_SELECT_USERS", body, &response); err != nil { return err } @@ -89,7 +89,7 @@ func (c *ResellerContext) DeleteUsers(usernames ...string) error { func (c *ResellerContext) GetMyUsers() ([]string, error) { var users []string - if _, err := c.api.makeRequest(http.MethodGet, "API_SHOW_USERS", c.credentials, nil, &users); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_SHOW_USERS", nil, &users); err != nil { return nil, err } @@ -175,7 +175,7 @@ func (c *ResellerContext) GetUserConfig(username string) (*UserConfig, error) { var err error var rawConfig rawUserConfig - if _, err := c.api.makeRequest(http.MethodGet, "API_SHOW_USER_CONFIG?user="+username, c.credentials, nil, &rawConfig); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_SHOW_USER_CONFIG?user="+username, nil, &rawConfig); err != nil { return nil, err } @@ -192,7 +192,7 @@ func (c *ResellerContext) GetUserUsage(username string) (*UserUsage, error) { var rawUsage rawUserUsage var usage UserUsage - if _, err := c.api.makeRequest(http.MethodGet, "API_SHOW_USER_USAGE?bytes=yes&user="+username, c.credentials, nil, &rawUsage); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_SHOW_USER_USAGE?bytes=yes&user="+username, nil, &rawUsage); err != nil { return nil, err } @@ -232,7 +232,7 @@ func (c *ResellerContext) toggleUserSuspension(suspend bool, usernames ...string body.Set("select"+cast.ToString(counter), username) } - if _, err := c.api.makeRequest(http.MethodPost, "API_SELECT_USERS", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_SELECT_USERS", body, &response); err != nil { return err } diff --git a/session.go b/session.go new file mode 100644 index 0000000..d207525 --- /dev/null +++ b/session.go @@ -0,0 +1,135 @@ +package directadmin + +import ( + "net/http" + "strings" +) + +type Session struct { + AllowedCommands []string `json:"allowedCommands"` + ConfigFeatures struct { + Auth2FA bool `json:"auth2FA"` + BruteforceLogScanner bool `json:"bruteforceLogScanner"` + Cgroup bool `json:"cgroup"` + Clamav bool `json:"clamav"` + Composer bool `json:"composer"` + Dnssec int `json:"dnssec"` + Git bool `json:"git"` + Imapsync bool `json:"imapsync"` + Inode bool `json:"inode"` + Ipv6 bool `json:"ipv6"` + Jail int `json:"jail"` + MxWithoutDNSControl bool `json:"mxWithoutDNSControl"` + NetdataSock bool `json:"netdataSock"` + Nginx bool `json:"nginx"` + NginxProxy bool `json:"nginxProxy"` + NginxTemplates bool `json:"nginxTemplates"` + OneClickPMALogin bool `json:"oneClickPMALogin"` + Phpmyadmin bool `json:"phpmyadmin"` + Redis bool `json:"redis"` + ResellerCustomizeSkinConfigJson bool `json:"resellerCustomizeSkinConfigJson"` + Roundcube bool `json:"roundcube"` + RspamdSock bool `json:"rspamdSock"` + SecurityQuestions bool `json:"securityQuestions"` + SquirrelMail bool `json:"squirrelMail"` + Unit bool `json:"unit"` + Webmail bool `json:"webmail"` + Wordpress bool `json:"wordpress"` + } `json:"configFeatures"` + CustomDomainItems []struct { + Checked bool `json:"checked"` + Default string `json:"default"` + Description string `json:"description"` + Hidden bool `json:"hidden"` + Label string `json:"label"` + Name string `json:"name"` + Options []struct { + Text string `json:"text"` + Value string `json:"value"` + } `json:"options"` + ReadOnly bool `json:"readOnly"` + Type string `json:"type"` + } `json:"customDomainItems"` + CustombuildOptions struct { + ModSecurity bool `json:"modSecurity"` + } `json:"custombuildOptions"` + Demo bool `json:"demo"` + DirectadminConfig struct { + AllowForwarderPipe bool `json:"allowForwarderPipe"` + FtpSeparator string `json:"ftpSeparator"` + HomeOverrides []string `json:"homeOverrides"` + LoginKeys bool `json:"loginKeys"` + MaxFilesizeBytes int `json:"maxFilesizeBytes"` + ResellerWarningBandwidthPercentage int `json:"resellerWarningBandwidthPercentage"` + ShowPointersInList int `json:"showPointersInList"` + TableDefaultIPP int `json:"tableDefaultIPP"` + UserWarningBandwidthPercentage int `json:"userWarningBandwidthPercentage"` + UserWarningInodePercentage int `json:"userWarningInodePercentage"` + UserWarningQuotaPercentage int `json:"userWarningQuotaPercentage"` + WebappsSSL bool `json:"webappsSSL"` + WebmailHideLinks bool `json:"webmailHideLinks"` + WebmailLink string `json:"webmailLink"` + } `json:"directadminConfig"` + EffectiveRole string `json:"effectiveRole"` + EffectiveUsername string `json:"effectiveUsername"` + HavePluginHooksAdmin bool `json:"havePluginHooksAdmin"` + HavePluginHooksReseller bool `json:"havePluginHooksReseller"` + HavePluginHooksUser bool `json:"havePluginHooksUser"` + HomeDir string `json:"homeDir"` + LoginAsDNSControl bool `json:"loginAsDNSControl"` + PhpmyadminPublic bool `json:"phpmyadminPublic"` + RealUsername string `json:"realUsername"` + SelectedDomain string `json:"selectedDomain"` + SessionID string `json:"sessionID"` + TicketsEnabled bool `json:"ticketsEnabled"` +} + +func (c *UserContext) CreateSession() error { + response := struct { + SessionID string `json:"sessionID"` + }{} + + request := struct { + Username string `json:"username"` + Password string `json:"password"` + }{ + c.credentials.username, + c.credentials.passkey, + } + + // if we're a reseller logged in as a user, change the username to the reseller + if c.GetMyUsername() != c.credentials.username { + request.Username = strings.Split(c.credentials.username, "|")[0] + } + + if _, err := c.makeRequestNew(http.MethodPost, "login", request, &response); err != nil { + return err + } + + c.sessionID = response.SessionID + + // if we're a reseller logged in as a user, switch the session to the user + if c.GetMyUsername() != c.credentials.username { + switchRequest := struct { + Username string `json:"username"` + }{ + strings.Split(c.credentials.username, "|")[1], + } + + if _, err := c.makeRequestNew(http.MethodPost, "session/login-as/switch", switchRequest, nil); err != nil { + return err + } + } + + return nil +} + +func (c *UserContext) GetSessionInfo() (*Session, error) { + var session Session + + if _, err := c.makeRequestNew(http.MethodGet, "session", nil, &session); err != nil { + return nil, err + } + + return &session, nil +} diff --git a/ssl.go b/ssl.go index 88eb702..4a74e80 100644 --- a/ssl.go +++ b/ssl.go @@ -34,7 +34,7 @@ func (c *UserContext) IssueSSL(domain string, hostnamesToCertify ...string) erro body.Set("le_select"+cast.ToString(index), certDomain) } - if _, err := c.api.makeRequest(http.MethodPost, "API_SSL", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_SSL", body, &response); err != nil { return err } diff --git a/subdomain.go b/subdomain.go index d289746..ec057fe 100644 --- a/subdomain.go +++ b/subdomain.go @@ -24,7 +24,7 @@ func (c *UserContext) CreateSubdomain(subdomain Subdomain) error { body.Set("domain", subdomain.Domain) body.Set("subdomain", subdomain.Subdomain) - if _, err := c.api.makeRequest(http.MethodPost, "API_SUBDOMAINS?action=create", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_SUBDOMAINS?action=create", body, &response); err != nil { return fmt.Errorf("failed to create subdomain: %v", err) } @@ -52,7 +52,7 @@ func (c *UserContext) DeleteSubdomains(deleteData bool, domain string, subdomain body.Set("select"+cast.ToString(index), subdomain) } - if _, err := c.api.makeRequest(http.MethodPost, "API_SUBDOMAINS?action=delete", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "API_SUBDOMAINS?action=delete", body, &response); err != nil { return err } @@ -65,34 +65,13 @@ func (c *UserContext) DeleteSubdomains(deleteData bool, domain string, subdomain // ListSubdomains (user) returns an array of all subdomains for the given domain func (c *UserContext) ListSubdomains(domain string) (subdomainList []string, err error) { - if _, err = c.api.makeRequest(http.MethodGet, "API_SUBDOMAINS?bytes=yes&domain="+domain, c.credentials, nil, &subdomainList); err != nil { + if _, err = c.makeRequestOld(http.MethodGet, "API_SUBDOMAINS?bytes=yes&domain="+domain, nil, &subdomainList); err != nil { return nil, err } return subdomainList, nil } -// TODO: get php version list from server in most efficient way -// could call the user's primary domain and get the info from there, then cache it in the context -//func (u *UserContext) UpdateSubdomainPhpVersion(subdomain Subdomain) error { -// var response apiGenericResponse -// -// body := url.Values{} -// body.Set("domain", subdomain.Domain) -// body.Set("subdomain", subdomain.Subdomain) -// body.Set("public_html", subdomain.PublicHtml) -// -// if _, err := c.api.makeRequest(http.MethodPost, "SUBDOMAIN?action=document_root_override", c.credentials, body, &response); err != nil { -// return fmt.Errorf("failed to update subdomain root: %v", err) -// } -// -// if response.Success != "Success" { -// return fmt.Errorf("failed to update subdomain root: %v", response.Result) -// } -// -// return nil -//} - func (c *UserContext) UpdateSubdomainRoot(subdomain Subdomain) error { var response apiGenericResponse @@ -101,7 +80,7 @@ func (c *UserContext) UpdateSubdomainRoot(subdomain Subdomain) error { body.Set("subdomain", subdomain.Subdomain) body.Set("public_html", subdomain.PublicHtml) - if _, err := c.api.makeRequest(http.MethodPost, "SUBDOMAIN?action=document_root_override", c.credentials, body, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodPost, "SUBDOMAIN?action=document_root_override", body, &response); err != nil { return fmt.Errorf("failed to update subdomain root: %v", err) } diff --git a/system.go b/system.go index 9e8d0d4..4248538 100644 --- a/system.go +++ b/system.go @@ -99,31 +99,31 @@ type BasicSysInfo struct { func (c *UserContext) GetBasicSysInfo() (*BasicSysInfo, error) { var basicSysInfo BasicSysInfo - if _, err := c.api.makeRequestN(http.MethodGet, "info", c.credentials, nil, &basicSysInfo); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "info", nil, &basicSysInfo); err != nil { return nil, fmt.Errorf("failed to get basic sys info: %v", err) } return &basicSysInfo, nil } -//func (c *UserContext) GetPhpVersions() (*SysInfo, error) { +// func (c *UserContext) GetPhpVersions() (*SysInfo, error) { // var rawSys rawSysInfo // var sys SysInfo // -// if _, err := c.api.makeRequest(http.MethodGet, "API_SYSTEM_INFO", c.credentials, &rawSys); err != nil { +// if _, err := c.makeRequestOld(http.MethodGet, "API_SYSTEM_INFO", &rawSys); err != nil { // return nil, err // } // // sys = rawSys.parse() // // return &sys, nil -//} +// } func (c *UserContext) GetSysInfo() (*SysInfo, error) { var rawSys rawSysInfo var sys SysInfo - if _, err := c.api.makeRequest(http.MethodGet, "API_SYSTEM_INFO", c.credentials, nil, &rawSys); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "API_SYSTEM_INFO", nil, &rawSys); err != nil { return nil, err } diff --git a/user.go b/user.go index 45a4dd4..e0ec34c 100644 --- a/user.go +++ b/user.go @@ -18,6 +18,7 @@ type ( UserContext struct { api *API credentials credentials + sessionID string User User } @@ -93,7 +94,7 @@ func (c *UserContext) GetMyUserConfig() (*UserConfig, error) { var err error var rawConfig rawUserConfig - if _, err = c.api.makeRequest(http.MethodGet, "API_SHOW_USER_CONFIG", c.credentials, nil, &rawConfig); err != nil { + if _, err = c.makeRequestOld(http.MethodGet, "API_SHOW_USER_CONFIG", nil, &rawConfig); err != nil { return nil, err } @@ -114,7 +115,7 @@ func (c *UserContext) GetMyUserUsage() (*UserUsage, error) { return nil, errors.New("user does not have a domain") } - if _, err := c.api.makeRequest(http.MethodGet, "USER_STATS?bytes=yes&domain="+c.User.Config.Domain, c.credentials, nil, &rawUsage); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "USER_STATS?bytes=yes&domain="+c.User.Config.Domain, nil, &rawUsage); err != nil { return nil, err } @@ -134,7 +135,7 @@ func (c *UserContext) addUsernamePrefix(check string) string { func (c *UserContext) checkObjectExists(body url.Values) error { var response apiGenericResponse - if _, err := c.api.makeRequest(http.MethodGet, "JSON_VALIDATE?"+body.Encode(), c.credentials, nil, &response); err != nil { + if _, err := c.makeRequestOld(http.MethodGet, "JSON_VALIDATE?"+body.Encode(), nil, &response); err != nil { return err } diff --git a/wordpress.go b/wordpress.go index 7ee2173..ef89b8a 100644 --- a/wordpress.go +++ b/wordpress.go @@ -2,10 +2,11 @@ package directadmin import ( "fmt" - "github.com/spf13/cast" "net/http" "strings" "time" + + "github.com/spf13/cast" ) type ( @@ -65,7 +66,7 @@ func (c *UserContext) ChangeWordPressUserPassword(locationId string, userId int, passwordObject.Password = password - if _, err := c.api.makeRequestN(http.MethodPost, "wordpress/locations/"+locationId+"/users/"+cast.ToString(userId)+"/change-password", c.credentials, passwordObject, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "wordpress/locations/"+locationId+"/users/"+cast.ToString(userId)+"/change-password", passwordObject, nil); err != nil { return fmt.Errorf("failed to change wordpress user password: %v", err) } @@ -105,7 +106,7 @@ func (c *UserContext) CreateWordPressInstall(install WordPressInstall, createDat install.FilePath = install.FilePath[1:] } - if _, err := c.api.makeRequestN(http.MethodPost, "wordpress/install", c.credentials, install, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "wordpress/install", install, nil); err != nil { if createDatabase { if dbErr := c.DeleteDatabase(install.DbName); dbErr != nil { err = fmt.Errorf("%v: %v", dbErr, err) @@ -124,7 +125,7 @@ func (c *UserContext) CreateWordPressInstallQuick(install WordPressInstallQuick) install.FilePath = install.FilePath[1:] } - if _, err := c.api.makeRequestN(http.MethodPost, "wordpress/install-quick", c.credentials, install, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "wordpress/install-quick", install, nil); err != nil { return err } @@ -132,7 +133,7 @@ func (c *UserContext) CreateWordPressInstallQuick(install WordPressInstallQuick) } func (c *UserContext) DeleteWordPressInstall(id string) error { - if _, err := c.api.makeRequestN(http.MethodDelete, "wordpress/locations/"+id, c.credentials, nil, nil); err != nil { + if _, err := c.makeRequestNew(http.MethodDelete, "wordpress/locations/"+id, nil, nil); err != nil { return err } @@ -142,7 +143,7 @@ func (c *UserContext) DeleteWordPressInstall(id string) error { func (c *UserContext) GetWordPressInstalls() ([]*WordPressLocation, error) { var wordpressInstalls []*WordPressLocation - if _, err := c.api.makeRequestN(http.MethodGet, "wordpress/locations", c.credentials, nil, &wordpressInstalls); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "wordpress/locations", nil, &wordpressInstalls); err != nil { return nil, fmt.Errorf("failed to get wordpress installs: %v", err) } @@ -154,7 +155,7 @@ func (c *UserContext) GetWordPressSSOLink(locationId string, userId int) (string URL string `json:"url"` } - if _, err := c.api.makeRequestN(http.MethodPost, "wordpress/locations/"+locationId+"/users/"+cast.ToString(userId)+"/sso-login", c.credentials, nil, &ssoObject); err != nil { + if _, err := c.makeRequestNew(http.MethodPost, "wordpress/locations/"+locationId+"/users/"+cast.ToString(userId)+"/sso-login", nil, &ssoObject); err != nil { return "", fmt.Errorf("failed to get wordpress installs: %v", err) } @@ -164,7 +165,7 @@ func (c *UserContext) GetWordPressSSOLink(locationId string, userId int) (string func (c *UserContext) GetWordPressUsers(locationId string) ([]*WordPressUser, error) { var wordpressUsers []*WordPressUser - if _, err := c.api.makeRequestN(http.MethodGet, "wordpress/locations/"+locationId+"/users", c.credentials, nil, &wordpressUsers); err != nil { + if _, err := c.makeRequestNew(http.MethodGet, "wordpress/locations/"+locationId+"/users", nil, &wordpressUsers); err != nil { return nil, fmt.Errorf("failed to get wordpress users: %v", err) }