Compare commits

...

7 Commits

Author SHA1 Message Date
Adphi 76b69b7acd Fix user in doc.go [skip-ci] 2019-07-30 18:52:32 +00:00
Adphi 90081d6e8f improved comments 2019-07-23 09:52:34 +02:00
Adphi 5d284c1ad8 Merge branch 'v2' into 'master'
V2

See merge request partitio/Nextcloud-Partitio/gonextcloud!4
2019-07-22 14:56:11 +00:00
Adphi 5a9b0d715c updated README.md, unexported responses types 2019-07-22 16:40:42 +02:00
Adphi 85ab4b93f0 updated doc.go 2019-07-22 16:22:10 +02:00
Adphi 3cf8fdec75 code restructuration: private implementation 2019-07-22 16:17:11 +02:00
adphi 7022842ce1 fix webdav: moved to gitlab.bertha.cloud/adphi/gowebdav fork 2019-07-21 01:33:59 +02:00
205 changed files with 31241 additions and 15369 deletions

1226
README.md

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
package types
package gonextcloud
//App
// App is a nextcloud application (plugin)
type App struct {
ID string `json:"id"`
Ocsid string `json:"ocsid"`

View File

@ -1,29 +1,29 @@
package gonextcloud
import (
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
req "github.com/levigross/grequests"
)
//Apps contains all Apps available actions
type Apps struct {
c *Client
//apps contains all apps available actions
type apps struct {
c *client
}
//List return the list of the Nextcloud Apps
func (a *Apps) List() ([]string, error) {
//List return the list of the Nextcloud apps
func (a *apps) List() ([]string, error) {
res, err := a.c.baseRequest(http.MethodGet, routes.apps, nil)
if err != nil {
return nil, err
}
var r types.AppListResponse
var r appListResponse
res.JSON(&r)
return r.Ocs.Data.Apps, nil
}
//ListEnabled lists the enabled apps
func (a *Apps) ListEnabled() ([]string, error) {
func (a *apps) ListEnabled() ([]string, error) {
ro := &req.RequestOptions{
Params: map[string]string{"filter": "enabled"},
}
@ -31,13 +31,13 @@ func (a *Apps) ListEnabled() ([]string, error) {
if err != nil {
return nil, err
}
var r types.AppListResponse
var r appListResponse
res.JSON(&r)
return r.Ocs.Data.Apps, nil
}
//ListDisabled lists the disabled apps
func (a *Apps) ListDisabled() ([]string, error) {
func (a *apps) ListDisabled() ([]string, error) {
ro := &req.RequestOptions{
Params: map[string]string{"filter": "disabled"},
}
@ -45,30 +45,30 @@ func (a *Apps) ListDisabled() ([]string, error) {
if err != nil {
return nil, err
}
var r types.AppListResponse
var r appListResponse
res.JSON(&r)
return r.Ocs.Data.Apps, nil
}
//Infos return the app's details
func (a *Apps) Infos(name string) (types.App, error) {
func (a *apps) Infos(name string) (App, error) {
res, err := a.c.baseRequest(http.MethodGet, routes.apps, nil, name)
if err != nil {
return types.App{}, err
return App{}, err
}
var r types.AppResponse
var r appResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Enable enables an app
func (a *Apps) Enable(name string) error {
func (a *apps) Enable(name string) error {
_, err := a.c.baseRequest(http.MethodPost, routes.apps, nil, name)
return err
}
//Disable disables an app
func (a *Apps) Disable(name string) error {
func (a *apps) Disable(name string) error {
_, err := a.c.baseRequest(http.MethodDelete, routes.apps, nil, name)
return err
}

View File

@ -1,52 +1,52 @@
package gonextcloud
import (
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
"sync"
req "github.com/levigross/grequests"
)
//AppsConfig contains all Apps Configuration available actions
type AppsConfig struct {
c *Client
//appsConfig contains all apps Configuration available actions
type appsConfig struct {
c *client
}
//List lists all the available apps
func (a *AppsConfig) List() (apps []string, err error) {
func (a *appsConfig) List() (apps []string, err error) {
res, err := a.c.baseRequest(http.MethodGet, routes.appsConfig, nil)
if err != nil {
return nil, err
}
var r types.AppConfigResponse
var r appConfigResponse
res.JSON(&r)
return r.Ocs.Data.Data, nil
}
//Keys returns the app's config keys
func (a *AppsConfig) Keys(id string) (keys []string, err error) {
func (a *appsConfig) Keys(id string) (keys []string, err error) {
res, err := a.c.baseRequest(http.MethodGet, routes.appsConfig, nil, id)
if err != nil {
return nil, err
}
var r types.AppConfigResponse
var r appConfigResponse
res.JSON(&r)
return r.Ocs.Data.Data, nil
}
//Value get the config value for the given app's key
func (a *AppsConfig) Value(id, key string) (string, error) {
func (a *appsConfig) Value(id, key string) (string, error) {
res, err := a.c.baseRequest(http.MethodGet, routes.appsConfig, nil, id, key)
if err != nil {
return "", err
}
var r types.AppcConfigValueResponse
var r appcConfigValueResponse
res.JSON(&r)
return r.Ocs.Data.Data, nil
}
//SetValue set the config value for the given app's key
func (a *AppsConfig) SetValue(id, key, value string) error {
func (a *appsConfig) SetValue(id, key, value string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"value": value,
@ -57,13 +57,13 @@ func (a *AppsConfig) SetValue(id, key, value string) error {
}
//DeleteValue delete the config value and (!! be careful !!) the key
func (a *AppsConfig) DeleteValue(id, key, value string) error {
func (a *appsConfig) DeleteValue(id, key, value string) error {
_, err := a.c.baseRequest(http.MethodDelete, routes.appsConfig, nil, id, key)
return err
}
//Get returns all apps AppConfigDetails
func (a *AppsConfig) Get() (map[string]map[string]string, error) {
func (a *appsConfig) Get() (map[string]map[string]string, error) {
config := map[string]map[string]string{}
m := sync.Mutex{}
appsIDs, err := a.List()
@ -88,7 +88,7 @@ func (a *AppsConfig) Get() (map[string]map[string]string, error) {
}
//Details returns all the config's key, values pair of the app
func (a *AppsConfig) Details(appID string) (map[string]string, error) {
func (a *appsConfig) Details(appID string) (map[string]string, error) {
config := map[string]string{}
m := sync.Mutex{}
var err error

View File

@ -4,14 +4,12 @@ import (
"fmt"
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
)
var errUnauthorized = fmt.Errorf("login first")
// Login perform login and create a session with the Nextcloud API.
func (c *Client) Login(username string, password string) error {
func (c *client) Login(username string, password string) error {
c.username = username
c.password = password
options := req.RequestOptions{
@ -25,14 +23,14 @@ func (c *Client) Login(username string, password string) error {
if err != nil {
return err
}
var r types.CapabilitiesResponse
var r capabilitiesResponse
res.JSON(&r)
// No need to check for Ocs.Meta.StatusCode as capabilities are always returned
// No need to check for Ocs.meta.StatusCode as capabilities are always returned
c.capabilities = &r.Ocs.Data.Capabilities
c.version = &r.Ocs.Data.Version
// Check if authentication failed
if !c.loggedIn() {
e := types.APIError{Message: "authentication failed"}
e := APIError{Message: "authentication failed"}
return &e
}
// Create webdav client
@ -41,7 +39,7 @@ func (c *Client) Login(username string, password string) error {
}
// Logout logs out from the Nextcloud API, close the session and delete session's cookie
func (c *Client) Logout() error {
func (c *client) Logout() error {
c.session.CloseIdleConnections()
c.session.HTTPClient.Jar = nil
// Clear capabilities as it is used to check for valid authentication
@ -49,7 +47,7 @@ func (c *Client) Logout() error {
return nil
}
func (c *Client) loggedIn() bool {
func (c *client) loggedIn() bool {
// When authentication failed, capabilities doesn't contains core information
if c.capabilities == nil {
return false

View File

@ -1,6 +1,6 @@
package types
package gonextcloud
//Capabilities
// Capabilities is the capabilities provided by the Nextcloud server
type Capabilities struct {
Core struct {
Pollinterval int `json:"pollinterval"`

101
client.go
View File

@ -1,101 +0,0 @@
package gonextcloud
import (
"net/url"
req "github.com/levigross/grequests"
"github.com/studio-b12/gowebdav"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
)
// Client is the API client that performs all operations against a Nextcloud server.
type Client struct {
baseURL *url.URL
username string
password string
session *req.Session
headers map[string]string
capabilities *types.Capabilities
version *types.Version
apps *Apps
appsConfig *AppsConfig
groupFolders *GroupFolders
notifications *Notifications
shares *Shares
users *Users
groups *Groups
webdav *webDav
}
// NewClient create a new Client from the Nextcloud Instance URL
func NewClient(hostname string) (*Client, error) {
baseURL, err := url.ParseRequestURI(hostname)
if err != nil {
baseURL, err = url.ParseRequestURI("https://" + hostname)
if err != nil {
return nil, err
}
}
c := &Client{
baseURL: baseURL,
headers: map[string]string{
"OCS-APIREQUEST": "true",
"Accept": "application/json",
},
}
c.apps = &Apps{c}
c.appsConfig = &AppsConfig{c}
c.groupFolders = &GroupFolders{c}
c.notifications = &Notifications{c}
c.shares = &Shares{c}
c.users = &Users{c}
c.groups = &Groups{c}
// Create empty webdav client
// It will be replaced after login
c.webdav = &webDav{Client: &gowebdav.Client{}}
return c, nil
}
// Apps return the Apps client Interface
func (c *Client) Apps() types.Apps {
return c.apps
}
// AppsConfig return the AppsConfig client Interface
func (c *Client) AppsConfig() types.AppsConfig {
return c.appsConfig
}
// GroupFolders return the GroupFolders client Interface
func (c *Client) GroupFolders() types.GroupFolders {
return c.groupFolders
}
// Notifications return the Notifications client Interface
func (c *Client) Notifications() types.Notifications {
return c.notifications
}
// Shares return the Shares client Interface
func (c *Client) Shares() types.Shares {
return c.shares
}
// Users return the Users client Interface
func (c *Client) Users() types.Users {
return c.users
}
// Groups return the Groups client Interface
func (c *Client) Groups() types.Groups {
return c.groups
}
// WebDav return the WebDav client Interface
func (c *Client) WebDav() types.WebDav {
return c.webdav
}

99
client_impl.go Normal file
View File

@ -0,0 +1,99 @@
package gonextcloud
import (
"net/url"
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/adphi/gowebdav"
)
// client is the API client that performs all operations against a Nextcloud server.
type client struct {
baseURL *url.URL
username string
password string
session *req.Session
headers map[string]string
capabilities *Capabilities
version *Version
apps *apps
appsConfig *appsConfig
groupFolders *groupFolders
notifications *notifications
shares *shares
users *users
groups *groups
webdav *webDav
}
// newClient create a new client from the Nextcloud Instance URL
func newClient(hostname string) (*client, error) {
baseURL, err := url.ParseRequestURI(hostname)
if err != nil {
baseURL, err = url.ParseRequestURI("https://" + hostname)
if err != nil {
return nil, err
}
}
c := &client{
baseURL: baseURL,
headers: map[string]string{
"OCS-APIREQUEST": "true",
"Accept": "application/json",
},
}
c.apps = &apps{c}
c.appsConfig = &appsConfig{c}
c.groupFolders = &groupFolders{c}
c.notifications = &notifications{c}
c.shares = &shares{c}
c.users = &users{c}
c.groups = &groups{c}
// Create empty webdav client
// It will be replaced after login
c.webdav = &webDav{Client: &gowebdav.Client{}}
return c, nil
}
// apps return the apps client Interface
func (c *client) Apps() Apps {
return c.apps
}
// appsConfig return the appsConfig client Interface
func (c *client) AppsConfig() AppsConfig {
return c.appsConfig
}
// groupFolders return the groupFolders client Interface
func (c *client) GroupFolders() GroupFolders {
return c.groupFolders
}
// notifications return the notifications client Interface
func (c *client) Notifications() Notifications {
return c.notifications
}
// shares return the shares client Interface
func (c *client) Shares() Shares {
return c.shares
}
// users return the users client Interface
func (c *client) Users() Users {
return c.users
}
// groups return the groups client Interface
func (c *client) Groups() Groups {
return c.groups
}
// WebDav return the WebDav client Interface
func (c *client) WebDav() WebDav {
return c.webdav
}

4
doc.go
View File

@ -1,5 +1,5 @@
/*
Package gonextcloud is a Go client for the Nextcloud Provisioning API.
Package gonextcloud is a simple Go Client for Nextcloud's API.
For more information about the Provisioning API, see the documentation:
https://docs.nextcloud.com/server/13/admin_manual/configuration_user/user_provisioning_api.html
@ -34,7 +34,7 @@ For example, to list all the Nextcloud's instance users:
if err != nil {
panic(err)
}
fmt.Println("Users :", users)
fmt.Println("users :", users)
}
*/
package gonextcloud

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,36 @@
package types
package gonextcloud
import (
"fmt"
"strings"
)
//APIError contains the returned error code and message from the Nextcloud's API
// APIError contains the returned error code and message from the Nextcloud's API
type APIError struct {
Code int
Message string
}
//ErrorFromMeta return a types.APIError from the Response's types.Meta
func ErrorFromMeta(meta Meta) *APIError {
//errorFromMeta return a types.APIError from the Response's types.meta
func errorFromMeta(meta meta) *APIError {
return &APIError{
meta.Statuscode,
meta.Message,
}
}
//Error return the types.APIError string
// Error return the types.APIError string
func (e *APIError) Error() string {
return fmt.Sprintf("%d : %s", e.Code, e.Message)
}
//UpdateError contains the user's field and corresponding error
// UpdateError contains the user's field and corresponding error
type UpdateError struct {
Field string
Error error
}
//UpdateError contains the errors resulting from a UserUpdate or a UserCreateFull call
// UserUpdateError contains the errors resulting from a UserUpdate or a UserCreateFull call
type UserUpdateError struct {
Errors map[string]error
}
@ -43,8 +43,8 @@ func (e *UserUpdateError) Error() string {
return strings.Join(errors, ", ")
}
//NewUpdateError returns an UpdateError based on an UpdateError channel
func NewUpdateError(errors chan *UpdateError) *UserUpdateError {
//newUpdateError returns an UpdateError based on an UpdateError channel
func newUpdateError(errors chan *UpdateError) *UserUpdateError {
ue := UserUpdateError{map[string]error{}}
for e := range errors {
if e != nil {

View File

@ -1,11 +1,12 @@
package types
package gonextcloud
import (
"errors"
"github.com/stretchr/testify/assert"
"strconv"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUserUpdateErrors(t *testing.T) {
@ -24,7 +25,7 @@ func TestUserUpdateErrors(t *testing.T) {
}
close(errs)
}()
uerrs := NewUpdateError(errs)
uerrs := newUpdateError(errs)
assert.Equal(t, exp, uerrs.Errors)
assert.NotEmpty(t, uerrs.Error())
}
@ -41,6 +42,6 @@ func TestUserUpdateErrorsNil(t *testing.T) {
wg.Wait()
close(errs)
}()
uerrs := NewUpdateError(errs)
uerrs := newUpdateError(errs)
assert.Nil(t, uerrs)
}

26
go.mod
View File

@ -3,36 +3,16 @@ module gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud
go 1.12
require (
9fans.net/go v0.0.2 // indirect
github.com/alecthomas/gometalinter v3.0.0+incompatible // indirect
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
github.com/davidrjenni/reftools v0.0.0-20190411195930-981bbac422f8 // indirect
github.com/fatih/gomodifytags v0.0.0-20190714141403-b08246536dab // indirect
github.com/fatih/motion v0.0.0-20190527122956-41470362fad4 // indirect
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf // indirect
github.com/josharian/impl v0.0.0-20190706034337-76254c7a7314 // indirect
github.com/jstemmer/gotags v1.4.1 // indirect
github.com/kisielk/errcheck v1.2.0 // indirect
github.com/klauspost/asmfmt v1.2.0 // indirect
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91 // indirect
github.com/kr/pty v1.1.8 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/levigross/grequests v0.0.0-20171009010347-bf9788368aa0
github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f // indirect
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0
github.com/rogpeppe/godef v1.1.1 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.2.2
github.com/studio-b12/gowebdav v0.0.0-20190103184047-38f79aeaf1ac
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
golang.org/x/mod v0.1.0 // indirect
gitlab.bertha.cloud/adphi/gowebdav v0.0.0-20190720232020-771eec6e76d0
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect
golang.org/x/text v0.3.2 // indirect
golang.org/x/tools v0.0.0-20190715044752-607ca053a137 // indirect
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.1
honnef.co/go/tools v0.0.0-2019.2.1 // indirect
)

72
go.sum
View File

@ -1,110 +1,42 @@
9fans.net/go v0.0.0-20181112161441-237454027057/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY=
9fans.net/go v0.0.2 h1:RYM6lWITV8oADrwLfdzxmt8ucfW6UtP9v1jg4qAbqts=
9fans.net/go v0.0.2/go.mod h1:lfPdxjq9v8pVQXUMBCx5EO5oLXWQFlKRQgs1kEkjoIM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/alecthomas/gometalinter v3.0.0+incompatible h1:e9Zfvfytsw/e6Kd/PYd75wggK+/kX5Xn8IYDUKyc5fU=
github.com/alecthomas/gometalinter v3.0.0+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidrjenni/reftools v0.0.0-20190411195930-981bbac422f8 h1:Eu7kPTwAOeiiot8fjH/WXIYSsoaYB7Xs7sPF8NYeKhs=
github.com/davidrjenni/reftools v0.0.0-20190411195930-981bbac422f8/go.mod h1:0qWLWApvobxwtd9/A8fS62VkRImuquIgtCv/ye+KnxA=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/gomodifytags v0.0.0-20190714141403-b08246536dab h1:rRN6YcbF0x5L4bjw+k3JzetTxPyTKNpwqZypLEEkX6w=
github.com/fatih/gomodifytags v0.0.0-20190714141403-b08246536dab/go.mod h1:p2/x7bnOQsbq/deXsDIlj2yLiKFGPkD2nuoYqwn8R4Y=
github.com/fatih/motion v0.0.0-20190527122956-41470362fad4 h1:g4GA9iRiXHcBKOAjgZi2HsLP5zD2bSXZE5VL/+3yNN8=
github.com/fatih/motion v0.0.0-20190527122956-41470362fad4/go.mod h1:pseIrV+t9A4+po+KJ1LheSnYH8m1qs6WhKx2zFiGi9I=
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7 h1:bGT+Ub6bpzHl7AAYQhBrZ5nYTAH2SF/848WducU0Ao4=
github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg=
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/josharian/impl v0.0.0-20190706034337-76254c7a7314 h1:c7alkeL6KkwLgu+Vqizv/8+YzbCXF7kizm3sSZEIWNw=
github.com/josharian/impl v0.0.0-20190706034337-76254c7a7314/go.mod h1:t4Tr0tn92eq5ISef4cS5plFAMYAqZlAXtgUcKE6y8nw=
github.com/jstemmer/gotags v1.4.1 h1:aWIyXsU3lTDqhsEC49MP85p2cUUWr2ptvdGNqqGA3r4=
github.com/jstemmer/gotags v1.4.1/go.mod h1:b6J3X0bsLbR4C5SgSx3V3KjuWTtmRzcmWPbTkWZ49PA=
github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/asmfmt v1.2.0 h1:zwsyBYgEdabg32alMful/5pRtMTcR5C5w1LKNg9OD78=
github.com/klauspost/asmfmt v1.2.0/go.mod h1:RAoUvqkWr2rUa2I19qKMEVZQe4BVtcHGTMCUOcCU2Lg=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91 h1:hunjgdb3b21ZdRmzDPXii0EcnHpjH7uCP+kODoE1JH0=
github.com/koron/iferr v0.0.0-20180615142939-bb332a3b1d91/go.mod h1:C2tFh8w3I6i4lnUJfoBx2Hwku3mgu4wPNTtUNp1i5KI=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/levigross/grequests v0.0.0-20171009010347-bf9788368aa0 h1:QpdhtrR7SX3R7OlEv9dZarsXogM3PM/tl1ibRH/eHbQ=
github.com/levigross/grequests v0.0.0-20171009010347-bf9788368aa0/go.mod h1:uCZIhROSrVmuF/BPYFPwDeiiQ6juSLp0kikFoEcNcEs=
github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f h1:ee+twVCignaZjt7jpbMSLxAeTN/Nfq9W/nm91E7QO1A=
github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f/go.mod h1:hltEC42XzfMNgg0S1v6JTywwra2Mu6F6cLR03debVQ8=
github.com/nicksnyder/go-i18n v2.0.2+incompatible h1:Xt6dluut3s2zBUha8/3sj6atWMQbFioi9OMqUGH9khg=
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0 h1:R+lX9nKwNd1n7UE5SQAyoorREvRn3aLF6ZndXBoIWqY=
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/godef v1.1.1 h1:NujOtt9q9vIClRTB3sCZpavac+NMRaIayzrcz1h4fSE=
github.com/rogpeppe/godef v1.1.1/go.mod h1:oEo1eMy1VUEHUzUIX4F7IqvMJRiz9UId44mvnR8oPlQ=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/studio-b12/gowebdav v0.0.0-20190103184047-38f79aeaf1ac h1:xQ9gCVzqb939vjhxuES4IXYe4AlHB4Q71/K06aazQmQ=
github.com/studio-b12/gowebdav v0.0.0-20190103184047-38f79aeaf1ac/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s=
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a h1:00UFliGZl2UciXe8o/2iuEsRQ9u7z0rzDTVzuj6EYY0=
github.com/zmb3/gogetdoc v0.0.0-20190228002656-b37376c5da6a/go.mod h1:ofmGw6LrMypycsiWcyug6516EXpIxSbZ+uI9ppGypfY=
gitlab.bertha.cloud/adphi/gowebdav v0.0.0-20190720232020-771eec6e76d0 h1:kjJ5Xn+FgD+QvWP670A2hmdMqxWkOuffMukEA1JSGo4=
gitlab.bertha.cloud/adphi/gowebdav v0.0.0-20190720232020-771eec6e76d0/go.mod h1:Nr6YgM/ZBLPOlAAjcER6HSAXF64AAlal6AJ2CEKg2Fc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 h1:xx5MUFyRQRbPk6VjWjIE1epE/K5AoDD8QUN116NCy8k=
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181130195746-895048a75ecf/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181207195948-8634b1ecd393/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190408220357-e5b8258f4918/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190715044752-607ca053a137 h1:66qBt03VVO7qHSq0eBAYy9iD1y2EbXU09A+F5dZ0j8s=
golang.org/x/tools v0.0.0-20190715044752-607ca053a137/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c h1:vTxShRUnK60yd8DZU+f95p1zSLj814+5CuEh7NjF2/Y=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-2019.2.1 h1:fW1wbZIKRbRK56ETe5SYloH5SdLzhXOFet2KlpRKDqg=
honnef.co/go/tools v0.0.0-2019.2.1/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@ -1,25 +1,49 @@
package types
package gonextcloud
//Client is the main client interface
import (
"io"
"os"
"path/filepath"
)
// NewClient create a new client
func NewClient(hostname string) (Client, error) {
return newClient(hostname)
}
// Client is the main client interface
type Client interface {
// Nextcloud Apps client
Apps() Apps
// Nextcloud App Config client
AppsConfig() AppsConfig
// Nextcloud Group Folders client
GroupFolders() GroupFolders
// Nextcloud Notifications client
Notifications() Notifications
// Nextcloud Shares client
Shares() Shares
// Nextcloud Users client
Users() Users
// Nextcloud Groups client
Groups() Groups
// Nextcloud WebDav (files) client
WebDav() WebDav
// Nextcloud Monitoring client
Monitoring() (*Monitoring, error)
// Login authorize client
Login(username string, password string) error
// Logout clear connetion and session
Logout() error
}
// Auth is the standard auth methods
type Auth interface {
Login(username string, password string) error
Logout() error
}
//Apps available methods
// Apps available methods
type Apps interface {
List() ([]string, error)
ListEnabled() ([]string, error)
@ -29,7 +53,7 @@ type Apps interface {
Disable(name string) error
}
//AppsConfig available methods
// AppsConfig available methods
type AppsConfig interface {
List() (apps []string, err error)
Keys(id string) (keys []string, err error)
@ -40,7 +64,7 @@ type AppsConfig interface {
Details(appID string) (map[string]string, error)
}
//Groups available methods
// Groups available methods
type Groups interface {
List() ([]string, error)
ListDetails(search string) ([]Group, error)
@ -51,7 +75,7 @@ type Groups interface {
SubAdminList(name string) ([]string, error)
}
//GroupFolders available methods
// GroupFolders available methods
type GroupFolders interface {
List() (map[int]GroupFolder, error)
Get(id int) (GroupFolder, error)
@ -63,7 +87,7 @@ type GroupFolders interface {
SetQuota(folderID int, quota int) error
}
//Notifications available methods
// Notifications available methods
type Notifications interface {
List() ([]Notification, error)
Get(id int) (Notification, error)
@ -74,7 +98,7 @@ type Notifications interface {
Available() error
}
//Shares available methods
// Shares available methods
type Shares interface {
List() ([]Share, error)
GetFromPath(path string, reshares bool, subfiles bool) ([]Share, error)
@ -95,7 +119,7 @@ type Shares interface {
UpdatePermissions(shareID int, permissions SharePermission) error
}
//Users available methods
// Users available methods
type Users interface {
List() ([]string, error)
ListDetails() (map[string]UserDetails, error)
@ -124,3 +148,35 @@ type Users interface {
GroupDemote(name string, group string) error
GroupSubAdminList(name string) ([]string, error)
}
// WebDav available methods
type WebDav interface {
// ReadDir reads the contents of a remote directory
ReadDir(path string) ([]os.FileInfo, error)
// Stat returns the file stats for a specified path
Stat(path string) (os.FileInfo, error)
// Remove removes a remote file
Remove(path string) error
// RemoveAll removes remote files
RemoveAll(path string) error
// Mkdir makes a directory
Mkdir(path string, _ os.FileMode) error
// MkdirAll like mkdir -p, but for webdav
MkdirAll(path string, _ os.FileMode) error
// Rename moves a file from A to B
Rename(oldpath, newpath string, overwrite bool) error
// Copy copies a file from A to B
Copy(oldpath, newpath string, overwrite bool) error
// Read reads the contents of a remote file
Read(path string) ([]byte, error)
// ReadStream reads the stream for a given path
ReadStream(path string) (io.ReadCloser, error)
// Write writes data to a given path
Write(path string, data []byte, _ os.FileMode) error
// WriteStream writes a stream
WriteStream(path string, stream io.Reader, _ os.FileMode) error
// Walk walks the file tree rooted at root, calling walkFn for each file or
// directory in the tree, including root.
Walk(path string, walkFunc filepath.WalkFunc) error
}

View File

@ -2,9 +2,6 @@ package gonextcloud
import (
"fmt"
"github.com/stretchr/testify/assert"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"gopkg.in/yaml.v2"
"io/ioutil"
"math/rand"
"net/http"
@ -15,6 +12,9 @@ import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
)
type Config struct {
@ -35,7 +35,7 @@ type test = func(t *testing.T)
var (
config = Config{}
c *Client
c *client
groupID = 37
provisionningTests = []struct {
string
@ -52,7 +52,7 @@ var (
"create client",
func(t *testing.T) {
var err error
c, err = NewClient(config.URL)
c, err = newClient(config.URL)
assert.NoError(t, err, "aie")
},
},
@ -131,7 +131,7 @@ var (
// return
// }
// username := fmt.Sprintf("%s-2", config.NotExistingUser)
// user := &types.Users{
// user := &types.users{
// ID: username,
// Displayname: strings.ToUpper(username),
// Email: "some@address.com",
@ -140,9 +140,9 @@ var (
// Phone: "42 42 242 424",
// Website: "my.site.com",
// }
// err := c.Users().Create(username, password, user)
// err := c.users().Create(username, password, user)
// assert.NoError(t, err)
// u, err := c.Users().Get(username)
// u, err := c.users().Get(username)
// assert.NoError(t, err)
// o := structs.Map(user)
// r := structs.Map(u)
@ -153,7 +153,7 @@ var (
// assert.Equal(t, o[k], r[k])
// }
// // Clean up
// err = c.Users().Delete(u.ID)
// err = c.users().Delete(u.ID)
// assert.NoError(t, err)
// },
//},
@ -283,8 +283,8 @@ var (
quota := int64(1024 * 1024 * 1024)
err := c.Users().UpdateQuota(config.NotExistingUser, quota)
assert.NoError(t, err)
// TODO : Find better verification : A never connected Users does not have quota available
//u, err := c.Users(config.NotExistingUser)
// TODO : Find better verification : A never connected users does not have quota available
//u, err := c.users(config.NotExistingUser)
//assert.NoError(t, err)
//assert.Equal(t, quota, u.Quota.Quota)
},
@ -407,15 +407,15 @@ var (
{
"TestLoggedIn",
func(t *testing.T) {
c := &Client{}
c.capabilities = &types.Capabilities{}
c := &client{}
c.capabilities = &Capabilities{}
assert.False(t, c.loggedIn())
},
},
{
"TestLoginInvalidURL",
func(t *testing.T) {
c, _ = NewClient("")
c, _ = newClient("")
err := c.Login("", "")
assert.Error(t, err)
},
@ -423,7 +423,7 @@ var (
{
"TestBaseRequest",
func(t *testing.T) {
c, _ = NewClient("")
c, _ = newClient("")
_, err := c.baseRequest(http.MethodGet, routes.capabilities, nil, "admin", "invalid")
assert.Error(t, err)
},
@ -463,10 +463,10 @@ func TestUserCreateBatchWithoutPassword(t *testing.T) {
if c.version.Major < 14 {
t.SkipNow()
}
var us []types.User
var us []User
for i := 0; i < 5; i++ {
u := fmt.Sprintf(config.NotExistingUser+"_%d", i)
us = append(us, types.User{
us = append(us, User{
Username: u,
DisplayName: strings.Title(u),
Groups: []string{"admin"},
@ -547,7 +547,7 @@ func initClient() error {
return err
}
var err error
c, err = NewClient(config.URL)
c, err = newClient(config.URL)
if err != nil {
return err
}

View File

@ -1,6 +1,6 @@
package types
package gonextcloud
//Group
// Group is a Nextcloud group
type Group struct {
ID string `json:"id"`
Displayname string `json:"displayname"`

View File

@ -1,140 +1,58 @@
package gonextcloud
import (
"fmt"
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
"strconv"
)
import "strconv"
//GroupFolders contains all Groups Folders available actions
type GroupFolders struct {
c *Client
type groupFolderBadFormatIDAndGroups struct {
ID string `json:"id"`
MountPoint string `json:"mount_point"`
Groups map[string]string `json:"groups"`
Quota string `json:"quota"`
Size int `json:"size"`
}
//List returns the groups folders
func (g *GroupFolders) List() (map[int]types.GroupFolder, error) {
res, err := g.c.baseRequest(http.MethodGet, routes.groupfolders, nil)
if err != nil {
return nil, err
}
var r types.GroupFoldersListResponse
res.JSON(&r)
gfs := formatBadIDAndGroups(r.Ocs.Data)
return gfs, nil
type groupFolderBadFormatGroups struct {
ID int `json:"id"`
MountPoint string `json:"mount_point"`
Groups map[string]string `json:"groups"`
Quota string `json:"quota"`
Size int `json:"size"`
}
//Get returns the group folder details
func (g *GroupFolders) Get(id int) (types.GroupFolder, error) {
res, err := g.c.baseRequest(http.MethodGet, routes.groupfolders, nil, strconv.Itoa(id))
if err != nil {
return types.GroupFolder{}, err
}
var r types.GroupFoldersResponse
res.JSON(&r)
if r.Ocs.Data.ID == 0 {
return types.GroupFolder{}, fmt.Errorf("%d is not a valid groupfolder's id", id)
}
return r.Ocs.Data.FormatGroupFolder(), nil
// GroupFolder is group shared folder from groupfolders application
type GroupFolder struct {
ID int `json:"id"`
MountPoint string `json:"mount_point"`
Groups map[string]SharePermission `json:"groups"`
Quota int `json:"quota"`
Size int `json:"size"`
}
//Create creates a group folder
func (g *GroupFolders) Create(name string) (id int, err error) {
// TODO: Validate Folder name
ro := &req.RequestOptions{
Data: map[string]string{
"mountpoint": name,
},
func (gf *groupFolderBadFormatGroups) FormatGroupFolder() GroupFolder {
g := GroupFolder{}
g.ID = gf.ID
g.MountPoint = gf.MountPoint
g.Groups = map[string]SharePermission{}
for k, v := range gf.Groups {
p, _ := strconv.Atoi(v)
g.Groups[k] = SharePermission(p)
}
res, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro)
if err != nil {
return 0, err
}
var r types.GroupFoldersCreateResponse
res.JSON(&r)
id, _ = strconv.Atoi(r.Ocs.Data.ID)
return id, nil
q, _ := strconv.Atoi(gf.Quota)
g.Quota = q
g.Size = gf.Size
return g
}
//Rename renames the group folder
func (g *GroupFolders) Rename(groupID int, name string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"mountpoint": name,
},
func (gf *groupFolderBadFormatIDAndGroups) FormatGroupFolder() GroupFolder {
g := GroupFolder{}
g.ID, _ = strconv.Atoi(gf.ID)
g.MountPoint = gf.MountPoint
g.Groups = map[string]SharePermission{}
for k, v := range gf.Groups {
p, _ := strconv.Atoi(v)
g.Groups[k] = SharePermission(p)
}
// GroupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(groupID), "mountpoint")
if err != nil {
return err
}
return nil
}
//TODO func (c *Client) GroupFoldersDelete(id int) error {
//AddGroup adds group to folder
func (g *GroupFolders) AddGroup(folderID int, groupName string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"group": groupName,
},
}
// GroupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(folderID), "groups")
if err != nil {
return err
}
return nil
}
//RemoveGroup remove a group from the group folder
func (g *GroupFolders) RemoveGroup(folderID int, groupName string) error {
// GroupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodDelete, routes.groupfolders, nil, strconv.Itoa(folderID), "groups", groupName)
if err != nil {
return err
}
return nil
}
//SetGroupPermissions set groups permissions
func (g *GroupFolders) SetGroupPermissions(folderID int, groupName string, permission types.SharePermission) error {
ro := &req.RequestOptions{
Data: map[string]string{
"permissions": strconv.Itoa(int(permission)),
},
}
// GroupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(folderID), "groups", groupName)
if err != nil {
return err
}
return nil
}
//SetQuota set quota on the group folder. quota in bytes, use -3 for unlimited
func (g *GroupFolders) SetQuota(folderID int, quota int) error {
ro := &req.RequestOptions{
Data: map[string]string{
"quota": strconv.Itoa(int(quota)),
},
}
// GroupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(folderID), "quota")
if err != nil {
return err
}
return nil
}
func formatBadIDAndGroups(g map[string]types.GroupFolderBadFormatIDAndGroups) map[int]types.GroupFolder {
var gfs = map[int]types.GroupFolder{}
for k := range g {
i, _ := strconv.Atoi(k)
d := g[k]
gfs[i] = d.FormatGroupFolder()
}
return gfs
q, _ := strconv.Atoi(gf.Quota)
g.Quota = q
g.Size = gf.Size
return g
}

140
groupfolders_impl.go Normal file
View File

@ -0,0 +1,140 @@
package gonextcloud
import (
"fmt"
"net/http"
"strconv"
req "github.com/levigross/grequests"
)
//groupFolders contains all groups Folders available actions
type groupFolders struct {
c *client
}
//List returns the groups folders
func (g *groupFolders) List() (map[int]GroupFolder, error) {
res, err := g.c.baseRequest(http.MethodGet, routes.groupfolders, nil)
if err != nil {
return nil, err
}
var r groupFoldersListResponse
res.JSON(&r)
gfs := formatBadIDAndGroups(r.Ocs.Data)
return gfs, nil
}
//Get returns the group folder details
func (g *groupFolders) Get(id int) (GroupFolder, error) {
res, err := g.c.baseRequest(http.MethodGet, routes.groupfolders, nil, strconv.Itoa(id))
if err != nil {
return GroupFolder{}, err
}
var r groupFoldersResponse
res.JSON(&r)
if r.Ocs.Data.ID == 0 {
return GroupFolder{}, fmt.Errorf("%d is not a valid groupfolder's id", id)
}
return r.Ocs.Data.FormatGroupFolder(), nil
}
//Create creates a group folder
func (g *groupFolders) Create(name string) (id int, err error) {
// TODO: Validate Folder name
ro := &req.RequestOptions{
Data: map[string]string{
"mountpoint": name,
},
}
res, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro)
if err != nil {
return 0, err
}
var r groupFoldersCreateResponse
res.JSON(&r)
id, _ = strconv.Atoi(r.Ocs.Data.ID)
return id, nil
}
//Rename renames the group folder
func (g *groupFolders) Rename(groupID int, name string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"mountpoint": name,
},
}
// groupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(groupID), "mountpoint")
if err != nil {
return err
}
return nil
}
//TODO func (c *client) GroupFoldersDelete(id int) error {
//AddGroup adds group to folder
func (g *groupFolders) AddGroup(folderID int, groupName string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"group": groupName,
},
}
// groupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(folderID), "groups")
if err != nil {
return err
}
return nil
}
//RemoveGroup remove a group from the group folder
func (g *groupFolders) RemoveGroup(folderID int, groupName string) error {
// groupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodDelete, routes.groupfolders, nil, strconv.Itoa(folderID), "groups", groupName)
if err != nil {
return err
}
return nil
}
//SetGroupPermissions set groups permissions
func (g *groupFolders) SetGroupPermissions(folderID int, groupName string, permission SharePermission) error {
ro := &req.RequestOptions{
Data: map[string]string{
"permissions": strconv.Itoa(int(permission)),
},
}
// groupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(folderID), "groups", groupName)
if err != nil {
return err
}
return nil
}
//SetQuota set quota on the group folder. quota in bytes, use -3 for unlimited
func (g *groupFolders) SetQuota(folderID int, quota int) error {
ro := &req.RequestOptions{
Data: map[string]string{
"quota": strconv.Itoa(int(quota)),
},
}
// groupFolders's response does not give any clues about success or failure
_, err := g.c.baseRequest(http.MethodPost, routes.groupfolders, ro, strconv.Itoa(folderID), "quota")
if err != nil {
return err
}
return nil
}
func formatBadIDAndGroups(g map[string]groupFolderBadFormatIDAndGroups) map[int]GroupFolder {
var gfs = map[int]GroupFolder{}
for k := range g {
i, _ := strconv.Atoi(k)
d := g[k]
gfs[i] = d.FormatGroupFolder()
}
return gfs
}

View File

@ -1,9 +1,9 @@
package gonextcloud
import (
"github.com/stretchr/testify/assert"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"testing"
"github.com/stretchr/testify/assert"
)
var (
@ -53,7 +53,7 @@ var (
{
"TestGroupFoldersSetGroupPermissions",
func(t *testing.T) {
err := c.GroupFolders().SetGroupPermissions(groupID, "admin", types.ReadPermission)
err := c.GroupFolders().SetGroupPermissions(groupID, "admin", ReadPermission)
assert.NoError(t, err)
},
},

View File

@ -1,29 +1,29 @@
package gonextcloud
import (
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
req "github.com/levigross/grequests"
)
//Groups contains all Groups available actions
type Groups struct {
c *Client
//groups contains all groups available actions
type groups struct {
c *client
}
//List lists the Nextcloud groups
func (g *Groups) List() ([]string, error) {
func (g *groups) List() ([]string, error) {
res, err := g.c.baseRequest(http.MethodGet, routes.groups, nil)
if err != nil {
return nil, err
}
var r types.GroupListResponse
var r groupListResponse
res.JSON(&r)
return r.Ocs.Data.Groups, nil
}
//ListDetails lists the Nextcloud groups
func (g *Groups) ListDetails(search string) ([]types.Group, error) {
func (g *groups) ListDetails(search string) ([]Group, error) {
ro := &req.RequestOptions{
Params: map[string]string{
"search": search,
@ -33,24 +33,24 @@ func (g *Groups) ListDetails(search string) ([]types.Group, error) {
if err != nil {
return nil, err
}
var r types.GroupListDetailsResponse
var r groupListDetailsResponse
res.JSON(&r)
return r.Ocs.Data.Groups, nil
}
//Users list the group's users
func (g *Groups) Users(name string) ([]string, error) {
//users list the group's users
func (g *groups) Users(name string) ([]string, error) {
res, err := g.c.baseRequest(http.MethodGet, routes.groups, nil, name)
if err != nil {
return nil, err
}
var r types.UserListResponse
var r userListResponse
res.JSON(&r)
return r.Ocs.Data.Users, nil
}
//Search return the list of groups matching the search string
func (g *Groups) Search(search string) ([]string, error) {
func (g *groups) Search(search string) ([]string, error) {
ro := &req.RequestOptions{
Params: map[string]string{"search": search},
}
@ -58,13 +58,13 @@ func (g *Groups) Search(search string) ([]string, error) {
if err != nil {
return nil, err
}
var r types.GroupListResponse
var r groupListResponse
res.JSON(&r)
return r.Ocs.Data.Groups, nil
}
//Create creates a group
func (g *Groups) Create(name string) error {
func (g *groups) Create(name string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"groupid": name,
@ -74,22 +74,22 @@ func (g *Groups) Create(name string) error {
}
//Delete deletes the group
func (g *Groups) Delete(name string) error {
func (g *groups) Delete(name string) error {
return g.baseRequest(http.MethodDelete, nil, name)
}
//SubAdminList lists the group's subadmins
func (g *Groups) SubAdminList(name string) ([]string, error) {
func (g *groups) SubAdminList(name string) ([]string, error) {
res, err := g.c.baseRequest(http.MethodGet, routes.groups, nil, name, "subadmins")
if err != nil {
return nil, err
}
var r types.UserListResponse
var r userListResponse
res.JSON(&r)
return r.Ocs.Data.Users, nil
}
func (g *Groups) baseRequest(method string, ro *req.RequestOptions, subRoute ...string) error {
func (g *groups) baseRequest(method string, ro *req.RequestOptions, subRoute ...string) error {
_, err := g.c.baseRequest(method, routes.groups, ro, subRoute...)
return err
}

View File

@ -2,8 +2,11 @@
package mocks
import mock "github.com/stretchr/testify/mock"
import types "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
import (
"github.com/stretchr/testify/mock"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud"
)
// Apps is an autogenerated mock type for the Apps type
type Apps struct {
@ -39,14 +42,14 @@ func (_m *Apps) Enable(name string) error {
}
// Infos provides a mock function with given fields: name
func (_m *Apps) Infos(name string) (types.App, error) {
func (_m *Apps) Infos(name string) (gonextcloud.App, error) {
ret := _m.Called(name)
var r0 types.App
if rf, ok := ret.Get(0).(func(string) types.App); ok {
var r0 gonextcloud.App
if rf, ok := ret.Get(0).(func(string) gonextcloud.App); ok {
r0 = rf(name)
} else {
r0 = ret.Get(0).(types.App)
r0 = ret.Get(0).(gonextcloud.App)
}
var r1 error

View File

@ -2,8 +2,11 @@
package mocks
import mock "github.com/stretchr/testify/mock"
import types "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
import (
"github.com/stretchr/testify/mock"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud"
)
// Client is an autogenerated mock type for the Client type
type Client struct {
@ -11,15 +14,15 @@ type Client struct {
}
// Apps provides a mock function with given fields:
func (_m *Client) Apps() types.Apps {
func (_m *Client) Apps() gonextcloud.Apps {
ret := _m.Called()
var r0 types.Apps
if rf, ok := ret.Get(0).(func() types.Apps); ok {
var r0 gonextcloud.Apps
if rf, ok := ret.Get(0).(func() gonextcloud.Apps); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.Apps)
r0 = ret.Get(0).(gonextcloud.Apps)
}
}
@ -27,15 +30,15 @@ func (_m *Client) Apps() types.Apps {
}
// AppsConfig provides a mock function with given fields:
func (_m *Client) AppsConfig() types.AppsConfig {
func (_m *Client) AppsConfig() gonextcloud.AppsConfig {
ret := _m.Called()
var r0 types.AppsConfig
if rf, ok := ret.Get(0).(func() types.AppsConfig); ok {
var r0 gonextcloud.AppsConfig
if rf, ok := ret.Get(0).(func() gonextcloud.AppsConfig); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.AppsConfig)
r0 = ret.Get(0).(gonextcloud.AppsConfig)
}
}
@ -43,15 +46,15 @@ func (_m *Client) AppsConfig() types.AppsConfig {
}
// GroupFolders provides a mock function with given fields:
func (_m *Client) GroupFolders() types.GroupFolders {
func (_m *Client) GroupFolders() gonextcloud.GroupFolders {
ret := _m.Called()
var r0 types.GroupFolders
if rf, ok := ret.Get(0).(func() types.GroupFolders); ok {
var r0 gonextcloud.GroupFolders
if rf, ok := ret.Get(0).(func() gonextcloud.GroupFolders); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.GroupFolders)
r0 = ret.Get(0).(gonextcloud.GroupFolders)
}
}
@ -59,15 +62,15 @@ func (_m *Client) GroupFolders() types.GroupFolders {
}
// Groups provides a mock function with given fields:
func (_m *Client) Groups() types.Groups {
func (_m *Client) Groups() gonextcloud.Groups {
ret := _m.Called()
var r0 types.Groups
if rf, ok := ret.Get(0).(func() types.Groups); ok {
var r0 gonextcloud.Groups
if rf, ok := ret.Get(0).(func() gonextcloud.Groups); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.Groups)
r0 = ret.Get(0).(gonextcloud.Groups)
}
}
@ -103,15 +106,15 @@ func (_m *Client) Logout() error {
}
// Notifications provides a mock function with given fields:
func (_m *Client) Notifications() types.Notifications {
func (_m *Client) Notifications() gonextcloud.Notifications {
ret := _m.Called()
var r0 types.Notifications
if rf, ok := ret.Get(0).(func() types.Notifications); ok {
var r0 gonextcloud.Notifications
if rf, ok := ret.Get(0).(func() gonextcloud.Notifications); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.Notifications)
r0 = ret.Get(0).(gonextcloud.Notifications)
}
}
@ -119,15 +122,15 @@ func (_m *Client) Notifications() types.Notifications {
}
// Shares provides a mock function with given fields:
func (_m *Client) Shares() types.Shares {
func (_m *Client) Shares() gonextcloud.Shares {
ret := _m.Called()
var r0 types.Shares
if rf, ok := ret.Get(0).(func() types.Shares); ok {
var r0 gonextcloud.Shares
if rf, ok := ret.Get(0).(func() gonextcloud.Shares); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.Shares)
r0 = ret.Get(0).(gonextcloud.Shares)
}
}
@ -135,15 +138,15 @@ func (_m *Client) Shares() types.Shares {
}
// Users provides a mock function with given fields:
func (_m *Client) Users() types.Users {
func (_m *Client) Users() gonextcloud.Users {
ret := _m.Called()
var r0 types.Users
if rf, ok := ret.Get(0).(func() types.Users); ok {
var r0 gonextcloud.Users
if rf, ok := ret.Get(0).(func() gonextcloud.Users); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(types.Users)
r0 = ret.Get(0).(gonextcloud.Users)
}
}

View File

@ -2,8 +2,11 @@
package mocks
import mock "github.com/stretchr/testify/mock"
import types "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
import (
"github.com/stretchr/testify/mock"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud"
)
// GroupFolders is an autogenerated mock type for the GroupFolders type
type GroupFolders struct {
@ -46,14 +49,14 @@ func (_m *GroupFolders) Create(name string) (int, error) {
}
// Get provides a mock function with given fields: id
func (_m *GroupFolders) Get(id int) (types.GroupFolder, error) {
func (_m *GroupFolders) Get(id int) (gonextcloud.GroupFolder, error) {
ret := _m.Called(id)
var r0 types.GroupFolder
if rf, ok := ret.Get(0).(func(int) types.GroupFolder); ok {
var r0 gonextcloud.GroupFolder
if rf, ok := ret.Get(0).(func(int) gonextcloud.GroupFolder); ok {
r0 = rf(id)
} else {
r0 = ret.Get(0).(types.GroupFolder)
r0 = ret.Get(0).(gonextcloud.GroupFolder)
}
var r1 error
@ -67,15 +70,15 @@ func (_m *GroupFolders) Get(id int) (types.GroupFolder, error) {
}
// List provides a mock function with given fields:
func (_m *GroupFolders) List() (map[int]types.GroupFolder, error) {
func (_m *GroupFolders) List() (map[int]gonextcloud.GroupFolder, error) {
ret := _m.Called()
var r0 map[int]types.GroupFolder
if rf, ok := ret.Get(0).(func() map[int]types.GroupFolder); ok {
var r0 map[int]gonextcloud.GroupFolder
if rf, ok := ret.Get(0).(func() map[int]gonextcloud.GroupFolder); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[int]types.GroupFolder)
r0 = ret.Get(0).(map[int]gonextcloud.GroupFolder)
}
}
@ -118,11 +121,11 @@ func (_m *GroupFolders) Rename(groupID int, name string) error {
}
// SetGroupPermissions provides a mock function with given fields: folderID, groupName, permission
func (_m *GroupFolders) SetGroupPermissions(folderID int, groupName string, permission types.SharePermission) error {
func (_m *GroupFolders) SetGroupPermissions(folderID int, groupName string, permission gonextcloud.SharePermission) error {
ret := _m.Called(folderID, groupName, permission)
var r0 error
if rf, ok := ret.Get(0).(func(int, string, types.SharePermission) error); ok {
if rf, ok := ret.Get(0).(func(int, string, gonextcloud.SharePermission) error); ok {
r0 = rf(folderID, groupName, permission)
} else {
r0 = ret.Error(0)

View File

@ -2,8 +2,11 @@
package mocks
import mock "github.com/stretchr/testify/mock"
import types "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
import (
"github.com/stretchr/testify/mock"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud"
)
// Groups is an autogenerated mock type for the Groups type
type Groups struct {
@ -62,15 +65,15 @@ func (_m *Groups) List() ([]string, error) {
}
// ListDetails provides a mock function with given fields: search
func (_m *Groups) ListDetails(search string) ([]types.Group, error) {
func (_m *Groups) ListDetails(search string) ([]gonextcloud.Group, error) {
ret := _m.Called(search)
var r0 []types.Group
if rf, ok := ret.Get(0).(func(string) []types.Group); ok {
var r0 []gonextcloud.Group
if rf, ok := ret.Get(0).(func(string) []gonextcloud.Group); ok {
r0 = rf(search)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]types.Group)
r0 = ret.Get(0).([]gonextcloud.Group)
}
}

View File

@ -2,8 +2,11 @@
package mocks
import mock "github.com/stretchr/testify/mock"
import types "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
import (
"github.com/stretchr/testify/mock"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud"
)
// Notifications is an autogenerated mock type for the Notifications type
type Notifications struct {
@ -81,14 +84,14 @@ func (_m *Notifications) DeleteAll() error {
}
// Get provides a mock function with given fields: id
func (_m *Notifications) Get(id int) (types.Notification, error) {
func (_m *Notifications) Get(id int) (gonextcloud.Notification, error) {
ret := _m.Called(id)
var r0 types.Notification
if rf, ok := ret.Get(0).(func(int) types.Notification); ok {
var r0 gonextcloud.Notification
if rf, ok := ret.Get(0).(func(int) gonextcloud.Notification); ok {
r0 = rf(id)
} else {
r0 = ret.Get(0).(types.Notification)
r0 = ret.Get(0).(gonextcloud.Notification)
}
var r1 error
@ -102,15 +105,15 @@ func (_m *Notifications) Get(id int) (types.Notification, error) {
}
// List provides a mock function with given fields:
func (_m *Notifications) List() ([]types.Notification, error) {
func (_m *Notifications) List() ([]gonextcloud.Notification, error) {
ret := _m.Called()
var r0 []types.Notification
if rf, ok := ret.Get(0).(func() []types.Notification); ok {
var r0 []gonextcloud.Notification
if rf, ok := ret.Get(0).(func() []gonextcloud.Notification); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]types.Notification)
r0 = ret.Get(0).([]gonextcloud.Notification)
}
}

View File

@ -2,8 +2,11 @@
package mocks
import mock "github.com/stretchr/testify/mock"
import types "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
import (
"github.com/stretchr/testify/mock"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud"
)
// Shares is an autogenerated mock type for the Shares type
type Shares struct {
@ -11,18 +14,18 @@ type Shares struct {
}
// Create provides a mock function with given fields: path, shareType, permission, shareWith, publicUpload, password
func (_m *Shares) Create(path string, shareType types.ShareType, permission types.SharePermission, shareWith string, publicUpload bool, password string) (types.Share, error) {
func (_m *Shares) Create(path string, shareType gonextcloud.ShareType, permission gonextcloud.SharePermission, shareWith string, publicUpload bool, password string) (gonextcloud.Share, error) {
ret := _m.Called(path, shareType, permission, shareWith, publicUpload, password)
var r0 types.Share
if rf, ok := ret.Get(0).(func(string, types.ShareType, types.SharePermission, string, bool, string) types.Share); ok {
var r0 gonextcloud.Share
if rf, ok := ret.Get(0).(func(string, gonextcloud.ShareType, gonextcloud.SharePermission, string, bool, string) gonextcloud.Share); ok {
r0 = rf(path, shareType, permission, shareWith, publicUpload, password)
} else {
r0 = ret.Get(0).(types.Share)
r0 = ret.Get(0).(gonextcloud.Share)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, types.ShareType, types.SharePermission, string, bool, string) error); ok {
if rf, ok := ret.Get(1).(func(string, gonextcloud.ShareType, gonextcloud.SharePermission, string, bool, string) error); ok {
r1 = rf(path, shareType, permission, shareWith, publicUpload, password)
} else {
r1 = ret.Error(1)
@ -46,14 +49,14 @@ func (_m *Shares) Delete(shareID int) error {
}
// Get provides a mock function with given fields: shareID
func (_m *Shares) Get(shareID string) (types.Share, error) {
func (_m *Shares) Get(shareID string) (gonextcloud.Share, error) {
ret := _m.Called(shareID)
var r0 types.Share
if rf, ok := ret.Get(0).(func(string) types.Share); ok {
var r0 gonextcloud.Share
if rf, ok := ret.Get(0).(func(string) gonextcloud.Share); ok {
r0 = rf(shareID)
} else {
r0 = ret.Get(0).(types.Share)
r0 = ret.Get(0).(gonextcloud.Share)
}
var r1 error
@ -67,15 +70,15 @@ func (_m *Shares) Get(shareID string) (types.Share, error) {
}
// GetFromPath provides a mock function with given fields: path, reshares, subfiles
func (_m *Shares) GetFromPath(path string, reshares bool, subfiles bool) ([]types.Share, error) {
func (_m *Shares) GetFromPath(path string, reshares bool, subfiles bool) ([]gonextcloud.Share, error) {
ret := _m.Called(path, reshares, subfiles)
var r0 []types.Share
if rf, ok := ret.Get(0).(func(string, bool, bool) []types.Share); ok {
var r0 []gonextcloud.Share
if rf, ok := ret.Get(0).(func(string, bool, bool) []gonextcloud.Share); ok {
r0 = rf(path, reshares, subfiles)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]types.Share)
r0 = ret.Get(0).([]gonextcloud.Share)
}
}
@ -90,15 +93,15 @@ func (_m *Shares) GetFromPath(path string, reshares bool, subfiles bool) ([]type
}
// List provides a mock function with given fields:
func (_m *Shares) List() ([]types.Share, error) {
func (_m *Shares) List() ([]gonextcloud.Share, error) {
ret := _m.Called()
var r0 []types.Share
if rf, ok := ret.Get(0).(func() []types.Share); ok {
var r0 []gonextcloud.Share
if rf, ok := ret.Get(0).(func() []gonextcloud.Share); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]types.Share)
r0 = ret.Get(0).([]gonextcloud.Share)
}
}
@ -113,11 +116,11 @@ func (_m *Shares) List() ([]types.Share, error) {
}
// Update provides a mock function with given fields: shareUpdate
func (_m *Shares) Update(shareUpdate types.ShareUpdate) error {
func (_m *Shares) Update(shareUpdate gonextcloud.ShareUpdate) error {
ret := _m.Called(shareUpdate)
var r0 error
if rf, ok := ret.Get(0).(func(types.ShareUpdate) error); ok {
if rf, ok := ret.Get(0).(func(gonextcloud.ShareUpdate) error); ok {
r0 = rf(shareUpdate)
} else {
r0 = ret.Error(0)
@ -155,11 +158,11 @@ func (_m *Shares) UpdatePassword(shareID int, password string) error {
}
// UpdatePermissions provides a mock function with given fields: shareID, permissions
func (_m *Shares) UpdatePermissions(shareID int, permissions types.SharePermission) error {
func (_m *Shares) UpdatePermissions(shareID int, permissions gonextcloud.SharePermission) error {
ret := _m.Called(shareID, permissions)
var r0 error
if rf, ok := ret.Get(0).(func(int, types.SharePermission) error); ok {
if rf, ok := ret.Get(0).(func(int, gonextcloud.SharePermission) error); ok {
r0 = rf(shareID, permissions)
} else {
r0 = ret.Error(0)

View File

@ -2,8 +2,11 @@
package mocks
import mock "github.com/stretchr/testify/mock"
import types "gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
import (
"github.com/stretchr/testify/mock"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud"
)
// Users is an autogenerated mock type for the Users type
type Users struct {
@ -11,11 +14,11 @@ type Users struct {
}
// Create provides a mock function with given fields: username, password, user
func (_m *Users) Create(username string, password string, user *types.UserDetails) error {
func (_m *Users) Create(username string, password string, user *gonextcloud.UserDetails) error {
ret := _m.Called(username, password, user)
var r0 error
if rf, ok := ret.Get(0).(func(string, string, *types.UserDetails) error); ok {
if rf, ok := ret.Get(0).(func(string, string, *gonextcloud.UserDetails) error); ok {
r0 = rf(username, password, user)
} else {
r0 = ret.Error(0)
@ -25,11 +28,11 @@ func (_m *Users) Create(username string, password string, user *types.UserDetail
}
// CreateBatchWithoutPassword provides a mock function with given fields: users
func (_m *Users) CreateBatchWithoutPassword(users []types.User) error {
func (_m *Users) CreateBatchWithoutPassword(users []gonextcloud.User) error {
ret := _m.Called(users)
var r0 error
if rf, ok := ret.Get(0).(func([]types.User) error); ok {
if rf, ok := ret.Get(0).(func([]gonextcloud.User) error); ok {
r0 = rf(users)
} else {
r0 = ret.Error(0)
@ -102,15 +105,15 @@ func (_m *Users) Enable(name string) error {
}
// Get provides a mock function with given fields: name
func (_m *Users) Get(name string) (*types.UserDetails, error) {
func (_m *Users) Get(name string) (*gonextcloud.UserDetails, error) {
ret := _m.Called(name)
var r0 *types.UserDetails
if rf, ok := ret.Get(0).(func(string) *types.UserDetails); ok {
var r0 *gonextcloud.UserDetails
if rf, ok := ret.Get(0).(func(string) *gonextcloud.UserDetails); ok {
r0 = rf(name)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.UserDetails)
r0 = ret.Get(0).(*gonextcloud.UserDetails)
}
}
@ -250,15 +253,15 @@ func (_m *Users) List() ([]string, error) {
}
// ListDetails provides a mock function with given fields:
func (_m *Users) ListDetails() (map[string]types.UserDetails, error) {
func (_m *Users) ListDetails() (map[string]gonextcloud.UserDetails, error) {
ret := _m.Called()
var r0 map[string]types.UserDetails
if rf, ok := ret.Get(0).(func() map[string]types.UserDetails); ok {
var r0 map[string]gonextcloud.UserDetails
if rf, ok := ret.Get(0).(func() map[string]gonextcloud.UserDetails); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]types.UserDetails)
r0 = ret.Get(0).(map[string]gonextcloud.UserDetails)
}
}
@ -310,11 +313,11 @@ func (_m *Users) SendWelcomeEmail(name string) error {
}
// Update provides a mock function with given fields: user
func (_m *Users) Update(user *types.UserDetails) error {
func (_m *Users) Update(user *gonextcloud.UserDetails) error {
ret := _m.Called(user)
var r0 error
if rf, ok := ret.Get(0).(func(*types.UserDetails) error); ok {
if rf, ok := ret.Get(0).(func(*gonextcloud.UserDetails) error); ok {
r0 = rf(user)
} else {
r0 = ret.Error(0)

View File

@ -1,17 +1,72 @@
package gonextcloud
import (
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
)
//Monitoring return nextcloud monitoring statistics
func (c *Client) Monitoring() (*types.Monitoring, error) {
res, err := c.baseRequest(http.MethodGet, routes.monitor, nil)
if err != nil {
return nil, err
}
var m types.MonitoringResponse
res.JSON(&m)
return &m.Ocs.Data, nil
// System contains the operating system statistics
type System struct {
Version string `json:"version"`
Theme string `json:"theme"`
EnableAvatars string `json:"enable_avatars"`
EnablePreviews string `json:"enable_previews"`
MemcacheLocal string `json:"memcache.local"`
MemcacheDistributed string `json:"memcache.distributed"`
FilelockingEnabled string `json:"filelocking.enabled"`
MemcacheLocking string `json:"memcache.locking"`
Debug string `json:"debug"`
Freespace int64 `json:"freespace"`
Cpuload []float32 `json:"cpuload"`
MemTotal int `json:"mem_total"`
MemFree int `json:"mem_free"`
SwapTotal int `json:"swap_total"`
SwapFree int `json:"swap_free"`
}
// Monitoring contains the nextcloud monitoring statistics
type Monitoring struct {
// Nextcloud Statistics
Nextcloud struct {
System System `json:"system"`
Storage Storage `json:"storage"`
Shares struct {
NumShares int `json:"num_shares"`
NumSharesUser int `json:"num_shares_user"`
NumSharesGroups int `json:"num_shares_groups"`
NumSharesLink int `json:"num_shares_link"`
NumSharesLinkNoPassword int `json:"num_shares_link_no_password"`
NumFedSharesSent int `json:"num_fed_shares_sent"`
NumFedSharesReceived int `json:"num_fed_shares_received"`
} `json:"shares"`
} `json:"nextcloud"`
// Server statistics
Server struct {
Webserver string `json:"webserver"`
Php struct {
Version string `json:"version"`
MemoryLimit int `json:"memory_limit"`
MaxExecutionTime int `json:"max_execution_time"`
UploadMaxFilesize int `json:"upload_max_filesize"`
} `json:"php"`
Database struct {
Type string `json:"type"`
Version string `json:"version"`
Size int `json:"size"`
} `json:"database"`
} `json:"server"`
// Active users statistics
ActiveUsers ActiveUsers `json:"activeUsers"`
}
// ActiveUsers contains the active users statistics
type ActiveUsers struct {
Last5Minutes int `json:"last5minutes"`
Last1Hour int `json:"last1hour"`
Last24Hours int `json:"last24hours"`
}
// Storage contains the storage statistics
type Storage struct {
NumUsers int `json:"num_users"`
NumFiles int `json:"num_files"`
NumStorages int `json:"num_storages"`
NumStoragesLocal int `json:"num_storages_local"`
NumStoragesHome int `json:"num_storages_home"`
NumStoragesOther int `json:"num_storages_other"`
}

16
monitoring_impl.go Normal file
View File

@ -0,0 +1,16 @@
package gonextcloud
import (
"net/http"
)
//Monitoring return nextcloud monitoring statistics
func (c *client) Monitoring() (*Monitoring, error) {
res, err := c.baseRequest(http.MethodGet, routes.monitor, nil)
if err != nil {
return nil, err
}
var m monitoringResponse
res.JSON(&m)
return &m.Ocs.Data, nil
}

View File

@ -1,7 +1,8 @@
package types
package gonextcloud
import "time"
// Notification is a nextcloud notification (from notification app)
type Notification struct {
NotificationID int `json:"notification_id"`
App string `json:"app"`

View File

@ -2,19 +2,19 @@ package gonextcloud
import (
"errors"
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
"strconv"
req "github.com/levigross/grequests"
)
//Notifications contains all Notifications available actions
type Notifications struct {
c *Client
//notifications contains all notifications available actions
type notifications struct {
c *client
}
//List returns all the notifications
func (n *Notifications) List() ([]types.Notification, error) {
func (n *notifications) List() ([]Notification, error) {
if err := n.Available(); err != nil {
return nil, err
}
@ -22,27 +22,27 @@ func (n *Notifications) List() ([]types.Notification, error) {
if err != nil {
return nil, err
}
var r types.NotificationsListResponse
var r notificationsListResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Get returns the notification corresponding to the id
func (n *Notifications) Get(id int) (types.Notification, error) {
func (n *notifications) Get(id int) (Notification, error) {
if err := n.Available(); err != nil {
return types.Notification{}, err
return Notification{}, err
}
res, err := n.c.baseRequest(http.MethodGet, routes.notifications, nil, strconv.Itoa(id))
if err != nil {
return types.Notification{}, err
return Notification{}, err
}
var r types.NotificationResponse
var r notificationResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Delete deletes the notification corresponding to the id
func (n *Notifications) Delete(id int) error {
func (n *notifications) Delete(id int) error {
if err := n.Available(); err != nil {
return err
}
@ -51,7 +51,7 @@ func (n *Notifications) Delete(id int) error {
}
//DeleteAll deletes all notifications
func (n *Notifications) DeleteAll() error {
func (n *notifications) DeleteAll() error {
if err := n.Available(); err != nil {
return err
}
@ -60,7 +60,7 @@ func (n *Notifications) DeleteAll() error {
}
//Create creates a notification (if the user is an admin)
func (n *Notifications) Create(userID, title, message string) error {
func (n *notifications) Create(userID, title, message string) error {
if err := n.AdminAvailable(); err != nil {
return err
}
@ -75,7 +75,7 @@ func (n *Notifications) Create(userID, title, message string) error {
}
//AdminAvailable returns an error if the admin-notifications app is not installed
func (n *Notifications) AdminAvailable() error {
func (n *notifications) AdminAvailable() error {
if len(n.c.capabilities.Notifications.AdminNotifications) == 0 {
return errors.New("'admin notifications' not available on this instance")
}
@ -83,7 +83,7 @@ func (n *Notifications) AdminAvailable() error {
}
//Available returns an error if the notifications app is not installed
func (n *Notifications) Available() error {
func (n *notifications) Available() error {
if len(n.c.capabilities.Notifications.OcsEndpoints) == 0 {
return errors.New("notifications not available on this instance")
}

View File

@ -1,7 +1,7 @@
package types
package gonextcloud
//Meta
type Meta struct {
//meta
type meta struct {
Status string `json:"status"`
Statuscode int `json:"statuscode"`
Message string `json:"message"`
@ -9,109 +9,109 @@ type Meta struct {
Itemsperpage string `json:"itemsperpage"`
}
//ErrorResponse
type ErrorResponse struct {
//errorResponse
type errorResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data []interface{} `json:"data"`
} `json:"ocs"`
}
//UserListResponse
type UserListResponse struct {
//userListResponse
type userListResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Users []string `json:"users"`
} `json:"data"`
} `json:"ocs"`
}
type UserListDetailsResponse struct {
type userListDetailsResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Users map[string]UserDetails `json:"users"`
} `json:"data"`
} `json:"ocs"`
}
//UserResponse
type UserResponse struct {
//userResponse
type userResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data UserDetails `json:"data"`
} `json:"ocs"`
}
//BaseResponse
type BaseResponse struct {
//baseResponse
type baseResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data []string `json:"data"`
} `json:"ocs"`
}
//GroupListResponse
type GroupListResponse struct {
//groupListResponse
type groupListResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Groups []string `json:"groups"`
} `json:"data"`
} `json:"ocs"`
}
//GroupListDetailsResponse
type GroupListDetailsResponse struct {
//groupListDetailsResponse
type groupListDetailsResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Groups []Group `json:"groups"`
} `json:"data"`
} `json:"ocs"`
}
//AppListResponse
type AppListResponse struct {
//appListResponse
type appListResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Apps []string `json:"apps"`
} `json:"data"`
} `json:"ocs"`
}
//AppResponse
type AppResponse struct {
//appResponse
type appResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data App `json:"data"`
} `json:"ocs"`
}
type AppConfigResponse struct {
type appConfigResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Data []string `json:"data"`
} `json:"data"`
} `json:"ocs"`
}
type AppcConfigValueResponse struct {
type appcConfigValueResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Data string `json:"data"`
} `json:"data"`
} `json:"ocs"`
}
//CapabilitiesResponse
type CapabilitiesResponse struct {
//capabilitiesResponse
type capabilitiesResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data struct {
Version Version `json:"version"`
Capabilities Capabilities `json:"capabilities"`
@ -119,6 +119,7 @@ type CapabilitiesResponse struct {
} `json:"ocs"`
}
// Version contains the nextcloud version informations
type Version struct {
Major int `json:"major"`
Minor int `json:"minor"`
@ -127,58 +128,58 @@ type Version struct {
Edition string `json:"edition"`
}
type MonitoringResponse struct {
type monitoringResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data Monitoring `json:"data"`
} `json:"ocs"`
}
type SharesListResponse struct {
type sharesListResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data []Share `json:"data"`
} `json:"ocs"`
}
type SharesResponse struct {
type sharesResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data Share `json:"data"`
} `json:"ocs"`
}
type GroupFoldersListResponse struct {
type groupFoldersListResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Data map[string]GroupFolderBadFormatIDAndGroups `json:"data"`
Meta meta `json:"meta"`
Data map[string]groupFolderBadFormatIDAndGroups `json:"data"`
} `json:"ocs"`
}
type GroupFoldersCreateResponse struct {
type groupFoldersCreateResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Data GroupFolderBadFormatIDAndGroups `json:"data"`
Meta meta `json:"meta"`
Data groupFolderBadFormatIDAndGroups `json:"data"`
} `json:"ocs"`
}
type GroupFoldersResponse struct {
type groupFoldersResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Data GroupFolderBadFormatGroups `json:"data"`
Meta meta `json:"meta"`
Data groupFolderBadFormatGroups `json:"data"`
} `json:"ocs"`
}
type NotificationsListResponse struct {
type notificationsListResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data []Notification `json:"data"`
} `json:"ocs"`
}
type NotificationResponse struct {
type notificationResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Meta meta `json:"meta"`
Data Notification `json:"data"`
} `json:"ocs"`
}

View File

@ -2,8 +2,8 @@ package gonextcloud
import "net/url"
// Routes references the available routes
type Routes struct {
// apiRoutes references the available routes
type apiRoutes struct {
capabilities *url.URL
users *url.URL
groups *url.URL
@ -20,7 +20,7 @@ const badRequest = 998
var (
apiPath = &url.URL{Path: "/ocs/v2.php"}
routes = Routes{
routes = apiRoutes{
capabilities: &url.URL{Path: apiPath.Path + "/cloud/capabilities"},
users: &url.URL{Path: apiPath.Path + "/cloud/users"},
groups: &url.URL{Path: apiPath.Path + "/cloud/groups"},

228
shares.go
View File

@ -1,174 +1,68 @@
package gonextcloud
import (
"fmt"
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
"strconv"
"sync"
// ShareType is the nextcloud shares types enum :
type ShareType int
// SharePermission is the nextcloud share permissions enum
type SharePermission int
const (
// UserShare is a file or folder shared with other user(s)
UserShare ShareType = 0
// GroupShare is a file or folder shared with a group
GroupShare ShareType = 1
// PublicLinkShare is a file or folder shared through public link
PublicLinkShare ShareType = 3
// FederatedCloudShare is a file or folder shared through federated cloud
FederatedCloudShare ShareType = 6
// ReadPermission grant read permission
ReadPermission SharePermission = 1
// UpdatePermission grant update permission
UpdatePermission SharePermission = 2
// CreatePermission grant create permission
CreatePermission SharePermission = 4
// DeletePermission grant delete permission
DeletePermission SharePermission = 8
// ReSharePermission grant resharing permission
ReSharePermission SharePermission = 16
// AllPermissions grant all permissions
AllPermissions SharePermission = 31
)
//Shares contains all Shares available actions
type Shares struct {
c *Client
// ShareUpdate contains the data required in order to update a nextcloud share
type ShareUpdate struct {
ShareID int
Permissions SharePermission
Password string
PublicUpload bool
ExpireDate string
}
//List list all shares of the logged in user
func (s *Shares) List() ([]types.Share, error) {
res, err := s.c.baseRequest(http.MethodGet, routes.shares, nil)
if err != nil {
return nil, err
}
var r types.SharesListResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//GetFromPath return shares from a specific file or folder
func (s *Shares) GetFromPath(path string, reshares bool, subfiles bool) ([]types.Share, error) {
ro := &req.RequestOptions{
Params: map[string]string{
"path": path,
"reshares": strconv.FormatBool(reshares),
"subfiles": strconv.FormatBool(subfiles),
},
}
res, err := s.c.baseRequest(http.MethodGet, routes.shares, ro)
if err != nil {
return nil, err
}
var r types.SharesListResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Get information about a known Share
func (s *Shares) Get(shareID string) (types.Share, error) {
res, err := s.c.baseRequest(http.MethodGet, routes.shares, nil, shareID)
if err != nil {
return types.Share{}, err
}
var r types.SharesListResponse
res.JSON(&r)
return r.Ocs.Data[0], nil
}
//Create create a share
func (s *Shares) Create(
path string,
shareType types.ShareType,
permission types.SharePermission,
shareWith string,
publicUpload bool,
password string,
) (types.Share, error) {
if (shareType == types.UserShare || shareType == types.GroupShare) && shareWith == "" {
return types.Share{}, fmt.Errorf("shareWith cannot be empty for ShareType UserShare or GroupShare")
}
ro := &req.RequestOptions{
Data: map[string]string{
"path": path,
"shareType": strconv.Itoa(int(shareType)),
"shareWith": shareWith,
"publicUpload": strconv.FormatBool(publicUpload),
"password": password,
"permissions": strconv.Itoa(int(permission)),
},
}
res, err := s.c.baseRequest(http.MethodPost, routes.shares, ro)
if err != nil {
return types.Share{}, err
}
var r types.SharesResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Delete Remove the given share.
func (s *Shares) Delete(shareID int) error {
_, err := s.c.baseRequest(http.MethodDelete, routes.shares, nil, strconv.Itoa(shareID))
return err
}
// Update update share details
// expireDate expireDate expects a well formatted date string, e.g. YYYY-MM-DD
func (s *Shares) Update(shareUpdate types.ShareUpdate) error {
errs := make(chan *types.UpdateError)
var wg sync.WaitGroup
wg.Add(4)
go func() {
defer wg.Done()
if err := s.UpdatePassword(shareUpdate.ShareID, shareUpdate.Password); err != nil {
errs <- &types.UpdateError{
Field: "password",
Error: err,
}
}
}()
go func() {
defer wg.Done()
if err := s.UpdateExpireDate(shareUpdate.ShareID, shareUpdate.ExpireDate); err != nil {
errs <- &types.UpdateError{
Field: "expireDate",
Error: err,
}
}
}()
go func() {
defer wg.Done()
if err := s.UpdatePermissions(shareUpdate.ShareID, shareUpdate.Permissions); err != nil {
errs <- &types.UpdateError{
Field: "permissions",
Error: err,
}
}
}()
go func() {
defer wg.Done()
if err := s.UpdatePublicUpload(shareUpdate.ShareID, shareUpdate.PublicUpload); err != nil {
errs <- &types.UpdateError{
Field: "publicUpload",
Error: err,
}
}
}()
go func() {
wg.Wait()
close(errs)
}()
if err := types.NewUpdateError(errs); err != nil {
return err
}
return nil
}
//UpdateExpireDate updates the share's expire date
// expireDate expects a well formatted date string, e.g. YYYY-MM-DD
func (s *Shares) UpdateExpireDate(shareID int, expireDate string) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "expireDate", expireDate)
}
//UpdatePublicUpload enable or disable public upload
func (s *Shares) UpdatePublicUpload(shareID int, public bool) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "publicUpload", strconv.FormatBool(public))
}
//UpdatePassword updates share password
func (s *Shares) UpdatePassword(shareID int, password string) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "password", password)
}
//UpdatePermissions update permissions
func (s *Shares) UpdatePermissions(shareID int, permissions types.SharePermission) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "permissions", strconv.Itoa(int(permissions)))
}
func (s *Shares) baseShareUpdate(shareID string, key string, value string) error {
ro := &req.RequestOptions{
Data: map[string]string{key: value},
}
_, err := s.c.baseRequest(http.MethodPut, routes.shares, ro, shareID)
return err
// Share is a nextcloud share
type Share struct {
ID string `json:"id"`
ShareType int `json:"share_type"`
UIDOwner string `json:"uid_owner"`
DisplaynameOwner string `json:"displayname_owner"`
Permissions int `json:"permissions"`
Stime int `json:"stime"`
Parent interface{} `json:"parent"`
Expiration string `json:"expiration"`
Token string `json:"token"`
UIDFileOwner string `json:"uid_file_owner"`
DisplaynameFileOwner string `json:"displayname_file_owner"`
Path string `json:"path"`
ItemType string `json:"item_type"`
Mimetype string `json:"mimetype"`
StorageID string `json:"storage_id"`
Storage int `json:"storage"`
ItemSource int `json:"item_source"`
FileSource int `json:"file_source"`
FileParent int `json:"file_parent"`
FileTarget string `json:"file_target"`
ShareWith string `json:"share_with"`
ShareWithDisplayname string `json:"share_with_displayname"`
MailSend int `json:"mail_send"`
Tags []string `json:"tags"`
}

174
shares_impl.go Normal file
View File

@ -0,0 +1,174 @@
package gonextcloud
import (
"fmt"
"net/http"
"strconv"
"sync"
req "github.com/levigross/grequests"
)
//shares contains all shares available actions
type shares struct {
c *client
}
//List list all shares of the logged in user
func (s *shares) List() ([]Share, error) {
res, err := s.c.baseRequest(http.MethodGet, routes.shares, nil)
if err != nil {
return nil, err
}
var r sharesListResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//GetFromPath return shares from a specific file or folder
func (s *shares) GetFromPath(path string, reshares bool, subfiles bool) ([]Share, error) {
ro := &req.RequestOptions{
Params: map[string]string{
"path": path,
"reshares": strconv.FormatBool(reshares),
"subfiles": strconv.FormatBool(subfiles),
},
}
res, err := s.c.baseRequest(http.MethodGet, routes.shares, ro)
if err != nil {
return nil, err
}
var r sharesListResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Get information about a known Share
func (s *shares) Get(shareID string) (Share, error) {
res, err := s.c.baseRequest(http.MethodGet, routes.shares, nil, shareID)
if err != nil {
return Share{}, err
}
var r sharesListResponse
res.JSON(&r)
return r.Ocs.Data[0], nil
}
//Create create a share
func (s *shares) Create(
path string,
shareType ShareType,
permission SharePermission,
shareWith string,
publicUpload bool,
password string,
) (Share, error) {
if (shareType == UserShare || shareType == GroupShare) && shareWith == "" {
return Share{}, fmt.Errorf("shareWith cannot be empty for ShareType UserShare or GroupShare")
}
ro := &req.RequestOptions{
Data: map[string]string{
"path": path,
"shareType": strconv.Itoa(int(shareType)),
"shareWith": shareWith,
"publicUpload": strconv.FormatBool(publicUpload),
"password": password,
"permissions": strconv.Itoa(int(permission)),
},
}
res, err := s.c.baseRequest(http.MethodPost, routes.shares, ro)
if err != nil {
return Share{}, err
}
var r sharesResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Delete Remove the given share.
func (s *shares) Delete(shareID int) error {
_, err := s.c.baseRequest(http.MethodDelete, routes.shares, nil, strconv.Itoa(shareID))
return err
}
// Update update share details
// expireDate expireDate expects a well formatted date string, e.g. YYYY-MM-DD
func (s *shares) Update(shareUpdate ShareUpdate) error {
errs := make(chan *UpdateError)
var wg sync.WaitGroup
wg.Add(4)
go func() {
defer wg.Done()
if err := s.UpdatePassword(shareUpdate.ShareID, shareUpdate.Password); err != nil {
errs <- &UpdateError{
Field: "password",
Error: err,
}
}
}()
go func() {
defer wg.Done()
if err := s.UpdateExpireDate(shareUpdate.ShareID, shareUpdate.ExpireDate); err != nil {
errs <- &UpdateError{
Field: "expireDate",
Error: err,
}
}
}()
go func() {
defer wg.Done()
if err := s.UpdatePermissions(shareUpdate.ShareID, shareUpdate.Permissions); err != nil {
errs <- &UpdateError{
Field: "permissions",
Error: err,
}
}
}()
go func() {
defer wg.Done()
if err := s.UpdatePublicUpload(shareUpdate.ShareID, shareUpdate.PublicUpload); err != nil {
errs <- &UpdateError{
Field: "publicUpload",
Error: err,
}
}
}()
go func() {
wg.Wait()
close(errs)
}()
if err := newUpdateError(errs); err != nil {
return err
}
return nil
}
//UpdateExpireDate updates the share's expire date
// expireDate expects a well formatted date string, e.g. YYYY-MM-DD
func (s *shares) UpdateExpireDate(shareID int, expireDate string) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "expireDate", expireDate)
}
//UpdatePublicUpload enable or disable public upload
func (s *shares) UpdatePublicUpload(shareID int, public bool) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "publicUpload", strconv.FormatBool(public))
}
//UpdatePassword updates share password
func (s *shares) UpdatePassword(shareID int, password string) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "password", password)
}
//UpdatePermissions update permissions
func (s *shares) UpdatePermissions(shareID int, permissions SharePermission) error {
return s.baseShareUpdate(strconv.Itoa(shareID), "permissions", strconv.Itoa(int(permissions)))
}
func (s *shares) baseShareUpdate(shareID string, key string, value string) error {
ro := &req.RequestOptions{
Data: map[string]string{key: value},
}
_, err := s.c.baseRequest(http.MethodPut, routes.shares, ro, shareID)
return err
}

View File

@ -1,57 +0,0 @@
package types
import "strconv"
type GroupFolderBadFormatIDAndGroups struct {
ID string `json:"id"`
MountPoint string `json:"mount_point"`
Groups map[string]string `json:"groups"`
Quota string `json:"quota"`
Size int `json:"size"`
}
type GroupFolderBadFormatGroups struct {
ID int `json:"id"`
MountPoint string `json:"mount_point"`
Groups map[string]string `json:"groups"`
Quota string `json:"quota"`
Size int `json:"size"`
}
type GroupFolder struct {
ID int `json:"id"`
MountPoint string `json:"mount_point"`
Groups map[string]SharePermission `json:"groups"`
Quota int `json:"quota"`
Size int `json:"size"`
}
func (gf *GroupFolderBadFormatGroups) FormatGroupFolder() GroupFolder {
g := GroupFolder{}
g.ID = gf.ID
g.MountPoint = gf.MountPoint
g.Groups = map[string]SharePermission{}
for k, v := range gf.Groups {
p, _ := strconv.Atoi(v)
g.Groups[k] = SharePermission(p)
}
q, _ := strconv.Atoi(gf.Quota)
g.Quota = q
g.Size = gf.Size
return g
}
func (gf *GroupFolderBadFormatIDAndGroups) FormatGroupFolder() GroupFolder {
g := GroupFolder{}
g.ID, _ = strconv.Atoi(gf.ID)
g.MountPoint = gf.MountPoint
g.Groups = map[string]SharePermission{}
for k, v := range gf.Groups {
p, _ := strconv.Atoi(v)
g.Groups[k] = SharePermission(p)
}
q, _ := strconv.Atoi(gf.Quota)
g.Quota = q
g.Size = gf.Size
return g
}

View File

@ -1,65 +0,0 @@
package types
type System struct {
Version string `json:"version"`
Theme string `json:"theme"`
EnableAvatars string `json:"enable_avatars"`
EnablePreviews string `json:"enable_previews"`
MemcacheLocal string `json:"memcache.local"`
MemcacheDistributed string `json:"memcache.distributed"`
FilelockingEnabled string `json:"filelocking.enabled"`
MemcacheLocking string `json:"memcache.locking"`
Debug string `json:"debug"`
Freespace int64 `json:"freespace"`
Cpuload []float32 `json:"cpuload"`
MemTotal int `json:"mem_total"`
MemFree int `json:"mem_free"`
SwapTotal int `json:"swap_total"`
SwapFree int `json:"swap_free"`
}
type Monitoring struct {
Nextcloud struct {
System System `json:"system"`
Storage Storage `json:"storage"`
Shares struct {
NumShares int `json:"num_shares"`
NumSharesUser int `json:"num_shares_user"`
NumSharesGroups int `json:"num_shares_groups"`
NumSharesLink int `json:"num_shares_link"`
NumSharesLinkNoPassword int `json:"num_shares_link_no_password"`
NumFedSharesSent int `json:"num_fed_shares_sent"`
NumFedSharesReceived int `json:"num_fed_shares_received"`
} `json:"shares"`
} `json:"nextcloud"`
Server struct {
Webserver string `json:"webserver"`
Php struct {
Version string `json:"version"`
MemoryLimit int `json:"memory_limit"`
MaxExecutionTime int `json:"max_execution_time"`
UploadMaxFilesize int `json:"upload_max_filesize"`
} `json:"php"`
Database struct {
Type string `json:"type"`
Version string `json:"version"`
Size int `json:"size"`
} `json:"database"`
} `json:"server"`
ActiveUsers ActiveUsers `json:"activeUsers"`
}
type ActiveUsers struct {
Last5Minutes int `json:"last5minutes"`
Last1Hour int `json:"last1hour"`
Last24Hours int `json:"last24hours"`
}
type Storage struct {
NumUsers int `json:"num_users"`
NumFiles int `json:"num_files"`
NumStorages int `json:"num_storages"`
NumStoragesLocal int `json:"num_storages_local"`
NumStoragesHome int `json:"num_storages_home"`
NumStoragesOther int `json:"num_storages_other"`
}

View File

@ -1,53 +0,0 @@
package types
type ShareType int
type SharePermission int
const (
UserShare ShareType = 0
GroupShare ShareType = 1
PublicLinkShare ShareType = 3
FederatedCloudShare ShareType = 6
ReadPermission SharePermission = 1
UpdatePermission SharePermission = 2
CreatePermission SharePermission = 4
DeletePermission SharePermission = 8
ReSharePermission SharePermission = 16
AllPermissions SharePermission = 31
)
type ShareUpdate struct {
ShareID int
Permissions SharePermission
Password string
PublicUpload bool
ExpireDate string
}
type Share struct {
ID string `json:"id"`
ShareType int `json:"share_type"`
UIDOwner string `json:"uid_owner"`
DisplaynameOwner string `json:"displayname_owner"`
Permissions int `json:"permissions"`
Stime int `json:"stime"`
Parent interface{} `json:"parent"`
Expiration string `json:"expiration"`
Token string `json:"token"`
UIDFileOwner string `json:"uid_file_owner"`
DisplaynameFileOwner string `json:"displayname_file_owner"`
Path string `json:"path"`
ItemType string `json:"item_type"`
Mimetype string `json:"mimetype"`
StorageID string `json:"storage_id"`
Storage int `json:"storage"`
ItemSource int `json:"item_source"`
FileSource int `json:"file_source"`
FileParent int `json:"file_parent"`
FileTarget string `json:"file_target"`
ShareWith string `json:"share_with"`
ShareWithDisplayname string `json:"share_with_displayname"`
MailSend int `json:"mail_send"`
Tags []string `json:"tags"`
}

View File

@ -1,39 +0,0 @@
package types
import (
"io"
"os"
"path/filepath"
)
// WebDav available methods
type WebDav interface {
// ReadDir reads the contents of a remote directory
ReadDir(path string) ([]os.FileInfo, error)
// Stat returns the file stats for a specified path
Stat(path string) (os.FileInfo, error)
// Remove removes a remote file
Remove(path string) error
// RemoveAll removes remote files
RemoveAll(path string) error
// Mkdir makes a directory
Mkdir(path string, _ os.FileMode) error
// MkdirAll like mkdir -p, but for webdav
MkdirAll(path string, _ os.FileMode) error
// Rename moves a file from A to B
Rename(oldpath, newpath string, overwrite bool) error
// Copy copies a file from A to B
Copy(oldpath, newpath string, overwrite bool) error
// Read reads the contents of a remote file
Read(path string) ([]byte, error)
// ReadStream reads the stream for a given path
ReadStream(path string) (io.ReadCloser, error)
// Write writes data to a given path
Write(path string, data []byte, _ os.FileMode) error
// WriteStream writes a stream
WriteStream(path string, stream io.Reader, _ os.FileMode) error
// Walk walks the file tree rooted at root, calling walkFn for each file or
// directory in the tree, including root.
Walk(path string, walkFunc filepath.WalkFunc) error
}

View File

@ -2,12 +2,12 @@ package gonextcloud
import (
"fmt"
"github.com/fatih/structs"
"github.com/stretchr/testify/assert"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"strings"
"testing"
"time"
"github.com/fatih/structs"
"github.com/stretchr/testify/assert"
)
func TestUserUpdate(t *testing.T) {
@ -23,11 +23,11 @@ func TestUserUpdate(t *testing.T) {
if err != nil {
t.FailNow()
}
user := &types.UserDetails{
user := &UserDetails{
ID: username,
Displayname: strings.ToUpper(username),
Email: "some@mail.com",
Quota: types.Quota{
Quota: Quota{
// Unlimited
Quota: -3,
},

View File

@ -1,8 +1,8 @@
package types
package gonextcloud
import "strconv"
//User encapsulate the data needed to create a new Nextcloud's User
// User encapsulate the data needed to create a new Nextcloud's User
type User struct {
Username string
Email string
@ -12,7 +12,7 @@ type User struct {
Groups []string
}
//UserDetails is the raw Nextcloud User response
// UserDetails is the raw Nextcloud User response
type UserDetails struct {
Enabled bool `json:"enabled"`
ID string `json:"id"`
@ -33,6 +33,7 @@ type UserDetails struct {
Locale string `json:"locale,omitempty"`
}
// Quota is a use storage Quota
type Quota struct {
Free int64 `json:"free"`
Used int64 `json:"used"`

View File

@ -2,56 +2,56 @@ package gonextcloud
import (
"encoding/json"
req "github.com/levigross/grequests"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
"net/url"
"path"
"strings"
"sync"
req "github.com/levigross/grequests"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
//Users contains all Users available actions
type Users struct {
c *Client
//users contains all users available actions
type users struct {
c *client
}
// List return the Nextcloud'user list
func (u *Users) List() ([]string, error) {
func (u *users) List() ([]string, error) {
res, err := u.c.baseRequest(http.MethodGet, routes.users, nil)
//res, err := c.session.Get(u.String(), nil)
if err != nil {
return nil, err
}
var r types.UserListResponse
var r userListResponse
res.JSON(&r)
return r.Ocs.Data.Users, nil
}
//ListDetails return a map of user with details
func (u *Users) ListDetails() (map[string]types.UserDetails, error) {
func (u *users) ListDetails() (map[string]UserDetails, error) {
res, err := u.c.baseRequest(http.MethodGet, routes.users, nil, "details")
//res, err := c.session.Get(u.String(), nil)
if err != nil {
return nil, err
}
var r types.UserListDetailsResponse
var r userListDetailsResponse
res.JSON(&r)
return r.Ocs.Data.Users, nil
}
// Get return the details about the specified user
func (u *Users) Get(name string) (*types.UserDetails, error) {
func (u *users) Get(name string) (*UserDetails, error) {
if name == "" {
return nil, &types.APIError{Message: "name cannot be empty"}
return nil, &APIError{Message: "name cannot be empty"}
}
res, err := u.c.baseRequest(http.MethodGet, routes.users, nil, name)
if err != nil {
return nil, err
}
var r types.UserResponse
var r userResponse
js := res.String()
// Nextcloud does not encode JSON properly
js = reformatJSON(js)
@ -62,7 +62,7 @@ func (u *Users) Get(name string) (*types.UserDetails, error) {
}
// Search returns the users whose name match the search string
func (u *Users) Search(search string) ([]string, error) {
func (u *users) Search(search string) ([]string, error) {
ro := &req.RequestOptions{
Params: map[string]string{"search": search},
}
@ -70,14 +70,14 @@ func (u *Users) Search(search string) ([]string, error) {
if err != nil {
return nil, err
}
var r types.UserListResponse
var r userListResponse
res.JSON(&r)
return r.Ocs.Data.Users, nil
}
// Create create a new user
func (u *Users) Create(username string, password string, user *types.UserDetails) error {
// Create base Users
func (u *users) Create(username string, password string, user *UserDetails) error {
// Create base users
ro := &req.RequestOptions{
Data: map[string]string{
"userid": username,
@ -97,7 +97,7 @@ func (u *Users) Create(username string, password string, user *types.UserDetails
// CreateWithoutPassword create a user without provisioning a password, the email address must be provided to send
// an init password email
func (u *Users) CreateWithoutPassword(username, email, displayName, quota, language string, groups ...string) error {
func (u *users) CreateWithoutPassword(username, email, displayName, quota, language string, groups ...string) error {
if u.c.version.Major < 14 {
return errors.New("unsupported method: requires Nextcloud 14+")
}
@ -132,12 +132,12 @@ func (u *Users) CreateWithoutPassword(username, email, displayName, quota, langu
}
//CreateBatchWithoutPassword create multiple users and send them the init password email
func (u *Users) CreateBatchWithoutPassword(users []types.User) error {
func (u *users) CreateBatchWithoutPassword(users []User) error {
var wg sync.WaitGroup
errs := make(chan error)
for _, us := range users {
wg.Add(1)
go func(user types.User) {
go func(user User) {
logrus.Debugf("creating user %s", user.Username)
defer wg.Done()
if err := u.CreateWithoutPassword(
@ -162,12 +162,12 @@ func (u *Users) CreateBatchWithoutPassword(users []types.User) error {
}
//Delete delete the user
func (u *Users) Delete(name string) error {
func (u *users) Delete(name string) error {
return u.baseRequest(http.MethodDelete, nil, name)
}
//Enable enables the user
func (u *Users) Enable(name string) error {
func (u *users) Enable(name string) error {
ro := &req.RequestOptions{
Data: map[string]string{},
}
@ -175,7 +175,7 @@ func (u *Users) Enable(name string) error {
}
//Disable disables the user
func (u *Users) Disable(name string) error {
func (u *users) Disable(name string) error {
ro := &req.RequestOptions{
Data: map[string]string{},
}
@ -183,25 +183,25 @@ func (u *Users) Disable(name string) error {
}
//SendWelcomeEmail (re)send the welcome mail to the user (return an error if the user has not configured his email)
func (u *Users) SendWelcomeEmail(name string) error {
func (u *users) SendWelcomeEmail(name string) error {
return u.baseRequest(http.MethodPost, nil, name, "welcome")
}
//Update takes a *types.Users struct to update the user's information
// Updatable fields: Email, Displayname, Phone, Address, Website, Twitter, Quota, Groups
func (u *Users) Update(user *types.UserDetails) error {
//Update takes a *types.users struct to update the user's information
// Updatable fields: Email, Displayname, Phone, Address, Website, Twitter, Quota, groups
func (u *users) Update(user *UserDetails) error {
// Get user to update only modified fields
original, err := u.Get(user.ID)
if err != nil {
return err
}
errs := make(chan *types.UpdateError)
errs := make(chan *UpdateError)
var wg sync.WaitGroup
update := func(key string, value string) {
defer wg.Done()
if err := u.updateAttribute(user.ID, strings.ToLower(key), value); err != nil {
errs <- &types.UpdateError{
errs <- &UpdateError{
Field: key,
Error: err,
}
@ -242,7 +242,7 @@ func (u *Users) Update(user *types.UserDetails) error {
if user.Quota.Quota != original.Quota.Quota {
var value string
// If empty
if user.Quota == (types.Quota{}) {
if user.Quota == (Quota{}) {
value = "default"
} else {
value = user.Quota.String()
@ -250,7 +250,7 @@ func (u *Users) Update(user *types.UserDetails) error {
wg.Add(1)
go update("Quota", value)
}
// Groups
// groups
// Group removed
for _, g := range original.Groups {
if !contains(user.Groups, g) {
@ -258,8 +258,8 @@ func (u *Users) Update(user *types.UserDetails) error {
go func(gr string) {
defer wg.Done()
if err := u.GroupRemove(user.ID, gr); err != nil {
errs <- &types.UpdateError{
Field: "Groups/" + gr,
errs <- &UpdateError{
Field: "groups/" + gr,
Error: err,
}
}
@ -275,8 +275,8 @@ func (u *Users) Update(user *types.UserDetails) error {
go func(gr string) {
defer wg.Done()
if err := u.GroupAdd(user.ID, gr); err != nil {
errs <- &types.UpdateError{
Field: "Groups/" + gr,
errs <- &UpdateError{
Field: "groups/" + gr,
Error: err,
}
}
@ -290,66 +290,66 @@ func (u *Users) Update(user *types.UserDetails) error {
close(errs)
}()
// Warning : we actually need to check the *err
if err := types.NewUpdateError(errs); err != nil {
if err := newUpdateError(errs); err != nil {
return err
}
return nil
}
//UpdateEmail update the user's email
func (u *Users) UpdateEmail(name string, email string) error {
func (u *users) UpdateEmail(name string, email string) error {
return u.updateAttribute(name, "email", email)
}
//UpdateDisplayName update the user's display name
func (u *Users) UpdateDisplayName(name string, displayName string) error {
func (u *users) UpdateDisplayName(name string, displayName string) error {
return u.updateAttribute(name, "displayname", displayName)
}
//UpdatePhone update the user's phone
func (u *Users) UpdatePhone(name string, phone string) error {
func (u *users) UpdatePhone(name string, phone string) error {
return u.updateAttribute(name, "phone", phone)
}
//UpdateAddress update the user's address
func (u *Users) UpdateAddress(name string, address string) error {
func (u *users) UpdateAddress(name string, address string) error {
return u.updateAttribute(name, "address", address)
}
//UpdateWebSite update the user's website
func (u *Users) UpdateWebSite(name string, website string) error {
func (u *users) UpdateWebSite(name string, website string) error {
return u.updateAttribute(name, "website", website)
}
//UpdateTwitter update the user's twitter
func (u *Users) UpdateTwitter(name string, twitter string) error {
func (u *users) UpdateTwitter(name string, twitter string) error {
return u.updateAttribute(name, "twitter", twitter)
}
//UpdatePassword update the user's password
func (u *Users) UpdatePassword(name string, password string) error {
func (u *users) UpdatePassword(name string, password string) error {
return u.updateAttribute(name, "password", password)
}
//UpdateQuota update the user's quota (bytes). Set negative quota for unlimited
func (u *Users) UpdateQuota(name string, quota int64) error {
q := types.Quota{Quota: quota}
func (u *users) UpdateQuota(name string, quota int64) error {
q := Quota{Quota: quota}
return u.updateAttribute(name, "quota", q.String())
}
//GroupList lists the user's groups
func (u *Users) GroupList(name string) ([]string, error) {
func (u *users) GroupList(name string) ([]string, error) {
res, err := u.c.baseRequest(http.MethodGet, routes.users, nil, name, "groups")
if err != nil {
return nil, err
}
var r types.GroupListResponse
var r groupListResponse
res.JSON(&r)
return r.Ocs.Data.Groups, nil
}
//GroupAdd adds a the user to the group
func (u *Users) GroupAdd(name string, group string) error {
func (u *users) GroupAdd(name string, group string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"groupid": group,
@ -359,7 +359,7 @@ func (u *Users) GroupAdd(name string, group string) error {
}
//GroupRemove removes the user from the group
func (u *Users) GroupRemove(name string, group string) error {
func (u *users) GroupRemove(name string, group string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"groupid": group,
@ -369,7 +369,7 @@ func (u *Users) GroupRemove(name string, group string) error {
}
//GroupPromote promotes the user as group admin
func (u *Users) GroupPromote(name string, group string) error {
func (u *users) GroupPromote(name string, group string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"groupid": group,
@ -379,7 +379,7 @@ func (u *Users) GroupPromote(name string, group string) error {
}
//GroupDemote demotes the user
func (u *Users) GroupDemote(name string, group string) error {
func (u *users) GroupDemote(name string, group string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"groupid": group,
@ -389,7 +389,7 @@ func (u *Users) GroupDemote(name string, group string) error {
}
//GroupSubAdminList lists the groups where he is subadmin
func (u *Users) GroupSubAdminList(name string) ([]string, error) {
func (u *users) GroupSubAdminList(name string) ([]string, error) {
if !u.c.loggedIn() {
return nil, errUnauthorized
}
@ -399,12 +399,12 @@ func (u *Users) GroupSubAdminList(name string) ([]string, error) {
if err != nil {
return nil, err
}
var r types.BaseResponse
var r baseResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
func (u *Users) updateAttribute(name string, key string, value string) error {
func (u *users) updateAttribute(name string, key string, value string) error {
ro := &req.RequestOptions{
Data: map[string]string{
"key": key,
@ -414,13 +414,13 @@ func (u *Users) updateAttribute(name string, key string, value string) error {
return u.baseRequest(http.MethodPut, ro, name)
}
func (u *Users) baseRequest(method string, ro *req.RequestOptions, subRoutes ...string) error {
func (u *users) baseRequest(method string, ro *req.RequestOptions, subRoutes ...string) error {
_, err := u.c.baseRequest(method, routes.users, ro, subRoutes...)
return err
}
func ignoredUserField(key string) bool {
keys := []string{"Email", "Displayname", "Phone", "Address", "Website", "Twitter", "Quota", "Groups"}
keys := []string{"Email", "Displayname", "Phone", "Address", "Website", "Twitter", "Quota", "groups"}
for _, k := range keys {
if key == k {
return false

View File

@ -2,15 +2,15 @@ package gonextcloud
import (
"encoding/json"
req "github.com/levigross/grequests"
"gitlab.bertha.cloud/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
"net/url"
"path"
"strings"
req "github.com/levigross/grequests"
)
func (c *Client) baseRequest(method string, route *url.URL, ro *req.RequestOptions, subRoutes ...string) (*req.Response, error) {
func (c *client) baseRequest(method string, route *url.URL, ro *req.RequestOptions, subRoutes ...string) (*req.Response, error) {
if !c.loggedIn() {
return nil, errUnauthorized
}
@ -40,12 +40,12 @@ func (c *Client) baseRequest(method string, route *url.URL, ro *req.RequestOptio
}
// As we cannot read the ReaderCloser twice, we use the string content
js := res.String()
var r types.BaseResponse
var r baseResponse
json.Unmarshal([]byte(js), &r)
if r.Ocs.Meta.Statuscode == 200 || r.Ocs.Meta.Statuscode == 100 {
return res, nil
}
err = types.ErrorFromMeta(r.Ocs.Meta)
err = errorFromMeta(r.Ocs.Meta)
return nil, err
}

28
vendor/github.com/stretchr/testify/require/doc.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
// Package require implements the same assertions as the `assert` package but
// stops test execution when a test fails.
//
// Example Usage
//
// The following is a complete example using require in a standard test function:
// import (
// "testing"
// "github.com/stretchr/testify/require"
// )
//
// func TestSomething(t *testing.T) {
//
// var a string = "Hello"
// var b string = "Hello"
//
// require.Equal(t, a, b, "The two words should be the same.")
//
// }
//
// Assertions
//
// The `require` package have same global functions as in the `assert` package,
// but instead of returning a boolean result they call `t.FailNow()`.
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
package require

View File

@ -0,0 +1,16 @@
package require
// Assertions provides assertion methods around the
// TestingT interface.
type Assertions struct {
t TestingT
}
// New makes a new Assertions object for the specified TestingT.
func New(t TestingT) *Assertions {
return &Assertions{
t: t,
}
}
//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl -include-format-funcs

1227
vendor/github.com/stretchr/testify/require/require.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
{{.Comment}}
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return }
if h, ok := t.(tHelper); ok { h.Helper() }
t.FailNow()
}

View File

@ -0,0 +1,957 @@
/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package require
import (
assert "github.com/stretchr/testify/assert"
http "net/http"
url "net/url"
time "time"
)
// Condition uses a Comparison to assert a complex condition.
func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Condition(a.t, comp, msgAndArgs...)
}
// Conditionf uses a Comparison to assert a complex condition.
func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Conditionf(a.t, comp, msg, args...)
}
// Contains asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// a.Contains("Hello World", "World")
// a.Contains(["Hello", "World"], "World")
// a.Contains({"Hello": "World"}, "Hello")
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Contains(a.t, s, contains, msgAndArgs...)
}
// Containsf asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// a.Containsf("Hello World", "World", "error message %s", "formatted")
// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted")
// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted")
func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Containsf(a.t, s, contains, msg, args...)
}
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
DirExists(a.t, path, msgAndArgs...)
}
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
DirExistsf(a.t, path, msg, args...)
}
// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should match.
//
// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2])
func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ElementsMatch(a.t, listA, listB, msgAndArgs...)
}
// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified
// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,
// the number of appearances of each of them in both lists should match.
//
// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted")
func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
ElementsMatchf(a.t, listA, listB, msg, args...)
}
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// a.Empty(obj)
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Empty(a.t, object, msgAndArgs...)
}
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// a.Emptyf(obj, "error message %s", "formatted")
func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Emptyf(a.t, object, msg, args...)
}
// Equal asserts that two objects are equal.
//
// a.Equal(123, 123)
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail.
func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Equal(a.t, expected, actual, msgAndArgs...)
}
// EqualError asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// a.EqualError(err, expectedErrorString)
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
EqualError(a.t, theError, errString, msgAndArgs...)
}
// EqualErrorf asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted")
func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
EqualErrorf(a.t, theError, errString, msg, args...)
}
// EqualValues asserts that two objects are equal or convertable to the same types
// and equal.
//
// a.EqualValues(uint32(123), int32(123))
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
EqualValues(a.t, expected, actual, msgAndArgs...)
}
// EqualValuesf asserts that two objects are equal or convertable to the same types
// and equal.
//
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
EqualValuesf(a.t, expected, actual, msg, args...)
}
// Equalf asserts that two objects are equal.
//
// a.Equalf(123, 123, "error message %s", "formatted")
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses). Function equality
// cannot be determined and will always fail.
func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Equalf(a.t, expected, actual, msg, args...)
}
// Error asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if a.Error(err) {
// assert.Equal(t, expectedError, err)
// }
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Error(a.t, err, msgAndArgs...)
}
// Errorf asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if a.Errorf(err, "error message %s", "formatted") {
// assert.Equal(t, expectedErrorf, err)
// }
func (a *Assertions) Errorf(err error, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Errorf(a.t, err, msg, args...)
}
// Exactly asserts that two objects are equal in value and type.
//
// a.Exactly(int32(123), int64(123))
func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Exactly(a.t, expected, actual, msgAndArgs...)
}
// Exactlyf asserts that two objects are equal in value and type.
//
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Exactlyf(a.t, expected, actual, msg, args...)
}
// Fail reports a failure through
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Fail(a.t, failureMessage, msgAndArgs...)
}
// FailNow fails test
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
FailNow(a.t, failureMessage, msgAndArgs...)
}
// FailNowf fails test
func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
FailNowf(a.t, failureMessage, msg, args...)
}
// Failf reports a failure through
func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Failf(a.t, failureMessage, msg, args...)
}
// False asserts that the specified value is false.
//
// a.False(myBool)
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
False(a.t, value, msgAndArgs...)
}
// Falsef asserts that the specified value is false.
//
// a.Falsef(myBool, "error message %s", "formatted")
func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Falsef(a.t, value, msg, args...)
}
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
FileExists(a.t, path, msgAndArgs...)
}
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
FileExistsf(a.t, path, msg, args...)
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...)
}
// HTTPBodyContainsf asserts that a specified handler returns a
// body that contains a string.
//
// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...)
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...)
}
// HTTPBodyNotContainsf asserts that a specified handler returns a
// body that does not contain a string.
//
// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...)
}
// HTTPError asserts that a specified handler returns an error status code.
//
// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPError(a.t, handler, method, url, values, msgAndArgs...)
}
// HTTPErrorf asserts that a specified handler returns an error status code.
//
// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPErrorf(a.t, handler, method, url, values, msg, args...)
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...)
}
// HTTPRedirectf asserts that a specified handler returns a redirect status code.
//
// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...)
}
// HTTPSuccessf asserts that a specified handler returns a success status code.
//
// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
HTTPSuccessf(a.t, handler, method, url, values, msg, args...)
}
// Implements asserts that an object is implemented by the specified interface.
//
// a.Implements((*MyInterface)(nil), new(MyObject))
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Implements(a.t, interfaceObject, object, msgAndArgs...)
}
// Implementsf asserts that an object is implemented by the specified interface.
//
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Implementsf(a.t, interfaceObject, object, msg, args...)
}
// InDelta asserts that the two numerals are within delta of each other.
//
// a.InDelta(math.Pi, (22 / 7.0), 0.01)
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InDelta(a.t, expected, actual, delta, msgAndArgs...)
}
// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...)
}
// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys.
func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...)
}
// InDeltaSlice is the same as InDelta, except it compares two slices.
func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
}
// InDeltaSlicef is the same as InDelta, except it compares two slices.
func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InDeltaSlicef(a.t, expected, actual, delta, msg, args...)
}
// InDeltaf asserts that the two numerals are within delta of each other.
//
// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InDeltaf(a.t, expected, actual, delta, msg, args...)
}
// InEpsilon asserts that expected and actual have a relative error less than epsilon
func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
}
// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...)
}
// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices.
func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...)
}
// InEpsilonf asserts that expected and actual have a relative error less than epsilon
func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
InEpsilonf(a.t, expected, actual, epsilon, msg, args...)
}
// IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsType(a.t, expectedType, object, msgAndArgs...)
}
// IsTypef asserts that the specified objects are of the same type.
func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
IsTypef(a.t, expectedType, object, msg, args...)
}
// JSONEq asserts that two JSON strings are equivalent.
//
// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
JSONEq(a.t, expected, actual, msgAndArgs...)
}
// JSONEqf asserts that two JSON strings are equivalent.
//
// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted")
func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
JSONEqf(a.t, expected, actual, msg, args...)
}
// Len asserts that the specified object has specific length.
// Len also fails if the object has a type that len() not accept.
//
// a.Len(mySlice, 3)
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Len(a.t, object, length, msgAndArgs...)
}
// Lenf asserts that the specified object has specific length.
// Lenf also fails if the object has a type that len() not accept.
//
// a.Lenf(mySlice, 3, "error message %s", "formatted")
func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Lenf(a.t, object, length, msg, args...)
}
// Nil asserts that the specified object is nil.
//
// a.Nil(err)
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Nil(a.t, object, msgAndArgs...)
}
// Nilf asserts that the specified object is nil.
//
// a.Nilf(err, "error message %s", "formatted")
func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Nilf(a.t, object, msg, args...)
}
// NoError asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if a.NoError(err) {
// assert.Equal(t, expectedObj, actualObj)
// }
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NoError(a.t, err, msgAndArgs...)
}
// NoErrorf asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if a.NoErrorf(err, "error message %s", "formatted") {
// assert.Equal(t, expectedObj, actualObj)
// }
func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NoErrorf(a.t, err, msg, args...)
}
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// a.NotContains("Hello World", "Earth")
// a.NotContains(["Hello", "World"], "Earth")
// a.NotContains({"Hello": "World"}, "Earth")
func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotContains(a.t, s, contains, msgAndArgs...)
}
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted")
// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted")
// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted")
func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotContainsf(a.t, s, contains, msg, args...)
}
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if a.NotEmpty(obj) {
// assert.Equal(t, "two", obj[1])
// }
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotEmpty(a.t, object, msgAndArgs...)
}
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if a.NotEmptyf(obj, "error message %s", "formatted") {
// assert.Equal(t, "two", obj[1])
// }
func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotEmptyf(a.t, object, msg, args...)
}
// NotEqual asserts that the specified values are NOT equal.
//
// a.NotEqual(obj1, obj2)
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses).
func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotEqual(a.t, expected, actual, msgAndArgs...)
}
// NotEqualf asserts that the specified values are NOT equal.
//
// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
//
// Pointer variable equality is determined based on the equality of the
// referenced values (as opposed to the memory addresses).
func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotEqualf(a.t, expected, actual, msg, args...)
}
// NotNil asserts that the specified object is not nil.
//
// a.NotNil(err)
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotNil(a.t, object, msgAndArgs...)
}
// NotNilf asserts that the specified object is not nil.
//
// a.NotNilf(err, "error message %s", "formatted")
func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotNilf(a.t, object, msg, args...)
}
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// a.NotPanics(func(){ RemainCalm() })
func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotPanics(a.t, f, msgAndArgs...)
}
// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted")
func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotPanicsf(a.t, f, msg, args...)
}
// NotRegexp asserts that a specified regexp does not match a string.
//
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
// a.NotRegexp("^start", "it's not starting")
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotRegexp(a.t, rx, str, msgAndArgs...)
}
// NotRegexpf asserts that a specified regexp does not match a string.
//
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotRegexpf(a.t, rx, str, msg, args...)
}
// NotSubset asserts that the specified list(array, slice...) contains not all
// elements given in the specified subset(array, slice...).
//
// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotSubset(a.t, list, subset, msgAndArgs...)
}
// NotSubsetf asserts that the specified list(array, slice...) contains not all
// elements given in the specified subset(array, slice...).
//
// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotSubsetf(a.t, list, subset, msg, args...)
}
// NotZero asserts that i is not the zero value for its type.
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotZero(a.t, i, msgAndArgs...)
}
// NotZerof asserts that i is not the zero value for its type.
func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
NotZerof(a.t, i, msg, args...)
}
// Panics asserts that the code inside the specified PanicTestFunc panics.
//
// a.Panics(func(){ GoCrazy() })
func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Panics(a.t, f, msgAndArgs...)
}
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
// the recovered panic value equals the expected panic value.
//
// a.PanicsWithValue("crazy error", func(){ GoCrazy() })
func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
PanicsWithValue(a.t, expected, f, msgAndArgs...)
}
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
// the recovered panic value equals the expected panic value.
//
// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
PanicsWithValuef(a.t, expected, f, msg, args...)
}
// Panicsf asserts that the code inside the specified PanicTestFunc panics.
//
// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted")
func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Panicsf(a.t, f, msg, args...)
}
// Regexp asserts that a specified regexp matches a string.
//
// a.Regexp(regexp.MustCompile("start"), "it's starting")
// a.Regexp("start...$", "it's not starting")
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Regexp(a.t, rx, str, msgAndArgs...)
}
// Regexpf asserts that a specified regexp matches a string.
//
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Regexpf(a.t, rx, str, msg, args...)
}
// Subset asserts that the specified list(array, slice...) contains all
// elements given in the specified subset(array, slice...).
//
// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Subset(a.t, list, subset, msgAndArgs...)
}
// Subsetf asserts that the specified list(array, slice...) contains all
// elements given in the specified subset(array, slice...).
//
// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Subsetf(a.t, list, subset, msg, args...)
}
// True asserts that the specified value is true.
//
// a.True(myBool)
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
True(a.t, value, msgAndArgs...)
}
// Truef asserts that the specified value is true.
//
// a.Truef(myBool, "error message %s", "formatted")
func (a *Assertions) Truef(value bool, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Truef(a.t, value, msg, args...)
}
// WithinDuration asserts that the two times are within duration delta of each other.
//
// a.WithinDuration(time.Now(), time.Now(), 10*time.Second)
func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
}
// WithinDurationf asserts that the two times are within duration delta of each other.
//
// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted")
func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
WithinDurationf(a.t, expected, actual, delta, msg, args...)
}
// Zero asserts that i is the zero value for its type.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Zero(a.t, i, msgAndArgs...)
}
// Zerof asserts that i is the zero value for its type.
func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
Zerof(a.t, i, msg, args...)
}

View File

@ -0,0 +1,5 @@
{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) {
if h, ok := a.t.(tHelper); ok { h.Helper() }
{{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}

View File

@ -0,0 +1,29 @@
package require
// TestingT is an interface wrapper around *testing.T
type TestingT interface {
Errorf(format string, args ...interface{})
FailNow()
}
type tHelper interface {
Helper()
}
// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful
// for table driven tests.
type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{})
// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful
// for table driven tests.
type ValueAssertionFunc func(TestingT, interface{}, ...interface{})
// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful
// for table driven tests.
type BoolAssertionFunc func(TestingT, bool, ...interface{})
// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful
// for table driven tests.
type ErrorAssertionFunc func(TestingT, error, ...interface{})
//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl -include-format-funcs

View File

@ -0,0 +1,19 @@
# Folders to ignore
/src
/bin
/pkg
/gowebdav
/.idea
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

View File

@ -0,0 +1,10 @@
language: go
go:
- "1.x"
install:
- go get ./...
script:
- go test -v --short ./...

View File

@ -0,0 +1,27 @@
Copyright (c) 2014, Studio B12 GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,33 @@
BIN := gowebdav
SRC := $(wildcard *.go) cmd/gowebdav/main.go
all: test cmd
cmd: ${BIN}
${BIN}: ${SRC}
go build -o $@ ./cmd/gowebdav
test:
go test -v --short ./...
api:
@sed '/^## API$$/,$$d' -i README.md
@echo '## API' >> README.md
@godoc2md github.com/studio-b12/gowebdav | sed '/^$$/N;/^\n$$/D' |\
sed '2d' |\
sed 's/\/src\/github.com\/studio-b12\/gowebdav\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\
sed 's/\/src\/target\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\
sed 's/^#/##/g' >> README.md
check:
gofmt -w -s $(SRC)
@echo
gocyclo -over 15 .
@echo
golint ./...
clean:
@rm -f ${BIN}
.PHONY: all cmd clean test api check

View File

@ -0,0 +1,147 @@
# GoWebDAV
[![Build Status](https://travis-ci.org/studio-b12/gowebdav.svg?branch=master)](https://travis-ci.org/studio-b12/gowebdav)
[![GoDoc](https://godoc.org/github.com/studio-b12/gowebdav?status.svg)](https://godoc.org/github.com/studio-b12/gowebdav)
[![Go Report Card](https://goreportcard.com/badge/github.com/studio-b12/gowebdav)](https://goreportcard.com/report/github.com/studio-b12/gowebdav)
A golang WebDAV client library.
## Main features
`gowebdav` library allows to perform following actions on the remote WebDAV server:
* [create path](#create-path-on-a-webdav-server)
* [get files list](#get-files-list)
* [download file](#download-file-to-byte-array)
* [upload file](#upload-file-from-byte-array)
* [get information about specified file/folder](#get-information-about-specified-filefolder)
* [move file to another location](#move-file-to-another-location)
* [copy file to another location](#copy-file-to-another-location)
* [delete file](#delete-file)
## Usage
First of all you should create `Client` instance using `NewClient()` function:
```go
root := "https://webdav.mydomain.me"
user := "user"
password := "password"
c := gowebdav.NewClient(root, user, password)
```
After you can use this `Client` to perform actions, described below.
**NOTICE:** we will not check errors in examples, to focus you on the `gowebdav` library's code, but you should do it in your code!
### Create path on a WebDAV server
```go
err := c.Mkdir("folder", 0644)
```
In case you want to create several folders you can use `c.MkdirAll()`:
```go
err := c.MkdirAll("folder/subfolder/subfolder2", 0644)
```
### Get files list
```go
files, _ := c.ReadDir("folder/subfolder")
for _, file := range files {
//notice that [file] has os.FileInfo type
fmt.Println(file.Name())
}
```
### Download file to byte array
```go
webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"
bytes, _ := c.Read(webdavFilePath)
ioutil.WriteFile(localFilePath, bytes, 0644)
```
### Download file via reader
Also you can use `c.ReadStream()` method:
```go
webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"
reader, _ := c.ReadStream(webdavFilePath)
file, _ := os.Create(localFilePath)
defer file.Close()
io.Copy(file, reader)
```
### Upload file from byte array
```go
webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"
bytes, _ := ioutil.ReadFile(localFilePath)
c.Write(webdavFilePath, bytes, 0644)
```
### Upload file via writer
```go
webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"
file, _ := os.Open(localFilePath)
defer file.Close()
c.WriteStream(webdavFilePath, file, 0644)
```
### Get information about specified file/folder
```go
webdavFilePath := "folder/subfolder/file.txt"
info := c.Stat(webdavFilePath)
//notice that [info] has os.FileInfo type
fmt.Println(info)
```
### Move file to another location
```go
oldPath := "folder/subfolder/file.txt"
newPath := "folder/subfolder/moved.txt"
isOverwrite := true
c.Rename(oldPath, newPath, isOverwrite)
```
### Copy file to another location
```go
oldPath := "folder/subfolder/file.txt"
newPath := "folder/subfolder/file-copy.txt"
isOverwrite := true
c.Copy(oldPath, newPath, isOverwrite)
```
### Delete file
```go
webdavFilePath := "folder/subfolder/file.txt"
c.Remove(webdavFilePath)
```
## Links
More details about WebDAV server you can read from following resources:
* [RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)](https://tools.ietf.org/html/rfc4918)
* [RFC 5689 - Extended MKCOL for Web Distributed Authoring and Versioning (WebDAV)](https://tools.ietf.org/html/rfc5689)
* [RFC 2616 - HTTP/1.1 Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html "HTTP/1.1 Status Code Definitions")
* [WebDav: Next Generation Collaborative Web Authoring By Lisa Dusseaul](https://books.google.de/books?isbn=0130652083 "WebDav: Next Generation Collaborative Web Authoring By Lisa Dusseault")
**NOTICE**: RFC 2518 is obsoleted by RFC 4918 in June 2007
## Contributing
All contributing are welcome. If you have any suggestions or find some bug - please create an Issue to let us make this project better. We appreciate your help!
## License
This library is distributed under the BSD 3-Clause license found in the [LICENSE](https://github.com/studio-b12/gowebdav/blob/master/LICENSE) file.

View File

@ -0,0 +1,33 @@
package gowebdav
import (
"encoding/base64"
)
// BasicAuth structure holds our credentials
type BasicAuth struct {
user string
pw string
}
// Type identifies the BasicAuthenticator
func (b *BasicAuth) Type() string {
return "BasicAuth"
}
// User holds the BasicAuth username
func (b *BasicAuth) User() string {
return b.user
}
// Pass holds the BasicAuth password
func (b *BasicAuth) Pass() string {
return b.pw
}
// Authorize the current request
func (b *BasicAuth) Authorize(c *Client, method string, path string) {
a := b.user + ":" + b.pw
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(a))
c.headers.Set("Authorization", auth)
}

View File

@ -0,0 +1,384 @@
package gowebdav
import (
"bytes"
"encoding/xml"
"io"
"net/http"
"net/url"
"os"
pathpkg "path"
"strings"
"time"
)
// Client defines our structure
type Client struct {
root string
headers http.Header
c *http.Client
auth Authenticator
}
// Authenticator stub
type Authenticator interface {
Type() string
User() string
Pass() string
Authorize(*Client, string, string)
}
// NoAuth structure holds our credentials
type NoAuth struct {
user string
pw string
}
// Type identifies the authenticator
func (n *NoAuth) Type() string {
return "NoAuth"
}
// User returns the current user
func (n *NoAuth) User() string {
return n.user
}
// Pass returns the current password
func (n *NoAuth) Pass() string {
return n.pw
}
// Authorize the current request
func (n *NoAuth) Authorize(c *Client, method string, path string) {
}
// NewClient creates a new instance of client
func NewClient(uri, user, pw string) *Client {
return &Client{FixSlash(uri), make(http.Header), &http.Client{}, &NoAuth{user, pw}}
}
// SetHeader lets us set arbitrary headers for a given client
func (c *Client) SetHeader(key, value string) {
c.headers.Add(key, value)
}
// SetTimeout exposes the ability to set a time limit for requests
func (c *Client) SetTimeout(timeout time.Duration) {
c.c.Timeout = timeout
}
// SetTransport exposes the ability to define custom transports
func (c *Client) SetTransport(transport http.RoundTripper) {
c.c.Transport = transport
}
// Connect connects to our dav server
func (c *Client) Connect() error {
rs, err := c.options("/")
if err != nil {
return err
}
err = rs.Body.Close()
if err != nil {
return err
}
if rs.StatusCode != 200 {
return newPathError("Connect", c.root, rs.StatusCode)
}
return nil
}
type props struct {
Status string `xml:"DAV: status"`
Name string `xml:"DAV: prop>displayname,omitempty"`
Type xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"`
Size string `xml:"DAV: prop>getcontentlength,omitempty"`
ContentType string `xml:"DAV: prop>getcontenttype,omitempty"`
ETag string `xml:"DAV: prop>getetag,omitempty"`
Modified string `xml:"DAV: prop>getlastmodified,omitempty"`
}
type response struct {
Href string `xml:"DAV: href"`
Props []props `xml:"DAV: propstat"`
}
func getProps(r *response, status string) *props {
for _, prop := range r.Props {
if strings.Contains(prop.Status, status) {
return &prop
}
}
return nil
}
// ReadDir reads the contents of a remote directory
func (c *Client) ReadDir(path string) ([]os.FileInfo, error) {
path = FixSlashes(path)
files := make([]os.FileInfo, 0)
skipSelf := true
parse := func(resp interface{}) error {
r := resp.(*response)
if skipSelf {
skipSelf = false
if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" {
r.Props = nil
return nil
}
return newPathError("ReadDir", path, 405)
}
if p := getProps(r, "200"); p != nil {
f := new(File)
if ps, err := url.QueryUnescape(r.Href); err == nil {
f.name = pathpkg.Base(ps)
} else {
f.name = p.Name
}
f.path = path + f.name
f.modified = parseModified(&p.Modified)
f.etag = p.ETag
f.contentType = p.ContentType
if p.Type.Local == "collection" {
f.path += "/"
f.size = 0
f.isdir = true
} else {
f.size = parseInt64(&p.Size)
f.isdir = false
}
files = append(files, *f)
}
r.Props = nil
return nil
}
err := c.propfind(path, false,
`<d:propfind xmlns:d='DAV:'>
<d:prop>
<d:displayname/>
<d:resourcetype/>
<d:getcontentlength/>
<d:getcontenttype/>
<d:getetag/>
<d:getlastmodified/>
</d:prop>
</d:propfind>`,
&response{},
parse)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
err = newPathErrorErr("ReadDir", path, err)
}
}
return files, err
}
// Stat returns the file stats for a specified path
func (c *Client) Stat(path string) (os.FileInfo, error) {
var f *File
parse := func(resp interface{}) error {
r := resp.(*response)
if p := getProps(r, "200"); p != nil && f == nil {
f = new(File)
if ps, err := url.QueryUnescape(r.Href); err == nil {
f.name = pathpkg.Base(ps)
} else {
f.name = p.Name
}
f.path = path
f.etag = p.ETag
f.contentType = p.ContentType
if p.Type.Local == "collection" {
if !strings.HasSuffix(f.path, "/") {
f.path += "/"
}
f.size = 0
f.modified = time.Unix(0, 0)
f.isdir = true
} else {
f.size = parseInt64(&p.Size)
f.modified = parseModified(&p.Modified)
f.isdir = false
}
}
r.Props = nil
return nil
}
err := c.propfind(path, true,
`<d:propfind xmlns:d='DAV:'>
<d:prop>
<d:displayname/>
<d:resourcetype/>
<d:getcontentlength/>
<d:getcontenttype/>
<d:getetag/>
<d:getlastmodified/>
</d:prop>
</d:propfind>`,
&response{},
parse)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
err = newPathErrorErr("ReadDir", path, err)
}
}
return f, err
}
// Remove removes a remote file
func (c *Client) Remove(path string) error {
return c.RemoveAll(path)
}
// RemoveAll removes remote files
func (c *Client) RemoveAll(path string) error {
rs, err := c.req("DELETE", path, nil, nil)
if err != nil {
return newPathError("Remove", path, 400)
}
err = rs.Body.Close()
if err != nil {
return err
}
if rs.StatusCode == 200 || rs.StatusCode == 204 || rs.StatusCode == 404 {
return nil
}
return newPathError("Remove", path, rs.StatusCode)
}
// Mkdir makes a directory
func (c *Client) Mkdir(path string, _ os.FileMode) error {
path = FixSlashes(path)
status := c.mkcol(path)
if status == 201 {
return nil
}
return newPathError("Mkdir", path, status)
}
// MkdirAll like mkdir -p, but for webdav
func (c *Client) MkdirAll(path string, _ os.FileMode) error {
path = FixSlashes(path)
status := c.mkcol(path)
if status == 201 {
return nil
} else if status == 409 {
paths := strings.Split(path, "/")
sub := "/"
for _, e := range paths {
if e == "" {
continue
}
sub += e + "/"
status = c.mkcol(sub)
if status != 201 {
return newPathError("MkdirAll", sub, status)
}
}
return nil
}
return newPathError("MkdirAll", path, status)
}
// Rename moves a file from A to B
func (c *Client) Rename(oldpath, newpath string, overwrite bool) error {
return c.copymove("MOVE", oldpath, newpath, overwrite)
}
// Copy copies a file from A to B
func (c *Client) Copy(oldpath, newpath string, overwrite bool) error {
return c.copymove("COPY", oldpath, newpath, overwrite)
}
// Read reads the contents of a remote file
func (c *Client) Read(path string) ([]byte, error) {
var stream io.ReadCloser
var err error
if stream, err = c.ReadStream(path); err != nil {
return nil, err
}
defer stream.Close()
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(stream)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// ReadStream reads the stream for a given path
func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
rs, err := c.req("GET", path, nil, nil)
if err != nil {
return nil, newPathErrorErr("ReadStream", path, err)
}
if rs.StatusCode == 200 {
return rs.Body, nil
}
rs.Body.Close()
return nil, newPathError("ReadStream", path, rs.StatusCode)
}
// Write writes data to a given path
func (c *Client) Write(path string, data []byte, _ os.FileMode) error {
s := c.put(path, bytes.NewReader(data))
switch s {
case 200, 201, 204:
return nil
case 409:
err := c.createParentCollection(path)
if err != nil {
return err
}
s = c.put(path, bytes.NewReader(data))
if s == 200 || s == 201 || s == 204 {
return nil
}
}
return newPathError("Write", path, s)
}
// WriteStream writes a stream
func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) error {
err := c.createParentCollection(path)
if err != nil {
return err
}
s := c.put(path, stream)
switch s {
case 200, 201, 204:
return nil
default:
return newPathError("WriteStream", path, s)
}
}

View File

@ -0,0 +1,146 @@
package gowebdav
import (
"crypto/md5"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"net/http"
"strings"
)
// DigestAuth structure holds our credentials
type DigestAuth struct {
user string
pw string
digestParts map[string]string
}
// Type identifies the DigestAuthenticator
func (d *DigestAuth) Type() string {
return "DigestAuth"
}
// User holds the DigestAuth username
func (d *DigestAuth) User() string {
return d.user
}
// Pass holds the DigestAuth password
func (d *DigestAuth) Pass() string {
return d.pw
}
// Authorize the current request
func (d *DigestAuth) Authorize(c *Client, method string, path string) {
d.digestParts["uri"] = path
d.digestParts["method"] = method
d.digestParts["username"] = d.user
d.digestParts["password"] = d.pw
c.headers.Set("Authorization", getDigestAuthorization(d.digestParts))
}
func digestParts(resp *http.Response) map[string]string {
result := map[string]string{}
if len(resp.Header["Www-Authenticate"]) > 0 {
wantedHeaders := []string{"nonce", "realm", "qop", "opaque", "algorithm", "entityBody"}
responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",")
for _, r := range responseHeaders {
for _, w := range wantedHeaders {
if strings.Contains(r, w) {
result[w] = strings.Trim(
strings.SplitN(r, `=`, 2)[1],
`"`,
)
}
}
}
}
return result
}
func getMD5(text string) string {
hasher := md5.New()
hasher.Write([]byte(text))
return hex.EncodeToString(hasher.Sum(nil))
}
func getCnonce() string {
b := make([]byte, 8)
io.ReadFull(rand.Reader, b)
return fmt.Sprintf("%x", b)[:16]
}
func getDigestAuthorization(digestParts map[string]string) string {
d := digestParts
// These are the correct ha1 and ha2 for qop=auth. We should probably check for other types of qop.
var (
ha1 string
ha2 string
nonceCount = 00000001
cnonce = getCnonce()
response string
)
// 'ha1' value depends on value of "algorithm" field
switch d["algorithm"] {
case "MD5", "":
ha1 = getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"])
case "MD5-sess":
ha1 = getMD5(
fmt.Sprintf("%s:%v:%s",
getMD5(d["username"]+":"+d["realm"]+":"+d["password"]),
nonceCount,
cnonce,
),
)
}
// 'ha2' value depends on value of "qop" field
switch d["qop"] {
case "auth", "":
ha2 = getMD5(d["method"] + ":" + d["uri"])
case "auth-int":
if d["entityBody"] != "" {
ha2 = getMD5(d["method"] + ":" + d["uri"] + ":" + getMD5(d["entityBody"]))
}
}
// 'response' value depends on value of "qop" field
switch d["qop"] {
case "":
response = getMD5(
fmt.Sprintf("%s:%s:%s",
ha1,
d["nonce"],
ha2,
),
)
case "auth", "auth-int":
response = getMD5(
fmt.Sprintf("%s:%s:%v:%s:%s:%s",
ha1,
d["nonce"],
nonceCount,
cnonce,
d["qop"],
ha2,
),
)
}
authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", nc=%v, cnonce="%s", response="%s"`,
d["username"], d["realm"], d["nonce"], d["uri"], nonceCount, cnonce, response)
if d["qop"] != "" {
authorization += fmt.Sprintf(`, qop=%s`, d["qop"])
}
if d["opaque"] != "" {
authorization += fmt.Sprintf(`, opaque="%s"`, d["opaque"])
}
return authorization
}

View File

@ -0,0 +1,3 @@
// Package gowebdav is a WebDAV client library with a command line tool
// included.
package gowebdav

View File

@ -0,0 +1,72 @@
package gowebdav
import (
"fmt"
"os"
"time"
)
// File is our structure for a given file
type File struct {
path string
name string
contentType string
size int64
modified time.Time
etag string
isdir bool
}
// Name returns the name of a file
func (f File) Name() string {
return f.name
}
// ContentType returns the content type of a file
func (f File) ContentType() string {
return f.contentType
}
// Size returns the size of a file
func (f File) Size() int64 {
return f.size
}
// Mode will return the mode of a given file
func (f File) Mode() os.FileMode {
// TODO check webdav perms
if f.isdir {
return 0775 | os.ModeDir
}
return 0664
}
// ModTime returns the modified time of a file
func (f File) ModTime() time.Time {
return f.modified
}
// ETag returns the ETag of a file
func (f File) ETag() string {
return f.etag
}
// IsDir let us see if a given file is a directory or not
func (f File) IsDir() bool {
return f.isdir
}
// Sys ????
func (f File) Sys() interface{} {
return nil
}
// String lets us see file information
func (f File) String() string {
if f.isdir {
return fmt.Sprintf("Dir : '%s' - '%s'", f.path, f.name)
}
return fmt.Sprintf("File: '%s' SIZE: %d MODIFIED: %s ETAG: %s CTYPE: %s", f.path, f.size, f.modified.String(), f.etag, f.contentType)
}

View File

@ -0,0 +1,54 @@
package gowebdav
import (
"bufio"
"fmt"
"net/url"
"os"
"regexp"
"strings"
)
func parseLine(s string) (login, pass string) {
fields := strings.Fields(s)
for i, f := range fields {
if f == "login" {
login = fields[i+1]
}
if f == "password" {
pass = fields[i+1]
}
}
return login, pass
}
// ReadConfig reads login and password configuration from ~/.netrc
// machine foo.com login username password 123456
func ReadConfig(uri, netrc string) (string, string) {
u, err := url.Parse(uri)
if err != nil {
return "", ""
}
file, err := os.Open(netrc)
if err != nil {
return "", ""
}
defer file.Close()
re := fmt.Sprintf(`^.*machine %s.*$`, u.Host)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
s := scanner.Text()
matched, err := regexp.MatchString(re, s)
if err != nil {
return "", ""
}
if matched {
return parseLine(s)
}
}
return "", ""
}

View File

@ -0,0 +1,164 @@
package gowebdav
import (
"bytes"
"fmt"
"io"
"net/http"
"path"
"strings"
)
func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (req *http.Response, err error) {
// Tee the body, because if authorization fails we will need to read from it again.
var r *http.Request
var ba bytes.Buffer
bb := io.TeeReader(body, &ba)
if body == nil {
r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), nil)
} else {
r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), bb)
}
if err != nil {
return nil, err
}
c.auth.Authorize(c, method, path)
for k, vals := range c.headers {
for _, v := range vals {
r.Header.Add(k, v)
}
}
if intercept != nil {
intercept(r)
}
rs, err := c.c.Do(r)
if err != nil {
return nil, err
}
if rs.StatusCode == 401 && c.auth.Type() == "NoAuth" {
if strings.Index(rs.Header.Get("Www-Authenticate"), "Digest") > -1 {
c.auth = &DigestAuth{c.auth.User(), c.auth.Pass(), digestParts(rs)}
} else if strings.Index(rs.Header.Get("Www-Authenticate"), "Basic") > -1 {
c.auth = &BasicAuth{c.auth.User(), c.auth.Pass()}
} else {
return rs, newPathError("Authorize", c.root, rs.StatusCode)
}
if body == nil {
return c.req(method, path, nil, intercept)
} else {
return c.req(method, path, &ba, intercept)
}
} else if rs.StatusCode == 401 {
return rs, newPathError("Authorize", c.root, rs.StatusCode)
}
return rs, err
}
func (c *Client) mkcol(path string) int {
rs, err := c.req("MKCOL", path, nil, nil)
if err != nil {
return 400
}
defer rs.Body.Close()
if rs.StatusCode == 201 || rs.StatusCode == 405 {
return 201
}
return rs.StatusCode
}
func (c *Client) options(path string) (*http.Response, error) {
return c.req("OPTIONS", path, nil, func(rq *http.Request) {
rq.Header.Add("Depth", "0")
})
}
func (c *Client) propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{}) error) error {
rs, err := c.req("PROPFIND", path, strings.NewReader(body), func(rq *http.Request) {
if self {
rq.Header.Add("Depth", "0")
} else {
rq.Header.Add("Depth", "1")
}
rq.Header.Add("Content-Type", "application/xml;charset=UTF-8")
rq.Header.Add("Accept", "application/xml,text/xml")
rq.Header.Add("Accept-Charset", "utf-8")
// TODO add support for 'gzip,deflate;q=0.8,q=0.7'
rq.Header.Add("Accept-Encoding", "")
})
if err != nil {
return err
}
defer rs.Body.Close()
if rs.StatusCode != 207 {
return fmt.Errorf("%s - %s %s", rs.Status, "PROPFIND", path)
}
return parseXML(rs.Body, resp, parse)
}
func (c *Client) doCopyMove(method string, oldpath string, newpath string, overwrite bool) (int, io.ReadCloser) {
rs, err := c.req(method, oldpath, nil, func(rq *http.Request) {
rq.Header.Add("Destination", Join(c.root, newpath))
if overwrite {
rq.Header.Add("Overwrite", "T")
} else {
rq.Header.Add("Overwrite", "F")
}
})
if err != nil {
return 400, nil
}
return rs.StatusCode, rs.Body
}
func (c *Client) copymove(method string, oldpath string, newpath string, overwrite bool) error {
s, data := c.doCopyMove(method, oldpath, newpath, overwrite)
defer data.Close()
switch s {
case 201, 204:
return nil
case 207:
// TODO handle multistat errors, worst case ...
log(fmt.Sprintf(" TODO handle %s - %s multistatus result %s", method, oldpath, String(data)))
case 409:
err := c.createParentCollection(newpath)
if err != nil {
return err
}
return c.copymove(method, oldpath, newpath, overwrite)
}
return newPathError(method, oldpath, s)
}
func (c *Client) put(path string, stream io.Reader) int {
rs, err := c.req("PUT", path, stream, nil)
if err != nil {
return 400
}
defer rs.Body.Close()
return rs.StatusCode
}
func (c *Client) createParentCollection(itemPath string) (err error) {
parentPath := path.Dir(itemPath)
return c.MkdirAll(parentPath, 0755)
}

View File

@ -0,0 +1,109 @@
package gowebdav
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"net/url"
"os"
"strconv"
"strings"
"time"
)
func log(msg interface{}) {
fmt.Println(msg)
}
func newPathError(op string, path string, statusCode int) error {
return &os.PathError{
Op: op,
Path: path,
Err: fmt.Errorf("%d", statusCode),
}
}
func newPathErrorErr(op string, path string, err error) error {
return &os.PathError{
Op: op,
Path: path,
Err: err,
}
}
// PathEscape escapes all segemnts of a given path
func PathEscape(path string) string {
s := strings.Split(path, "/")
for i, e := range s {
s[i] = url.PathEscape(e)
}
return strings.Join(s, "/")
}
// FixSlash appends a trailing / to our string
func FixSlash(s string) string {
if !strings.HasSuffix(s, "/") {
s += "/"
}
return s
}
// FixSlashes appends and prepends a / if they are missing
func FixSlashes(s string) string {
if s[0] != '/' {
s = "/" + s
}
return FixSlash(s)
}
// Join joins two paths
func Join(path0 string, path1 string) string {
return strings.TrimSuffix(path0, "/") + "/" + strings.TrimPrefix(path1, "/")
}
// String pulls a string out of our io.Reader
func String(r io.Reader) string {
buf := new(bytes.Buffer)
// TODO - make String return an error as well
_, _ = buf.ReadFrom(r)
return buf.String()
}
func parseUint(s *string) uint {
if n, e := strconv.ParseUint(*s, 10, 32); e == nil {
return uint(n)
}
return 0
}
func parseInt64(s *string) int64 {
if n, e := strconv.ParseInt(*s, 10, 64); e == nil {
return n
}
return 0
}
func parseModified(s *string) time.Time {
if t, e := time.Parse(time.RFC1123, *s); e == nil {
return t
}
return time.Unix(0, 0)
}
func parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) error) error {
decoder := xml.NewDecoder(data)
for t, _ := decoder.Token(); t != nil; t, _ = decoder.Token() {
switch se := t.(type) {
case xml.StartElement:
if se.Name.Local == "response" {
if e := decoder.DecodeElement(resp, &se); e == nil {
if err := parse(resp); err != nil {
return err
}
}
}
}
}
return nil
}

View File

@ -100,6 +100,7 @@ var (
labelsList = []string{}
labelsMap = map[string]bool{}
rules = []string{}
numICANNRules = 0
// validSuffixRE is used to check that the entries in the public suffix
// list are in canonical form (after Punycode encoding). Specifically,
@ -167,11 +168,14 @@ func main1() error {
}
s = strings.TrimSpace(s)
if strings.Contains(s, "BEGIN ICANN DOMAINS") {
if len(rules) != 0 {
return fmt.Errorf(`expected no rules before "BEGIN ICANN DOMAINS"`)
}
icann = true
continue
}
if strings.Contains(s, "END ICANN DOMAINS") {
icann = false
icann, numICANNRules = false, len(rules)
continue
}
if s == "" || strings.HasPrefix(s, "//") {
@ -287,7 +291,7 @@ func gitCommit() (sha, date string, retErr error) {
func printTest(w io.Writer, n *node) error {
fmt.Fprintf(w, "// generated by go run gen.go; DO NOT EDIT\n\n")
fmt.Fprintf(w, "package publicsuffix\n\nvar rules = [...]string{\n")
fmt.Fprintf(w, "package publicsuffix\n\nconst numICANNRules = %d\n\nvar rules = [...]string{\n", numICANNRules)
for _, rule := range rules {
fmt.Fprintf(w, "%q,\n", rule)
}

View File

@ -72,20 +72,24 @@ func (list) String() string {
// publicsuffix.org database compiled into the library.
//
// icann is whether the public suffix is managed by the Internet Corporation
// for Assigned Names and Numbers. If not, the public suffix is privately
// managed. For example, foo.org and foo.co.uk are ICANN domains,
// foo.dyndns.org and foo.blogspot.co.uk are private domains.
// for Assigned Names and Numbers. If not, the public suffix is either a
// privately managed domain (and in practice, not a top level domain) or an
// unmanaged top level domain (and not explicitly mentioned in the
// publicsuffix.org list). For example, "foo.org" and "foo.co.uk" are ICANN
// domains, "foo.dyndns.org" and "foo.blogspot.co.uk" are private domains and
// "cromulent" is an unmanaged top level domain.
//
// Use cases for distinguishing ICANN domains like foo.com from private
// domains like foo.appspot.com can be found at
// Use cases for distinguishing ICANN domains like "foo.com" from private
// domains like "foo.appspot.com" can be found at
// https://wiki.mozilla.org/Public_Suffix_List/Use_Cases
func PublicSuffix(domain string) (publicSuffix string, icann bool) {
lo, hi := uint32(0), uint32(numTLD)
s, suffix, wildcard := domain, len(domain), false
s, suffix, icannNode, wildcard := domain, len(domain), false, false
loop:
for {
dot := strings.LastIndex(s, ".")
if wildcard {
icann = icannNode
suffix = 1 + dot
}
if lo == hi {
@ -97,7 +101,7 @@ loop:
}
u := nodes[f] >> (nodesBitsTextOffset + nodesBitsTextLength)
icann = u&(1<<nodesBitsICANN-1) != 0
icannNode = u&(1<<nodesBitsICANN-1) != 0
u >>= nodesBitsICANN
u = children[u&(1<<nodesBitsChildren-1)]
lo = u & (1<<childrenBitsLo - 1)
@ -113,6 +117,9 @@ loop:
}
u >>= childrenBitsNodeType
wildcard = u&(1<<childrenBitsWildcard-1) != 0
if !wildcard {
icann = icannNode
}
if dot == -1 {
break
@ -158,6 +165,10 @@ func nodeLabel(i uint32) string {
// EffectiveTLDPlusOne returns the effective top level domain plus one more
// label. For example, the eTLD+1 for "foo.bar.golang.org" is "golang.org".
func EffectiveTLDPlusOne(domain string) (string, error) {
if strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") || strings.Contains(domain, "..") {
return "", fmt.Errorf("publicsuffix: empty label in domain %q", domain)
}
suffix, _ := PublicSuffix(domain)
if len(domain) <= len(suffix) {
return "", fmt.Errorf("publicsuffix: cannot derive eTLD+1 for domain %q", domain)

File diff suppressed because it is too large Load Diff

54
vendor/golang.org/x/sys/unix/asm_linux_riscv64.s generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build riscv64,!gccgo
#include "textflag.h"
//
// System calls for linux/riscv64.
//
// Where available, just jump to package syscall's implementation of
// these functions.
TEXT ·Syscall(SB),NOSPLIT,$0-56
JMP syscall·Syscall(SB)
TEXT ·Syscall6(SB),NOSPLIT,$0-80
JMP syscall·Syscall6(SB)
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
CALL runtime·entersyscall(SB)
MOV a1+8(FP), A0
MOV a2+16(FP), A1
MOV a3+24(FP), A2
MOV $0, A3
MOV $0, A4
MOV $0, A5
MOV $0, A6
MOV trap+0(FP), A7 // syscall entry
ECALL
MOV A0, r1+32(FP) // r1
MOV A1, r2+40(FP) // r2
CALL runtime·exitsyscall(SB)
RET
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
JMP syscall·RawSyscall(SB)
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
JMP syscall·RawSyscall6(SB)
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
MOV a1+8(FP), A0
MOV a2+16(FP), A1
MOV a3+24(FP), A2
MOV ZERO, A3
MOV ZERO, A4
MOV ZERO, A5
MOV trap+0(FP), A7 // syscall entry
ECALL
MOV A0, r1+32(FP)
MOV A1, r2+40(FP)
RET

29
vendor/golang.org/x/sys/unix/asm_openbsd_arm64.s generated vendored Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !gccgo
#include "textflag.h"
//
// System call support for arm64, OpenBSD
//
// Just jump to package syscall's implementation for all these functions.
// The runtime may know about them.
TEXT ·Syscall(SB),NOSPLIT,$0-56
JMP syscall·Syscall(SB)
TEXT ·Syscall6(SB),NOSPLIT,$0-80
JMP syscall·Syscall6(SB)
TEXT ·Syscall9(SB),NOSPLIT,$0-104
JMP syscall·Syscall9(SB)
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
JMP syscall·RawSyscall(SB)
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
JMP syscall·RawSyscall6(SB)

View File

@ -6,12 +6,97 @@
package unix
import "syscall"
import "unsafe"
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
if len(b) < int(off+size) {
return 0, false
}
if isBigEndian {
return readIntBE(b[off:], size), true
}
return readIntLE(b[off:], size), true
}
func readIntBE(b []byte, size uintptr) uint64 {
switch size {
case 1:
return uint64(b[0])
case 2:
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[1]) | uint64(b[0])<<8
case 4:
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24
case 8:
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
default:
panic("syscall: readInt with unsupported size")
}
}
func readIntLE(b []byte, size uintptr) uint64 {
switch size {
case 1:
return uint64(b[0])
case 2:
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8
case 4:
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24
case 8:
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
default:
panic("syscall: readInt with unsupported size")
}
}
// ParseDirent parses up to max directory entries in buf,
// appending the names to names. It returns the number of
// bytes consumed from buf, the number of entries added
// to names, and the new names slice.
func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
return syscall.ParseDirent(buf, max, names)
origlen := len(buf)
count = 0
for max != 0 && len(buf) > 0 {
reclen, ok := direntReclen(buf)
if !ok || reclen > uint64(len(buf)) {
return origlen, count, names
}
rec := buf[:reclen]
buf = buf[reclen:]
ino, ok := direntIno(rec)
if !ok {
break
}
if ino == 0 { // File absent in directory.
continue
}
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
namlen, ok := direntNamlen(rec)
if !ok || namoff+namlen > uint64(len(rec)) {
break
}
name := rec[namoff : namoff+namlen]
for i, c := range name {
if c == 0 {
name = name[:i]
break
}
}
// Check for useless names before allocating a string.
if string(name) == "." || string(name) == ".." {
continue
}
max--
count++
names = append(names, string(name))
}
return origlen - len(buf), count, names
}

View File

@ -105,25 +105,25 @@ dragonfly_amd64)
freebsd_386)
mkerrors="$mkerrors -m32"
mksyscall="go run mksyscall.go -l32"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
freebsd_amd64)
mkerrors="$mkerrors -m64"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
freebsd_arm)
mkerrors="$mkerrors"
mksyscall="go run mksyscall.go -l32 -arm"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'"
# Let the type of C char be signed for making the bare syscall
# API consistent across platforms.
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
;;
freebsd_arm64)
mkerrors="$mkerrors -m64"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master'"
mksysnum="go run mksysnum.go 'https://svn.freebsd.org/base/stable/11/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
netbsd_386)
@ -146,24 +146,39 @@ netbsd_arm)
# API consistent across platforms.
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
;;
netbsd_arm64)
mkerrors="$mkerrors -m64"
mksyscall="go run mksyscall.go -netbsd"
mksysnum="go run mksysnum.go 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
openbsd_386)
mkerrors="$mkerrors -m32"
mksyscall="go run mksyscall.go -l32 -openbsd"
mksysctl="./mksysctl_openbsd.pl"
mksysctl="go run mksysctl_openbsd.go"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
openbsd_amd64)
mkerrors="$mkerrors -m64"
mksyscall="go run mksyscall.go -openbsd"
mksysctl="./mksysctl_openbsd.pl"
mksysctl="go run mksysctl_openbsd.go"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
openbsd_arm)
mkerrors="$mkerrors"
mksyscall="go run mksyscall.go -l32 -openbsd -arm"
mksysctl="./mksysctl_openbsd.pl"
mksysctl="go run mksysctl_openbsd.go"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
# Let the type of C char be signed for making the bare syscall
# API consistent across platforms.
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
;;
openbsd_arm64)
mkerrors="$mkerrors -m64"
mksyscall="go run mksyscall.go -openbsd"
mksysctl="go run mksysctl_openbsd.go"
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
# Let the type of C char be signed for making the bare syscall
# API consistent across platforms.

View File

@ -182,6 +182,8 @@ struct ltchars {
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/xattr.h>
#include <linux/bpf.h>
#include <linux/capability.h>
#include <linux/errqueue.h>
#include <linux/if.h>
#include <linux/if_alg.h>
@ -197,6 +199,7 @@ struct ltchars {
#include <linux/fs.h>
#include <linux/kexec.h>
#include <linux/keyctl.h>
#include <linux/loop.h>
#include <linux/magic.h>
#include <linux/memfd.h>
#include <linux/module.h>
@ -222,6 +225,7 @@ struct ltchars {
#include <linux/hdreg.h>
#include <linux/rtc.h>
#include <linux/if_xdp.h>
#include <linux/cryptouser.h>
#include <mtd/ubi-user.h>
#include <net/route.h>
@ -432,7 +436,9 @@ ccflags="$@"
$2 ~ /^TC[IO](ON|OFF)$/ ||
$2 ~ /^IN_/ ||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
$2 ~ /^LO_(KEY|NAME)_SIZE$/ ||
$2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ ||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|MCAST|EVFILT|NOTE|EV|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
$2 ~ /^TP_STATUS_/ ||
$2 ~ /^FALLOC_/ ||
$2 == "ICMPV6_FILTER" ||
@ -465,7 +471,7 @@ ccflags="$@"
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ ||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
$2 ~ /^CLONE_[A-Z_]+/ ||
$2 !~ /^(BPF_TIMEVAL)$/ &&
$2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ &&
$2 ~ /^(BPF|DLT)_/ ||
$2 ~ /^(CLOCK|TIMER)_/ ||
$2 ~ /^CAN_/ ||
@ -499,6 +505,7 @@ ccflags="$@"
$2 ~ /^NFN/ ||
$2 ~ /^XDP_/ ||
$2 ~ /^(HDIO|WIN|SMART)_/ ||
$2 ~ /^CRYPTO_/ ||
$2 !~ "WMESGLEN" &&
$2 ~ /^W[A-Z0-9]+$/ ||
$2 ~/^PPPIOC/ ||

View File

@ -42,9 +42,16 @@ func main() {
log.Fatal(err)
}
if goos == "aix" {
// Replace type of Atim, Mtim and Ctim by Timespec in Stat_t
// to avoid having both StTimespec and Timespec.
sttimespec := regexp.MustCompile(`_Ctype_struct_st_timespec`)
b = sttimespec.ReplaceAll(b, []byte("Timespec"))
}
// Intentionally export __val fields in Fsid and Sigset_t
valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__val(\s+\S+\s+)}`)
b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$3}"))
valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__(bits|val)(\s+\S+\s+)}`)
b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$4}"))
// Intentionally export __fds_bits field in FdSet
fdSetRegex := regexp.MustCompile(`type (FdSet) struct {(\s+)X__fds_bits(\s+\S+\s+)}`)
@ -96,6 +103,15 @@ func main() {
cgoCommandRegex := regexp.MustCompile(`(cgo -godefs .*)`)
b = cgoCommandRegex.ReplaceAll(b, []byte(replacement))
// Rename Stat_t time fields
if goos == "freebsd" && goarch == "386" {
// Hide Stat_t.[AMCB]tim_ext fields
renameStatTimeExtFieldsRegex := regexp.MustCompile(`[AMCB]tim_ext`)
b = renameStatTimeExtFieldsRegex.ReplaceAll(b, []byte("_"))
}
renameStatTimeFieldsRegex := regexp.MustCompile(`([AMCB])(?:irth)?time?(?:spec)?\s+(Timespec|StTimespec)`)
b = renameStatTimeFieldsRegex.ReplaceAll(b, []byte("${1}tim ${2}"))
// gofmt
b, err = format.Source(b)
if err != nil {

355
vendor/golang.org/x/sys/unix/mksysctl_openbsd.go generated vendored Normal file
View File

@ -0,0 +1,355 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
// Parse the header files for OpenBSD and generate a Go usable sysctl MIB.
//
// Build a MIB with each entry being an array containing the level, type and
// a hash that will contain additional entries if the current entry is a node.
// We then walk this MIB and create a flattened sysctl name to OID hash.
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
)
var (
goos, goarch string
)
// cmdLine returns this programs's commandline arguments.
func cmdLine() string {
return "go run mksysctl_openbsd.go " + strings.Join(os.Args[1:], " ")
}
// buildTags returns build tags.
func buildTags() string {
return fmt.Sprintf("%s,%s", goarch, goos)
}
// reMatch performs regular expression match and stores the substring slice to value pointed by m.
func reMatch(re *regexp.Regexp, str string, m *[]string) bool {
*m = re.FindStringSubmatch(str)
if *m != nil {
return true
}
return false
}
type nodeElement struct {
n int
t string
pE *map[string]nodeElement
}
var (
debugEnabled bool
mib map[string]nodeElement
node *map[string]nodeElement
nodeMap map[string]string
sysCtl []string
)
var (
ctlNames1RE = regexp.MustCompile(`^#define\s+(CTL_NAMES)\s+{`)
ctlNames2RE = regexp.MustCompile(`^#define\s+(CTL_(.*)_NAMES)\s+{`)
ctlNames3RE = regexp.MustCompile(`^#define\s+((.*)CTL_NAMES)\s+{`)
netInetRE = regexp.MustCompile(`^netinet/`)
netInet6RE = regexp.MustCompile(`^netinet6/`)
netRE = regexp.MustCompile(`^net/`)
bracesRE = regexp.MustCompile(`{.*}`)
ctlTypeRE = regexp.MustCompile(`{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}`)
fsNetKernRE = regexp.MustCompile(`^(fs|net|kern)_`)
)
func debug(s string) {
if debugEnabled {
fmt.Fprintln(os.Stderr, s)
}
}
// Walk the MIB and build a sysctl name to OID mapping.
func buildSysctl(pNode *map[string]nodeElement, name string, oid []int) {
lNode := pNode // local copy of pointer to node
var keys []string
for k := range *lNode {
keys = append(keys, k)
}
sort.Strings(keys)
for _, key := range keys {
nodename := name
if name != "" {
nodename += "."
}
nodename += key
nodeoid := append(oid, (*pNode)[key].n)
if (*pNode)[key].t == `CTLTYPE_NODE` {
if _, ok := nodeMap[nodename]; ok {
lNode = &mib
ctlName := nodeMap[nodename]
for _, part := range strings.Split(ctlName, ".") {
lNode = ((*lNode)[part]).pE
}
} else {
lNode = (*pNode)[key].pE
}
buildSysctl(lNode, nodename, nodeoid)
} else if (*pNode)[key].t != "" {
oidStr := []string{}
for j := range nodeoid {
oidStr = append(oidStr, fmt.Sprintf("%d", nodeoid[j]))
}
text := "\t{ \"" + nodename + "\", []_C_int{ " + strings.Join(oidStr, ", ") + " } }, \n"
sysCtl = append(sysCtl, text)
}
}
}
func main() {
// Get the OS (using GOOS_TARGET if it exist)
goos = os.Getenv("GOOS_TARGET")
if goos == "" {
goos = os.Getenv("GOOS")
}
// Get the architecture (using GOARCH_TARGET if it exists)
goarch = os.Getenv("GOARCH_TARGET")
if goarch == "" {
goarch = os.Getenv("GOARCH")
}
// Check if GOOS and GOARCH environment variables are defined
if goarch == "" || goos == "" {
fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n")
os.Exit(1)
}
mib = make(map[string]nodeElement)
headers := [...]string{
`sys/sysctl.h`,
`sys/socket.h`,
`sys/tty.h`,
`sys/malloc.h`,
`sys/mount.h`,
`sys/namei.h`,
`sys/sem.h`,
`sys/shm.h`,
`sys/vmmeter.h`,
`uvm/uvmexp.h`,
`uvm/uvm_param.h`,
`uvm/uvm_swap_encrypt.h`,
`ddb/db_var.h`,
`net/if.h`,
`net/if_pfsync.h`,
`net/pipex.h`,
`netinet/in.h`,
`netinet/icmp_var.h`,
`netinet/igmp_var.h`,
`netinet/ip_ah.h`,
`netinet/ip_carp.h`,
`netinet/ip_divert.h`,
`netinet/ip_esp.h`,
`netinet/ip_ether.h`,
`netinet/ip_gre.h`,
`netinet/ip_ipcomp.h`,
`netinet/ip_ipip.h`,
`netinet/pim_var.h`,
`netinet/tcp_var.h`,
`netinet/udp_var.h`,
`netinet6/in6.h`,
`netinet6/ip6_divert.h`,
`netinet6/pim6_var.h`,
`netinet/icmp6.h`,
`netmpls/mpls.h`,
}
ctls := [...]string{
`kern`,
`vm`,
`fs`,
`net`,
//debug /* Special handling required */
`hw`,
//machdep /* Arch specific */
`user`,
`ddb`,
//vfs /* Special handling required */
`fs.posix`,
`kern.forkstat`,
`kern.intrcnt`,
`kern.malloc`,
`kern.nchstats`,
`kern.seminfo`,
`kern.shminfo`,
`kern.timecounter`,
`kern.tty`,
`kern.watchdog`,
`net.bpf`,
`net.ifq`,
`net.inet`,
`net.inet.ah`,
`net.inet.carp`,
`net.inet.divert`,
`net.inet.esp`,
`net.inet.etherip`,
`net.inet.gre`,
`net.inet.icmp`,
`net.inet.igmp`,
`net.inet.ip`,
`net.inet.ip.ifq`,
`net.inet.ipcomp`,
`net.inet.ipip`,
`net.inet.mobileip`,
`net.inet.pfsync`,
`net.inet.pim`,
`net.inet.tcp`,
`net.inet.udp`,
`net.inet6`,
`net.inet6.divert`,
`net.inet6.ip6`,
`net.inet6.icmp6`,
`net.inet6.pim6`,
`net.inet6.tcp6`,
`net.inet6.udp6`,
`net.mpls`,
`net.mpls.ifq`,
`net.key`,
`net.pflow`,
`net.pfsync`,
`net.pipex`,
`net.rt`,
`vm.swapencrypt`,
//vfsgenctl /* Special handling required */
}
// Node name "fixups"
ctlMap := map[string]string{
"ipproto": "net.inet",
"net.inet.ipproto": "net.inet",
"net.inet6.ipv6proto": "net.inet6",
"net.inet6.ipv6": "net.inet6.ip6",
"net.inet.icmpv6": "net.inet6.icmp6",
"net.inet6.divert6": "net.inet6.divert",
"net.inet6.tcp6": "net.inet.tcp",
"net.inet6.udp6": "net.inet.udp",
"mpls": "net.mpls",
"swpenc": "vm.swapencrypt",
}
// Node mappings
nodeMap = map[string]string{
"net.inet.ip.ifq": "net.ifq",
"net.inet.pfsync": "net.pfsync",
"net.mpls.ifq": "net.ifq",
}
mCtls := make(map[string]bool)
for _, ctl := range ctls {
mCtls[ctl] = true
}
for _, header := range headers {
debug("Processing " + header)
file, err := os.Open(filepath.Join("/usr/include", header))
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
s := bufio.NewScanner(file)
for s.Scan() {
var sub []string
if reMatch(ctlNames1RE, s.Text(), &sub) ||
reMatch(ctlNames2RE, s.Text(), &sub) ||
reMatch(ctlNames3RE, s.Text(), &sub) {
if sub[1] == `CTL_NAMES` {
// Top level.
node = &mib
} else {
// Node.
nodename := strings.ToLower(sub[2])
ctlName := ""
if reMatch(netInetRE, header, &sub) {
ctlName = "net.inet." + nodename
} else if reMatch(netInet6RE, header, &sub) {
ctlName = "net.inet6." + nodename
} else if reMatch(netRE, header, &sub) {
ctlName = "net." + nodename
} else {
ctlName = nodename
ctlName = fsNetKernRE.ReplaceAllString(ctlName, `$1.`)
}
if val, ok := ctlMap[ctlName]; ok {
ctlName = val
}
if _, ok := mCtls[ctlName]; !ok {
debug("Ignoring " + ctlName + "...")
continue
}
// Walk down from the top of the MIB.
node = &mib
for _, part := range strings.Split(ctlName, ".") {
if _, ok := (*node)[part]; !ok {
debug("Missing node " + part)
(*node)[part] = nodeElement{n: 0, t: "", pE: &map[string]nodeElement{}}
}
node = (*node)[part].pE
}
}
// Populate current node with entries.
i := -1
for !strings.HasPrefix(s.Text(), "}") {
s.Scan()
if reMatch(bracesRE, s.Text(), &sub) {
i++
}
if !reMatch(ctlTypeRE, s.Text(), &sub) {
continue
}
(*node)[sub[1]] = nodeElement{n: i, t: sub[2], pE: &map[string]nodeElement{}}
}
}
}
err = s.Err()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
file.Close()
}
buildSysctl(&mib, "", []int{})
sort.Strings(sysCtl)
text := strings.Join(sysCtl, "")
fmt.Printf(srcTemplate, cmdLine(), buildTags(), text)
}
const srcTemplate = `// %s
// Code generated by the command above; DO NOT EDIT.
// +build %s
package unix
type mibentry struct {
ctlname string
ctloid []_C_int
}
var sysctlMib = []mibentry {
%s
}
`

View File

@ -1,265 +0,0 @@
#!/usr/bin/env perl
# Copyright 2011 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
#
# Parse the header files for OpenBSD and generate a Go usable sysctl MIB.
#
# Build a MIB with each entry being an array containing the level, type and
# a hash that will contain additional entries if the current entry is a node.
# We then walk this MIB and create a flattened sysctl name to OID hash.
#
use strict;
if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
print STDERR "GOARCH or GOOS not defined in environment\n";
exit 1;
}
my $debug = 0;
my %ctls = ();
my @headers = qw (
sys/sysctl.h
sys/socket.h
sys/tty.h
sys/malloc.h
sys/mount.h
sys/namei.h
sys/sem.h
sys/shm.h
sys/vmmeter.h
uvm/uvmexp.h
uvm/uvm_param.h
uvm/uvm_swap_encrypt.h
ddb/db_var.h
net/if.h
net/if_pfsync.h
net/pipex.h
netinet/in.h
netinet/icmp_var.h
netinet/igmp_var.h
netinet/ip_ah.h
netinet/ip_carp.h
netinet/ip_divert.h
netinet/ip_esp.h
netinet/ip_ether.h
netinet/ip_gre.h
netinet/ip_ipcomp.h
netinet/ip_ipip.h
netinet/pim_var.h
netinet/tcp_var.h
netinet/udp_var.h
netinet6/in6.h
netinet6/ip6_divert.h
netinet6/pim6_var.h
netinet/icmp6.h
netmpls/mpls.h
);
my @ctls = qw (
kern
vm
fs
net
#debug # Special handling required
hw
#machdep # Arch specific
user
ddb
#vfs # Special handling required
fs.posix
kern.forkstat
kern.intrcnt
kern.malloc
kern.nchstats
kern.seminfo
kern.shminfo
kern.timecounter
kern.tty
kern.watchdog
net.bpf
net.ifq
net.inet
net.inet.ah
net.inet.carp
net.inet.divert
net.inet.esp
net.inet.etherip
net.inet.gre
net.inet.icmp
net.inet.igmp
net.inet.ip
net.inet.ip.ifq
net.inet.ipcomp
net.inet.ipip
net.inet.mobileip
net.inet.pfsync
net.inet.pim
net.inet.tcp
net.inet.udp
net.inet6
net.inet6.divert
net.inet6.ip6
net.inet6.icmp6
net.inet6.pim6
net.inet6.tcp6
net.inet6.udp6
net.mpls
net.mpls.ifq
net.key
net.pflow
net.pfsync
net.pipex
net.rt
vm.swapencrypt
#vfsgenctl # Special handling required
);
# Node name "fixups"
my %ctl_map = (
"ipproto" => "net.inet",
"net.inet.ipproto" => "net.inet",
"net.inet6.ipv6proto" => "net.inet6",
"net.inet6.ipv6" => "net.inet6.ip6",
"net.inet.icmpv6" => "net.inet6.icmp6",
"net.inet6.divert6" => "net.inet6.divert",
"net.inet6.tcp6" => "net.inet.tcp",
"net.inet6.udp6" => "net.inet.udp",
"mpls" => "net.mpls",
"swpenc" => "vm.swapencrypt"
);
# Node mappings
my %node_map = (
"net.inet.ip.ifq" => "net.ifq",
"net.inet.pfsync" => "net.pfsync",
"net.mpls.ifq" => "net.ifq"
);
my $ctlname;
my %mib = ();
my %sysctl = ();
my $node;
sub debug() {
print STDERR "$_[0]\n" if $debug;
}
# Walk the MIB and build a sysctl name to OID mapping.
sub build_sysctl() {
my ($node, $name, $oid) = @_;
my %node = %{$node};
my @oid = @{$oid};
foreach my $key (sort keys %node) {
my @node = @{$node{$key}};
my $nodename = $name.($name ne '' ? '.' : '').$key;
my @nodeoid = (@oid, $node[0]);
if ($node[1] eq 'CTLTYPE_NODE') {
if (exists $node_map{$nodename}) {
$node = \%mib;
$ctlname = $node_map{$nodename};
foreach my $part (split /\./, $ctlname) {
$node = \%{@{$$node{$part}}[2]};
}
} else {
$node = $node[2];
}
&build_sysctl($node, $nodename, \@nodeoid);
} elsif ($node[1] ne '') {
$sysctl{$nodename} = \@nodeoid;
}
}
}
foreach my $ctl (@ctls) {
$ctls{$ctl} = $ctl;
}
# Build MIB
foreach my $header (@headers) {
&debug("Processing $header...");
open HEADER, "/usr/include/$header" ||
print STDERR "Failed to open $header\n";
while (<HEADER>) {
if ($_ =~ /^#define\s+(CTL_NAMES)\s+{/ ||
$_ =~ /^#define\s+(CTL_(.*)_NAMES)\s+{/ ||
$_ =~ /^#define\s+((.*)CTL_NAMES)\s+{/) {
if ($1 eq 'CTL_NAMES') {
# Top level.
$node = \%mib;
} else {
# Node.
my $nodename = lc($2);
if ($header =~ /^netinet\//) {
$ctlname = "net.inet.$nodename";
} elsif ($header =~ /^netinet6\//) {
$ctlname = "net.inet6.$nodename";
} elsif ($header =~ /^net\//) {
$ctlname = "net.$nodename";
} else {
$ctlname = "$nodename";
$ctlname =~ s/^(fs|net|kern)_/$1\./;
}
if (exists $ctl_map{$ctlname}) {
$ctlname = $ctl_map{$ctlname};
}
if (not exists $ctls{$ctlname}) {
&debug("Ignoring $ctlname...");
next;
}
# Walk down from the top of the MIB.
$node = \%mib;
foreach my $part (split /\./, $ctlname) {
if (not exists $$node{$part}) {
&debug("Missing node $part");
$$node{$part} = [ 0, '', {} ];
}
$node = \%{@{$$node{$part}}[2]};
}
}
# Populate current node with entries.
my $i = -1;
while (defined($_) && $_ !~ /^}/) {
$_ = <HEADER>;
$i++ if $_ =~ /{.*}/;
next if $_ !~ /{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}/;
$$node{$1} = [ $i, $2, {} ];
}
}
}
close HEADER;
}
&build_sysctl(\%mib, "", []);
print <<EOF;
// mksysctl_openbsd.pl
// Code generated by the command above; DO NOT EDIT.
// +build $ENV{'GOARCH'},$ENV{'GOOS'}
package unix;
type mibentry struct {
ctlname string
ctloid []_C_int
}
var sysctlMib = []mibentry {
EOF
foreach my $name (sort keys %sysctl) {
my @oid = @{$sysctl{$name}};
print "\t{ \"$name\", []_C_int{ ", join(', ', @oid), " } }, \n";
}
print <<EOF;
}
EOF

View File

@ -139,7 +139,7 @@ func main() {
text += format(name, num, proto)
}
case "freebsd":
if t.Match(`^([0-9]+)\s+\S+\s+(?:NO)?STD\s+({ \S+\s+(\w+).*)$`) {
if t.Match(`^([0-9]+)\s+\S+\s+(?:(?:NO)?STD|COMPAT10)\s+({ \S+\s+(\w+).*)$`) {
num, proto := t.sub[1], t.sub[2]
name := fmt.Sprintf("SYS_%s", t.sub[3])
text += format(name, num, proto)

View File

@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build openbsd
// +build 386 amd64 arm
package unix
import (

12
vendor/golang.org/x/sys/unix/readdirent_getdents.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build aix dragonfly freebsd linux netbsd openbsd
package unix
// ReadDirent reads directory entries from fd and writes them into buf.
func ReadDirent(fd int, buf []byte) (n int, err error) {
return Getdents(fd, buf)
}

View File

@ -0,0 +1,19 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin
package unix
import "unsafe"
// ReadDirent reads directory entries from fd and writes them into buf.
func ReadDirent(fd int, buf []byte) (n int, err error) {
// Final argument is (basep *uintptr) and the syscall doesn't take nil.
// 64 bits should be enough. (32 bits isn't even on 386). Since the
// actual system call is getdirentries64, 64 is a good guess.
// TODO(rsc): Can we use a single global basep for all calls?
var base = (*uintptr)(unsafe.Pointer(new(uint64)))
return Getdirentries(fd, buf, base)
}

View File

@ -21,10 +21,10 @@ func cmsgAlignOf(salen int) int {
case "aix":
// There is no alignment on AIX.
salign = 1
case "darwin", "dragonfly", "solaris":
// NOTE: It seems like 64-bit Darwin, DragonFly BSD and
// Solaris kernels still require 32-bit aligned access to
// network subsystem.
case "darwin", "dragonfly", "solaris", "illumos":
// NOTE: It seems like 64-bit Darwin, DragonFly BSD,
// illumos, and Solaris kernels still require 32-bit
// aligned access to network subsystem.
if SizeofPtr == 8 {
salign = 4
}

View File

@ -50,5 +50,4 @@ func BytePtrFromString(s string) (*byte, error) {
}
// Single-word zero for use when we need a valid pointer to 0 bytes.
// See mkunix.pl.
var _zero uintptr

View File

@ -280,8 +280,24 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
return -1, ENOSYS
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}
func direntNamlen(buf []byte) (uint64, bool) {
reclen, ok := direntReclen(buf)
if !ok {
return 0, false
}
return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
}
//sys getdirent(fd int, buf []byte) (n int, err error)
func ReadDirent(fd int, buf []byte) (n int, err error) {
func Getdents(fd int, buf []byte) (n int, err error) {
return getdirent(fd, buf)
}
@ -454,8 +470,8 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) {
//sys Dup2(oldfd int, newfd int) (err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = posix_fadvise64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = fstatat
//sys fstat(fd int, stat *Stat_t) (err error)
//sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = fstatat
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
//sys Ftruncate(fd int, length int64) (err error)
//sysnb Getegid() (egid int)
@ -464,7 +480,7 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) {
//sysnb Getuid() (uid int)
//sys Lchown(path string, uid int, gid int) (err error)
//sys Listen(s int, n int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error)
//sys lstat(path string, stat *Stat_t) (err error)
//sys Pause() (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = pread64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = pwrite64
@ -474,7 +490,7 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) {
//sysnb Setreuid(ruid int, euid int) (err error)
//sys Shutdown(fd int, how int) (err error)
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
//sys Stat(path string, stat *Stat_t) (err error)
//sys stat(path string, statptr *Stat_t) (err error)
//sys Statfs(path string, buf *Statfs_t) (err error)
//sys Truncate(path string, length int64) (err error)

View File

@ -32,3 +32,19 @@ func (msghdr *Msghdr) SetControllen(length int) {
func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
func Fstat(fd int, stat *Stat_t) error {
return fstat(fd, stat)
}
func Fstatat(dirfd int, path string, stat *Stat_t, flags int) error {
return fstatat(dirfd, path, stat, flags)
}
func Lstat(path string, stat *Stat_t) error {
return lstat(path, stat)
}
func Stat(path string, statptr *Stat_t) error {
return stat(path, statptr)
}

View File

@ -32,3 +32,50 @@ func (msghdr *Msghdr) SetControllen(length int) {
func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
// In order to only have Timespec structure, type of Stat_t's fields
// Atim, Mtim and Ctim is changed from StTimespec to Timespec during
// ztypes generation.
// On ppc64, Timespec.Nsec is an int64 while StTimespec.Nsec is an
// int32, so the fields' value must be modified.
func fixStatTimFields(stat *Stat_t) {
stat.Atim.Nsec >>= 32
stat.Mtim.Nsec >>= 32
stat.Ctim.Nsec >>= 32
}
func Fstat(fd int, stat *Stat_t) error {
err := fstat(fd, stat)
if err != nil {
return err
}
fixStatTimFields(stat)
return nil
}
func Fstatat(dirfd int, path string, stat *Stat_t, flags int) error {
err := fstatat(dirfd, path, stat, flags)
if err != nil {
return err
}
fixStatTimFields(stat)
return nil
}
func Lstat(path string, stat *Stat_t) error {
err := lstat(path, stat)
if err != nil {
return err
}
fixStatTimFields(stat)
return nil
}
func Stat(path string, statptr *Stat_t) error {
err := stat(path, statptr)
if err != nil {
return err
}
fixStatTimFields(statptr)
return nil
}

View File

@ -63,15 +63,6 @@ func Setgroups(gids []int) (err error) {
return setgroups(len(a), &a[0])
}
func ReadDirent(fd int, buf []byte) (n int, err error) {
// Final argument is (basep *uintptr) and the syscall doesn't take nil.
// 64 bits should be enough. (32 bits isn't even on 386). Since the
// actual system call is getdirentries64, 64 is a good guess.
// TODO(rsc): Can we use a single global basep for all calls?
var base = (*uintptr)(unsafe.Pointer(new(uint64)))
return Getdirentries(fd, buf, base)
}
// Wait status is 7 bits at bottom, either 0 (exited),
// 0x7F (stopped), or a signal number that caused an exit.
// The 0x80 bit is whether there was a core dump.
@ -86,6 +77,7 @@ const (
shift = 8
exited = 0
killed = 9
stopped = 0x7F
)
@ -112,6 +104,8 @@ func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }
func (w WaitStatus) Stopped() bool { return w&mask == stopped && syscall.Signal(w>>shift) != SIGSTOP }
func (w WaitStatus) Killed() bool { return w&mask == killed && syscall.Signal(w>>shift) != SIGKILL }
func (w WaitStatus) Continued() bool { return w&mask == stopped && syscall.Signal(w>>shift) == SIGSTOP }
func (w WaitStatus) StopSignal() syscall.Signal {

View File

@ -77,6 +77,18 @@ func nametomib(name string) (mib []_C_int, err error) {
return buf[0 : n/siz], nil
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}
func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}
//sys ptrace(request int, pid int, addr uintptr, data uintptr) (err error)
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }

View File

@ -57,6 +57,22 @@ func nametomib(name string) (mib []_C_int, err error) {
return buf[0 : n/siz], nil
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
}
func direntReclen(buf []byte) (uint64, bool) {
namlen, ok := direntNamlen(buf)
if !ok {
return 0, false
}
return (16 + namlen + 1 + 7) &^ 7, true
}
func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}
//sysnb pipe() (r int, w int, err error)
func Pipe(p []int) (err error) {
@ -269,6 +285,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Fstatfs(fd int, stat *Statfs_t) (err error)
//sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error)
//sys Getdents(fd int, buf []byte) (n int, err error)
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys Getdtablesize() (size int)
//sysnb Getegid() (egid int)

View File

@ -82,6 +82,18 @@ func nametomib(name string) (mib []_C_int, err error) {
return buf[0 : n/siz], nil
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}
func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}
func Pipe(p []int) (err error) {
return Pipe2(p, 0)
}
@ -362,7 +374,21 @@ func Getdents(fd int, buf []byte) (n int, err error) {
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
if supportsABI(_ino64First) {
return getdirentries_freebsd12(fd, buf, basep)
if basep == nil || unsafe.Sizeof(*basep) == 8 {
return getdirentries_freebsd12(fd, buf, (*uint64)(unsafe.Pointer(basep)))
}
// The freebsd12 syscall needs a 64-bit base. On 32-bit machines
// we can't just use the basep passed in. See #32498.
var base uint64 = uint64(*basep)
n, err = getdirentries_freebsd12(fd, buf, &base)
*basep = uintptr(base)
if base>>32 != 0 {
// We can't stuff the base back into a uintptr, so any
// future calls would be suspect. Generate an error.
// EIO is allowed by getdirentries.
err = EIO
}
return
}
// The old syscall entries are smaller than the new. Use 1/4 of the original
@ -404,22 +430,22 @@ func roundup(x, y int) int {
func (s *Stat_t) convertFrom(old *stat_freebsd11_t) {
*s = Stat_t{
Dev: uint64(old.Dev),
Ino: uint64(old.Ino),
Nlink: uint64(old.Nlink),
Mode: old.Mode,
Uid: old.Uid,
Gid: old.Gid,
Rdev: uint64(old.Rdev),
Atim: old.Atim,
Mtim: old.Mtim,
Ctim: old.Ctim,
Birthtim: old.Birthtim,
Size: old.Size,
Blocks: old.Blocks,
Blksize: old.Blksize,
Flags: old.Flags,
Gen: uint64(old.Gen),
Dev: uint64(old.Dev),
Ino: uint64(old.Ino),
Nlink: uint64(old.Nlink),
Mode: old.Mode,
Uid: old.Uid,
Gid: old.Gid,
Rdev: uint64(old.Rdev),
Atim: old.Atim,
Mtim: old.Mtim,
Ctim: old.Ctim,
Btim: old.Btim,
Size: old.Size,
Blocks: old.Blocks,
Blksize: old.Blksize,
Flags: old.Flags,
Gen: uint64(old.Gen),
}
}
@ -507,6 +533,70 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
return sendfile(outfd, infd, offset, count)
}
//sys ptrace(request int, pid int, addr uintptr, data int) (err error)
func PtraceAttach(pid int) (err error) {
return ptrace(PTRACE_ATTACH, pid, 0, 0)
}
func PtraceCont(pid int, signal int) (err error) {
return ptrace(PTRACE_CONT, pid, 1, signal)
}
func PtraceDetach(pid int) (err error) {
return ptrace(PTRACE_DETACH, pid, 1, 0)
}
func PtraceGetFpRegs(pid int, fpregsout *FpReg) (err error) {
return ptrace(PTRACE_GETFPREGS, pid, uintptr(unsafe.Pointer(fpregsout)), 0)
}
func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
return ptrace(PTRACE_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0)
}
func PtraceGetRegs(pid int, regsout *Reg) (err error) {
return ptrace(PTRACE_GETREGS, pid, uintptr(unsafe.Pointer(regsout)), 0)
}
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint(countin)}
err = ptrace(PTRACE_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
return int(ioDesc.Len), err
}
func PtraceLwpEvents(pid int, enable int) (err error) {
return ptrace(PTRACE_LWPEVENTS, pid, 0, enable)
}
func PtraceLwpInfo(pid int, info uintptr) (err error) {
return ptrace(PTRACE_LWPINFO, pid, info, int(unsafe.Sizeof(PtraceLwpInfoStruct{})))
}
func PtracePeekData(pid int, addr uintptr, out []byte) (count int, err error) {
return PtraceIO(PIOD_READ_D, pid, addr, out, SizeofLong)
}
func PtracePeekText(pid int, addr uintptr, out []byte) (count int, err error) {
return PtraceIO(PIOD_READ_I, pid, addr, out, SizeofLong)
}
func PtracePokeData(pid int, addr uintptr, data []byte) (count int, err error) {
return PtraceIO(PIOD_WRITE_D, pid, addr, data, SizeofLong)
}
func PtracePokeText(pid int, addr uintptr, data []byte) (count int, err error) {
return PtraceIO(PIOD_WRITE_I, pid, addr, data, SizeofLong)
}
func PtraceSetRegs(pid int, regs *Reg) (err error) {
return ptrace(PTRACE_SETREGS, pid, uintptr(unsafe.Pointer(regs)), 0)
}
func PtraceSingleStep(pid int) (err error) {
return ptrace(PTRACE_SINGLESTEP, pid, 1, 0)
}
/*
* Exposed directly
*/
@ -555,7 +645,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error)
//sys getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys getdirentries_freebsd12(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys getdirentries_freebsd12(fd int, buf []byte, basep *uint64) (n int, err error)
//sys Getdtablesize() (size int)
//sysnb Getegid() (egid int)
//sysnb Geteuid() (uid int)

View File

@ -13,7 +13,6 @@ package unix
import (
"encoding/binary"
"net"
"runtime"
"syscall"
"unsafe"
@ -109,6 +108,12 @@ func IoctlGetInt(fd int, req uint) (int, error) {
return value, err
}
func IoctlGetUint32(fd int, req uint) (uint32, error) {
var value uint32
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
return value, err
}
func IoctlGetWinsize(fd int, req uint) (*Winsize, error) {
var value Winsize
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
@ -759,7 +764,7 @@ const px_proto_oe = 0
type SockaddrPPPoE struct {
SID uint16
Remote net.HardwareAddr
Remote []byte
Dev string
raw RawSockaddrPPPoX
}
@ -910,7 +915,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
}
sa := &SockaddrPPPoE{
SID: binary.BigEndian.Uint16(pp[6:8]),
Remote: net.HardwareAddr(pp[8:14]),
Remote: pp[8:14],
}
for i := 14; i < 14+IFNAMSIZ; i++ {
if pp[i] == 0 {
@ -1408,8 +1413,20 @@ func Reboot(cmd int) (err error) {
return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, "")
}
func ReadDirent(fd int, buf []byte) (n int, err error) {
return Getdents(fd, buf)
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}
func direntNamlen(buf []byte) (uint64, bool) {
reclen, ok := direntReclen(buf)
if !ok {
return 0, false
}
return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
}
//sys mount(source string, target string, fstype string, flags uintptr, data *byte) (err error)
@ -1444,6 +1461,8 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
//sys Acct(path string) (err error)
//sys AddKey(keyType string, description string, payload []byte, ringid int) (id int, err error)
//sys Adjtimex(buf *Timex) (state int, err error)
//sys Capget(hdr *CapUserHeader, data *CapUserData) (err error)
//sys Capset(hdr *CapUserHeader, data *CapUserData) (err error)
//sys Chdir(path string) (err error)
//sys Chroot(path string) (err error)
//sys ClockGetres(clockid int32, res *Timespec) (err error)
@ -1531,9 +1550,13 @@ func Setgid(uid int) (err error) {
return EOPNOTSUPP
}
func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
return signalfd(fd, sigmask, _C__NSIG/8, flags)
}
//sys Setpriority(which int, who int, prio int) (err error)
//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
//sys Signalfd(fd int, mask *Sigset_t, flags int) = SYS_SIGNALFD4
//sys signalfd(fd int, sigmask *Sigset_t, maskSize uintptr, flags int) (newfd int, err error) = SYS_SIGNALFD4
//sys Statx(dirfd int, path string, flags int, mask int, stat *Statx_t) (err error)
//sys Sync()
//sys Syncfs(fd int) (err error)
@ -1675,6 +1698,69 @@ type fileHandle struct {
Type int32
}
// FileHandle represents the C struct file_handle used by
// name_to_handle_at (see NameToHandleAt) and open_by_handle_at (see
// OpenByHandleAt).
type FileHandle struct {
*fileHandle
}
// NewFileHandle constructs a FileHandle.
func NewFileHandle(handleType int32, handle []byte) FileHandle {
const hdrSize = unsafe.Sizeof(fileHandle{})
buf := make([]byte, hdrSize+uintptr(len(handle)))
copy(buf[hdrSize:], handle)
fh := (*fileHandle)(unsafe.Pointer(&buf[0]))
fh.Type = handleType
fh.Bytes = uint32(len(handle))
return FileHandle{fh}
}
func (fh *FileHandle) Size() int { return int(fh.fileHandle.Bytes) }
func (fh *FileHandle) Type() int32 { return fh.fileHandle.Type }
func (fh *FileHandle) Bytes() []byte {
n := fh.Size()
if n == 0 {
return nil
}
return (*[1 << 30]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&fh.fileHandle.Type)) + 4))[:n:n]
}
// NameToHandleAt wraps the name_to_handle_at system call; it obtains
// a handle for a path name.
func NameToHandleAt(dirfd int, path string, flags int) (handle FileHandle, mountID int, err error) {
var mid _C_int
// Try first with a small buffer, assuming the handle will
// only be 32 bytes.
size := uint32(32 + unsafe.Sizeof(fileHandle{}))
didResize := false
for {
buf := make([]byte, size)
fh := (*fileHandle)(unsafe.Pointer(&buf[0]))
fh.Bytes = size - uint32(unsafe.Sizeof(fileHandle{}))
err = nameToHandleAt(dirfd, path, fh, &mid, flags)
if err == EOVERFLOW {
if didResize {
// We shouldn't need to resize more than once
return
}
didResize = true
size = fh.Bytes + uint32(unsafe.Sizeof(fileHandle{}))
continue
}
if err != nil {
return
}
return FileHandle{fh}, int(mid), nil
}
}
// OpenByHandleAt wraps the open_by_handle_at system call; it opens a
// file via a handle as previously returned by NameToHandleAt.
func OpenByHandleAt(mountFD int, handle FileHandle, flags int) (fd int, err error) {
return openByHandleAt(mountFD, handle.fileHandle, flags)
}
/*
* Unimplemented
*/
@ -1682,8 +1768,6 @@ type fileHandle struct {
// Alarm
// ArchPrctl
// Brk
// Capget
// Capset
// ClockNanosleep
// ClockSettime
// Clone

View File

@ -272,3 +272,16 @@ func SyncFileRange(fd int, off int64, n int64, flags int) error {
// order of their arguments.
return armSyncFileRange(fd, flags, off, n)
}
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error)
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error {
cmdlineLen := len(cmdline)
if cmdlineLen > 0 {
// Account for the additional NULL byte added by
// BytePtrFromString in kexecFileLoad. The kexec_file_load
// syscall expects a NULL-terminated string.
cmdlineLen++
}
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
}

View File

@ -94,6 +94,18 @@ func nametomib(name string) (mib []_C_int, err error) {
return mib, nil
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}
func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}
func SysctlClockinfo(name string) (*Clockinfo, error) {
mib, err := sysctlmib(name)
if err != nil {
@ -120,9 +132,30 @@ func Pipe(p []int) (err error) {
return
}
//sys getdents(fd int, buf []byte) (n int, err error)
//sys Getdents(fd int, buf []byte) (n int, err error)
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
return getdents(fd, buf)
n, err = Getdents(fd, buf)
if err != nil || basep == nil {
return
}
var off int64
off, err = Seek(fd, 0, 1 /* SEEK_CUR */)
if err != nil {
*basep = ^uintptr(0)
return
}
*basep = uintptr(off)
if unsafe.Sizeof(*basep) == 8 {
return
}
if off>>32 != 0 {
// We can't stuff the offset back into a uintptr, so any
// future calls would be suspect. Generate an error.
// EIO is allowed by getdirentries.
err = EIO
}
return
}
const ImplementsGetwd = true

View File

@ -43,6 +43,18 @@ func nametomib(name string) (mib []_C_int, err error) {
return nil, EINVAL
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Fileno), unsafe.Sizeof(Dirent{}.Fileno))
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}
func direntNamlen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
}
func SysctlClockinfo(name string) (*Clockinfo, error) {
mib, err := sysctlmib(name)
if err != nil {
@ -89,9 +101,30 @@ func Pipe(p []int) (err error) {
return
}
//sys getdents(fd int, buf []byte) (n int, err error)
//sys Getdents(fd int, buf []byte) (n int, err error)
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
return getdents(fd, buf)
n, err = Getdents(fd, buf)
if err != nil || basep == nil {
return
}
var off int64
off, err = Seek(fd, 0, 1 /* SEEK_CUR */)
if err != nil {
*basep = ^uintptr(0)
return
}
*basep = uintptr(off)
if unsafe.Sizeof(*basep) == 8 {
return
}
if off>>32 != 0 {
// We can't stuff the offset back into a uintptr, so any
// future calls would be suspect. Generate an error.
// EIO was allowed by getdirentries.
err = EIO
}
return
}
const ImplementsGetwd = true

37
vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm64,openbsd
package unix
func setTimespec(sec, nsec int64) Timespec {
return Timespec{Sec: sec, Nsec: nsec}
}
func setTimeval(sec, usec int64) Timeval {
return Timeval{Sec: sec, Usec: usec}
}
func SetKevent(k *Kevent_t, fd, mode, flags int) {
k.Ident = uint64(fd)
k.Filter = int16(mode)
k.Flags = uint16(flags)
}
func (iov *Iovec) SetLen(length int) {
iov.Len = uint64(length)
}
func (msghdr *Msghdr) SetControllen(length int) {
msghdr.Controllen = uint32(length)
}
func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length)
}
// SYS___SYSCTL is used by syscall_bsd.go for all BSDs, but in modern versions
// of openbsd/amd64 the syscall is called sysctl instead of __sysctl.
const SYS___SYSCTL = SYS_SYSCTL

View File

@ -35,6 +35,22 @@ type SockaddrDatalink struct {
raw RawSockaddrDatalink
}
func direntIno(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
}
func direntReclen(buf []byte) (uint64, bool) {
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
}
func direntNamlen(buf []byte) (uint64, bool) {
reclen, ok := direntReclen(buf)
if !ok {
return 0, false
}
return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
}
//sysnb pipe(p *[2]_C_int) (n int, err error)
func Pipe(p []int) (err error) {
@ -189,6 +205,7 @@ func Setgroups(gids []int) (err error) {
return setgroups(len(a), &a[0])
}
// ReadDirent reads directory entries from fd and writes them into buf.
func ReadDirent(fd int, buf []byte) (n int, err error) {
// Final argument is (basep *uintptr) and the syscall doesn't take nil.
// TODO(rsc): Can we use a single global basep for all calls?

View File

@ -87,8 +87,6 @@ type Mode_t C.mode_t
type Timespec C.struct_timespec
type StTimespec C.struct_st_timespec
type Timeval C.struct_timeval
type Timeval32 C.struct_timeval32
@ -133,6 +131,8 @@ type RawSockaddrInet6 C.struct_sockaddr_in6
type RawSockaddrUnix C.struct_sockaddr_un
type RawSockaddrDatalink C.struct_sockaddr_dl
type RawSockaddr C.struct_sockaddr
type RawSockaddrAny C.struct_sockaddr_any
@ -156,17 +156,18 @@ type Linger C.struct_linger
type Msghdr C.struct_msghdr
const (
SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
SizeofSockaddrAny = C.sizeof_struct_sockaddr_any
SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un
SizeofLinger = C.sizeof_struct_linger
SizeofIPMreq = C.sizeof_struct_ip_mreq
SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
SizeofMsghdr = C.sizeof_struct_msghdr
SizeofCmsghdr = C.sizeof_struct_cmsghdr
SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
SizeofSockaddrInet4 = C.sizeof_struct_sockaddr_in
SizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
SizeofSockaddrAny = C.sizeof_struct_sockaddr_any
SizeofSockaddrUnix = C.sizeof_struct_sockaddr_un
SizeofSockaddrDatalink = C.sizeof_struct_sockaddr_dl
SizeofLinger = C.sizeof_struct_linger
SizeofIPMreq = C.sizeof_struct_ip_mreq
SizeofIPv6Mreq = C.sizeof_struct_ipv6_mreq
SizeofIPv6MTUInfo = C.sizeof_struct_ip6_mtuinfo
SizeofMsghdr = C.sizeof_struct_msghdr
SizeofCmsghdr = C.sizeof_struct_cmsghdr
SizeofICMPv6Filter = C.sizeof_struct_icmp6_filter
)
// Routing and interface messages

Some files were not shown because too many files have changed in this diff Show More