mirror of
https://github.com/cristicalin/synology-csi.git
synced 2026-05-08 05:52:38 +00:00
Initial commit
This commit is contained in:
208
pkg/dsm/webapi/dsmwebapi.go
Normal file
208
pkg/dsm/webapi/dsmwebapi.go
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright 2021 Synology Inc.
|
||||
*/
|
||||
|
||||
package webapi
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"github.com/SynologyOpenSource/synology-csi/pkg/logger"
|
||||
)
|
||||
|
||||
type DSM struct {
|
||||
Ip string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
Sid string
|
||||
Https bool
|
||||
}
|
||||
|
||||
type errData struct {
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
type dsmApiResp struct {
|
||||
Success bool `json:"success"`
|
||||
Err errData `json:"error"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
StatusCode int
|
||||
ErrorCode int
|
||||
Success bool
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func (dsm *DSM) sendRequest(data string, apiTemplate interface{}, params url.Values, cgiPath string) (Response, error) {
|
||||
resp, err := dsm.sendRequestWithoutConnectionCheck(data, apiTemplate, params, cgiPath)
|
||||
if err != nil && resp.ErrorCode == 119 { // WEBAPI_ERR_SID_NOT_FOUND
|
||||
// Re-login
|
||||
if err := dsm.Login(); err != nil {
|
||||
return Response{}, fmt.Errorf("Failed to re-login to DSM: [%s]. err: %v", dsm.Ip, err)
|
||||
}
|
||||
log.Info("Re-login succeeded.")
|
||||
return dsm.sendRequestWithoutConnectionCheck(data, apiTemplate, params, cgiPath);
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (dsm *DSM) sendRequestWithoutConnectionCheck(data string, apiTemplate interface{}, params url.Values, cgiPath string) (Response, error) {
|
||||
client := &http.Client{}
|
||||
var req *http.Request
|
||||
var err error
|
||||
var cgiUrl string
|
||||
|
||||
// Ex: http://10.12.12.14:5000/webapi/auth.cgi
|
||||
if dsm.Https {
|
||||
// TODO: input CA certificate and fill in tls config
|
||||
// Skip Verify when https
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
cgiUrl = fmt.Sprintf("https://%s:%d/%s", dsm.Ip, dsm.Port, cgiPath)
|
||||
} else {
|
||||
cgiUrl = fmt.Sprintf("http://%s:%d/%s", dsm.Ip, dsm.Port, cgiPath)
|
||||
}
|
||||
|
||||
baseUrl, err := url.Parse(cgiUrl)
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
|
||||
baseUrl.RawQuery = params.Encode()
|
||||
|
||||
if logger.WebapiDebug {
|
||||
log.Debugln(baseUrl.RawQuery)
|
||||
}
|
||||
|
||||
if data != "" {
|
||||
req, err = http.NewRequest("POST", baseUrl.String(), nil)
|
||||
} else {
|
||||
req, err = http.NewRequest("GET", baseUrl.String(), nil)
|
||||
}
|
||||
|
||||
if dsm.Sid != "" {
|
||||
cookie := http.Cookie{Name: "id", Value: dsm.Sid}
|
||||
req.AddCookie(&cookie)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// For debug print text body
|
||||
var bodyText []byte
|
||||
if logger.WebapiDebug {
|
||||
bodyText, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
s := string(bodyText)
|
||||
log.Debugln(s)
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 && resp.StatusCode != 302 {
|
||||
return Response{}, fmt.Errorf("Bad response status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Strip data json data from response
|
||||
type envelop struct {
|
||||
dsmApiResp
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
e := envelop{}
|
||||
var outResp Response
|
||||
|
||||
if logger.WebapiDebug {
|
||||
if err := json.Unmarshal(bodyText, &e); err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
} else {
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
|
||||
if err := decoder.Decode(&e); err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
}
|
||||
outResp.Success = e.Success
|
||||
outResp.ErrorCode = e.Err.Code
|
||||
outResp.StatusCode = resp.StatusCode
|
||||
|
||||
if !e.Success {
|
||||
return outResp, fmt.Errorf("DSM Api error. Error code:%d", outResp.ErrorCode)
|
||||
}
|
||||
|
||||
if e.Data != nil {
|
||||
if err := json.Unmarshal(e.Data, apiTemplate); err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
}
|
||||
|
||||
outResp.Data = apiTemplate
|
||||
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
|
||||
return outResp, nil
|
||||
}
|
||||
|
||||
// Login by given user name and password
|
||||
func (dsm *DSM) Login() error {
|
||||
params := url.Values{}
|
||||
params.Add("api", "SYNO.API.Auth")
|
||||
params.Add("method", "login")
|
||||
params.Add("version", "3")
|
||||
params.Add("account", dsm.Username)
|
||||
params.Add("passwd", dsm.Password)
|
||||
params.Add("format", "sid")
|
||||
|
||||
type LoginResp struct {
|
||||
Sid string `json:"sid"`
|
||||
}
|
||||
|
||||
resp, err := dsm.sendRequestWithoutConnectionCheck("", &LoginResp{}, params, "webapi/auth.cgi")
|
||||
if err != nil {
|
||||
r, _ := regexp.Compile("passwd=.*&")
|
||||
temp := r.ReplaceAllString(err.Error(), "")
|
||||
|
||||
return fmt.Errorf("%s", temp)
|
||||
}
|
||||
|
||||
loginResp, ok := resp.Data.(*LoginResp)
|
||||
if !ok {
|
||||
return fmt.Errorf("Failed to assert response to %T", &LoginResp{})
|
||||
}
|
||||
dsm.Sid = loginResp.Sid
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logout on current IP and reset the synoToken
|
||||
func (dsm *DSM) Logout() error {
|
||||
params := url.Values{}
|
||||
params.Add("api", "SYNO.API.Auth")
|
||||
params.Add("method", "logout")
|
||||
params.Add("version", "1")
|
||||
|
||||
_, err := dsm.sendRequestWithoutConnectionCheck("", &struct{}{}, params, "webapi/entry.cgi")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dsm.Sid = ""
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user