diff options
author | Wim <wim@42.be> | 2017-08-16 23:37:37 +0200 |
---|---|---|
committer | Wim <wim@42.be> | 2017-08-16 23:37:37 +0200 |
commit | b963f83c6a09da1eaf02eb9f269c6f5881a0c711 (patch) | |
tree | 5ec37162046349d23c627e9ee26fb70c1a6c4f08 /vendor/github.com/mattermost/platform/model | |
parent | f6297ebbb09611165a50a6cd15abf6499268cf86 (diff) | |
download | matterbridge-msglm-b963f83c6a09da1eaf02eb9f269c6f5881a0c711.tar.gz matterbridge-msglm-b963f83c6a09da1eaf02eb9f269c6f5881a0c711.tar.bz2 matterbridge-msglm-b963f83c6a09da1eaf02eb9f269c6f5881a0c711.zip |
Update mattermost vendor (3.7 => 4.1)
Diffstat (limited to 'vendor/github.com/mattermost/platform/model')
73 files changed, 4284 insertions, 789 deletions
diff --git a/vendor/github.com/mattermost/platform/model/LICENSE.txt b/vendor/github.com/mattermost/platform/model/LICENSE.txt index e5875711..ead98cf0 100644 --- a/vendor/github.com/mattermost/platform/model/LICENSE.txt +++ b/vendor/github.com/mattermost/platform/model/LICENSE.txt @@ -1,32 +1,32 @@ Mattermost Licensing -SOFTWARE LICENSING +SOFTWARE LICENSING -You are licensed to use compiled versions of the Mattermost platform produced by Mattermost, Inc. under an MIT LICENSE +You are licensed to use compiled versions of the Mattermost platform produced by Mattermost, Inc. under an MIT LICENSE - See MIT-COMPILED-LICENSE.md included in compiled versions for details You may be licensed to use source code to create compiled versions not produced by Mattermost, Inc. in one of two ways: -1. Under the Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or -2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com +1. Under the Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or +2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com -You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/, +You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/, webapp/client, webapp/fonts, webapp/i18n, webapp/images and all subdirectories thereof) under the Apache License v2.0. -We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not +We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not link to the Mattermost Platform directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, and -(b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation of +(b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation of a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license. MATTERMOST TRADEMARK GUIDELINES -Your use of the mark Mattermost is subject to Mattermost, Inc's prior written approval and our organization’s Trademark -Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions -you have about using these trademarks, please email trademark@mattermost.com +Your use of the mark Mattermost is subject to Mattermost, Inc's prior written approval and our organization’s Trademark +Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions +you have about using these trademarks, please email trademark@mattermost.com ------------------------------------------------------------------------------------------------------------------------------ - + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/vendor/github.com/mattermost/platform/model/access.go b/vendor/github.com/mattermost/platform/model/access.go index 85417fce..9e16ed58 100644 --- a/vendor/github.com/mattermost/platform/model/access.go +++ b/vendor/github.com/mattermost/platform/model/access.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -21,6 +21,7 @@ type AccessData struct { RefreshToken string `json:"refresh_token"` RedirectUri string `json:"redirect_uri"` ExpiresAt int64 `json:"expires_at"` + Scope string `json:"scope"` } type AccessResponse struct { @@ -51,7 +52,7 @@ func (ad *AccessData) IsValid() *AppError { return NewLocAppError("AccessData.IsValid", "model.access.is_valid.refresh_token.app_error", nil, "") } - if len(ad.RedirectUri) > 256 { + if len(ad.RedirectUri) == 0 || len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) { return NewLocAppError("AccessData.IsValid", "model.access.is_valid.redirect_uri.app_error", nil, "") } diff --git a/vendor/github.com/mattermost/platform/model/analytics_row.go b/vendor/github.com/mattermost/platform/model/analytics_row.go index ed1d69dd..5493163d 100644 --- a/vendor/github.com/mattermost/platform/model/analytics_row.go +++ b/vendor/github.com/mattermost/platform/model/analytics_row.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/audit.go b/vendor/github.com/mattermost/platform/model/audit.go index 8fa1d558..7699cf93 100644 --- a/vendor/github.com/mattermost/platform/model/audit.go +++ b/vendor/github.com/mattermost/platform/model/audit.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/audits.go b/vendor/github.com/mattermost/platform/model/audits.go index 36c80629..e57e2a3a 100644 --- a/vendor/github.com/mattermost/platform/model/audits.go +++ b/vendor/github.com/mattermost/platform/model/audits.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/authorization.go b/vendor/github.com/mattermost/platform/model/authorization.go index a7a6f374..d413e294 100644 --- a/vendor/github.com/mattermost/platform/model/authorization.go +++ b/vendor/github.com/mattermost/platform/model/authorization.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -39,6 +39,7 @@ var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission var PERMISSION_EDIT_OTHER_USERS *Permission var PERMISSION_READ_CHANNEL *Permission +var PERMISSION_READ_PUBLIC_CHANNEL *Permission var PERMISSION_PERMANENT_DELETE_USER *Permission var PERMISSION_UPLOAD_FILE *Permission var PERMISSION_GET_PUBLIC_LINK *Permission @@ -47,6 +48,7 @@ var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission var PERMISSION_MANAGE_OAUTH *Permission var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission var PERMISSION_CREATE_POST *Permission +var PERMISSION_CREATE_POST_PUBLIC *Permission var PERMISSION_EDIT_POST *Permission var PERMISSION_EDIT_OTHERS_POSTS *Permission var PERMISSION_DELETE_POST *Permission @@ -56,6 +58,11 @@ var PERMISSION_CREATE_TEAM *Permission var PERMISSION_MANAGE_TEAM *Permission var PERMISSION_IMPORT_TEAM *Permission var PERMISSION_VIEW_TEAM *Permission +var PERMISSION_LIST_USERS_WITHOUT_TEAM *Permission +var PERMISSION_MANAGE_JOBS *Permission +var PERMISSION_CREATE_USER_ACCESS_TOKEN *Permission +var PERMISSION_READ_USER_ACCESS_TOKEN *Permission +var PERMISSION_REVOKE_USER_ACCESS_TOKEN *Permission // General permission that encompases all system admin functions // in the future this could be broken up to allow access to some @@ -64,9 +71,14 @@ var PERMISSION_MANAGE_SYSTEM *Permission var ROLE_SYSTEM_USER *Role var ROLE_SYSTEM_ADMIN *Role +var ROLE_SYSTEM_POST_ALL *Role +var ROLE_SYSTEM_POST_ALL_PUBLIC *Role +var ROLE_SYSTEM_USER_ACCESS_TOKEN *Role var ROLE_TEAM_USER *Role var ROLE_TEAM_ADMIN *Role +var ROLE_TEAM_POST_ALL *Role +var ROLE_TEAM_POST_ALL_PUBLIC *Role var ROLE_CHANNEL_USER *Role var ROLE_CHANNEL_ADMIN *Role @@ -195,6 +207,11 @@ func InitalizePermissions() { "authentication.permissions.read_channel.name", "authentication.permissions.read_channel.description", } + PERMISSION_READ_PUBLIC_CHANNEL = &Permission{ + "read_public_channel", + "authentication.permissions.read_public_channel.name", + "authentication.permissions.read_public_channel.description", + } PERMISSION_PERMANENT_DELETE_USER = &Permission{ "permanent_delete_user", "authentication.permissions.permanent_delete_user.name", @@ -235,6 +252,11 @@ func InitalizePermissions() { "authentication.permissions.create_post.name", "authentication.permissions.create_post.description", } + PERMISSION_CREATE_POST_PUBLIC = &Permission{ + "create_post_public", + "authentication.permissions.create_post_public.name", + "authentication.permissions.create_post_public.description", + } PERMISSION_EDIT_POST = &Permission{ "edit_post", "authentication.permissions.edit_post.name", @@ -280,6 +302,31 @@ func InitalizePermissions() { "authentication.permissions.view_team.name", "authentication.permissions.view_team.description", } + PERMISSION_LIST_USERS_WITHOUT_TEAM = &Permission{ + "list_users_without_team", + "authentication.permissions.list_users_without_team.name", + "authentication.permissions.list_users_without_team.description", + } + PERMISSION_CREATE_USER_ACCESS_TOKEN = &Permission{ + "create_user_access_token", + "authentication.permissions.create_user_access_token.name", + "authentication.permissions.create_user_access_token.description", + } + PERMISSION_READ_USER_ACCESS_TOKEN = &Permission{ + "read_user_access_token", + "authentication.permissions.read_user_access_token.name", + "authentication.permissions.read_user_access_token.description", + } + PERMISSION_REVOKE_USER_ACCESS_TOKEN = &Permission{ + "revoke_user_access_token", + "authentication.permissions.revoke_user_access_token.name", + "authentication.permissions.revoke_user_access_token.description", + } + PERMISSION_MANAGE_JOBS = &Permission{ + "manage_jobs", + "authentication.permisssions.manage_jobs.name", + "authentication.permisssions.manage_jobs.description", + } } func InitalizeRoles() { @@ -293,7 +340,6 @@ func InitalizeRoles() { []string{ PERMISSION_READ_CHANNEL.Id, PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, - PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, PERMISSION_UPLOAD_FILE.Id, PERMISSION_GET_PUBLIC_LINK.Id, PERMISSION_CREATE_POST.Id, @@ -326,17 +372,38 @@ func InitalizeRoles() { []string{ PERMISSION_LIST_TEAM_CHANNELS.Id, PERMISSION_JOIN_PUBLIC_CHANNELS.Id, + PERMISSION_READ_PUBLIC_CHANNEL.Id, PERMISSION_VIEW_TEAM.Id, }, } BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER + + ROLE_TEAM_POST_ALL = &Role{ + "team_post_all", + "authentication.roles.team_post_all.name", + "authentication.roles.team_post_all.description", + []string{ + PERMISSION_CREATE_POST.Id, + }, + } + BuiltInRoles[ROLE_TEAM_POST_ALL.Id] = ROLE_TEAM_POST_ALL + + ROLE_TEAM_POST_ALL_PUBLIC = &Role{ + "team_post_all_public", + "authentication.roles.team_post_all_public.name", + "authentication.roles.team_post_all_public.description", + []string{ + PERMISSION_CREATE_POST_PUBLIC.Id, + }, + } + BuiltInRoles[ROLE_TEAM_POST_ALL_PUBLIC.Id] = ROLE_TEAM_POST_ALL_PUBLIC + ROLE_TEAM_ADMIN = &Role{ "team_admin", "authentication.roles.team_admin.name", "authentication.roles.team_admin.description", []string{ PERMISSION_EDIT_OTHERS_POSTS.Id, - PERMISSION_ADD_USER_TO_TEAM.Id, PERMISSION_REMOVE_USER_FROM_TEAM.Id, PERMISSION_MANAGE_TEAM.Id, PERMISSION_IMPORT_TEAM.Id, @@ -358,10 +425,42 @@ func InitalizeRoles() { PERMISSION_CREATE_DIRECT_CHANNEL.Id, PERMISSION_CREATE_GROUP_CHANNEL.Id, PERMISSION_PERMANENT_DELETE_USER.Id, - PERMISSION_MANAGE_OAUTH.Id, }, } BuiltInRoles[ROLE_SYSTEM_USER.Id] = ROLE_SYSTEM_USER + + ROLE_SYSTEM_POST_ALL = &Role{ + "system_post_all", + "authentication.roles.system_post_all.name", + "authentication.roles.system_post_all.description", + []string{ + PERMISSION_CREATE_POST.Id, + }, + } + BuiltInRoles[ROLE_SYSTEM_POST_ALL.Id] = ROLE_SYSTEM_POST_ALL + + ROLE_SYSTEM_POST_ALL_PUBLIC = &Role{ + "system_post_all_public", + "authentication.roles.system_post_all_public.name", + "authentication.roles.system_post_all_public.description", + []string{ + PERMISSION_CREATE_POST_PUBLIC.Id, + }, + } + BuiltInRoles[ROLE_SYSTEM_POST_ALL_PUBLIC.Id] = ROLE_SYSTEM_POST_ALL_PUBLIC + + ROLE_SYSTEM_USER_ACCESS_TOKEN = &Role{ + "system_user_access_token", + "authentication.roles.system_user_access_token.name", + "authentication.roles.system_user_access_token.description", + []string{ + PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + PERMISSION_READ_USER_ACCESS_TOKEN.Id, + PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, + }, + } + BuiltInRoles[ROLE_SYSTEM_USER_ACCESS_TOKEN.Id] = ROLE_SYSTEM_USER_ACCESS_TOKEN + ROLE_SYSTEM_ADMIN = &Role{ "system_admin", "authentication.roles.global_admin.name", @@ -378,6 +477,8 @@ func InitalizeRoles() { PERMISSION_MANAGE_SYSTEM.Id, PERMISSION_MANAGE_ROLES.Id, PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id, + PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, + PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id, PERMISSION_DELETE_PUBLIC_CHANNEL.Id, PERMISSION_CREATE_PUBLIC_CHANNEL.Id, PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, @@ -391,6 +492,13 @@ func InitalizeRoles() { PERMISSION_DELETE_POST.Id, PERMISSION_DELETE_OTHERS_POSTS.Id, PERMISSION_CREATE_TEAM.Id, + PERMISSION_ADD_USER_TO_TEAM.Id, + PERMISSION_LIST_USERS_WITHOUT_TEAM.Id, + PERMISSION_MANAGE_JOBS.Id, + PERMISSION_CREATE_POST_PUBLIC.Id, + PERMISSION_CREATE_USER_ACCESS_TOKEN.Id, + PERMISSION_READ_USER_ACCESS_TOKEN.Id, + PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, }, ROLE_TEAM_USER.Permissions..., ), diff --git a/vendor/github.com/mattermost/platform/model/authorize.go b/vendor/github.com/mattermost/platform/model/authorize.go index 2b4017e9..460b7082 100644 --- a/vendor/github.com/mattermost/platform/model/authorize.go +++ b/vendor/github.com/mattermost/platform/model/authorize.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -6,6 +6,7 @@ package model import ( "encoding/json" "io" + "net/http" ) const ( @@ -25,6 +26,14 @@ type AuthData struct { Scope string `json:"scope"` } +type AuthorizeRequest struct { + ResponseType string `json:"response_type"` + ClientId string `json:"client_id"` + RedirectUri string `json:"redirect_uri"` + Scope string `json:"scope"` + State string `json:"state"` +} + // IsValid validates the AuthData and returns an error if it isn't configured // correctly. func (ad *AuthData) IsValid() *AppError { @@ -49,7 +58,7 @@ func (ad *AuthData) IsValid() *AppError { return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.create_at.app_error", nil, "client_id="+ad.ClientId) } - if len(ad.RedirectUri) > 256 { + if len(ad.RedirectUri) == 0 || len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) { return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ad.ClientId) } @@ -64,6 +73,33 @@ func (ad *AuthData) IsValid() *AppError { return nil } +// IsValid validates the AuthorizeRequest and returns an error if it isn't configured +// correctly. +func (ar *AuthorizeRequest) IsValid() *AppError { + + if len(ar.ClientId) != 26 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.client_id.app_error", nil, "", http.StatusBadRequest) + } + + if len(ar.ResponseType) == 0 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.response_type.app_error", nil, "", http.StatusBadRequest) + } + + if len(ar.RedirectUri) == 0 || len(ar.RedirectUri) > 256 || !IsValidHttpUrl(ar.RedirectUri) { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) + } + + if len(ar.State) > 128 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) + } + + if len(ar.Scope) > 128 { + return NewAppError("AuthData.IsValid", "model.authorize.is_valid.scope.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest) + } + + return nil +} + func (ad *AuthData) PreSave() { if ad.ExpiresIn == 0 { ad.ExpiresIn = AUTHCODE_EXPIRE_TIME @@ -98,6 +134,26 @@ func AuthDataFromJson(data io.Reader) *AuthData { } } +func (ar *AuthorizeRequest) ToJson() string { + b, err := json.Marshal(ar) + if err != nil { + return "" + } else { + return string(b) + } +} + +func AuthorizeRequestFromJson(data io.Reader) *AuthorizeRequest { + decoder := json.NewDecoder(data) + var ar AuthorizeRequest + err := decoder.Decode(&ar) + if err == nil { + return &ar + } else { + return nil + } +} + func (ad *AuthData) IsExpired() bool { if GetMillis() > ad.CreateAt+int64(ad.ExpiresIn*1000) { diff --git a/vendor/github.com/mattermost/platform/model/channel.go b/vendor/github.com/mattermost/platform/model/channel.go index d24fdb2b..50d48755 100644 --- a/vendor/github.com/mattermost/platform/model/channel.go +++ b/vendor/github.com/mattermost/platform/model/channel.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -46,6 +46,13 @@ type Channel struct { CreatorId string `json:"creator_id"` } +type ChannelPatch struct { + DisplayName *string `json:"display_name"` + Name *string `json:"name"` + Header *string `json:"header"` + Purpose *string `json:"purpose"` +} + func (o *Channel) ToJson() string { b, err := json.Marshal(o) if err != nil { @@ -55,6 +62,15 @@ func (o *Channel) ToJson() string { } } +func (o *ChannelPatch) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + func ChannelFromJson(data io.Reader) *Channel { decoder := json.NewDecoder(data) var o Channel @@ -66,6 +82,17 @@ func ChannelFromJson(data io.Reader) *Channel { } } +func ChannelPatchFromJson(data io.Reader) *ChannelPatch { + decoder := json.NewDecoder(data) + var o ChannelPatch + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} + func (o *Channel) Etag() string { return Etag(o.Id, o.UpdateAt) } @@ -137,6 +164,24 @@ func (o *Channel) IsGroupOrDirect() bool { return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP } +func (o *Channel) Patch(patch *ChannelPatch) { + if patch.DisplayName != nil { + o.DisplayName = *patch.DisplayName + } + + if patch.Name != nil { + o.Name = *patch.Name + } + + if patch.Header != nil { + o.Header = *patch.Header + } + + if patch.Purpose != nil { + o.Purpose = *patch.Purpose + } +} + func GetDMNameFromIds(userId1, userId2 string) string { if userId1 > userId2 { return userId2 + "__" + userId1 diff --git a/vendor/github.com/mattermost/platform/model/channel_count.go b/vendor/github.com/mattermost/platform/model/channel_count.go index 6cc1b2f2..aecb0c71 100644 --- a/vendor/github.com/mattermost/platform/model/channel_count.go +++ b/vendor/github.com/mattermost/platform/model/channel_count.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/channel_data.go b/vendor/github.com/mattermost/platform/model/channel_data.go index 731d50e7..41b7eaa6 100644 --- a/vendor/github.com/mattermost/platform/model/channel_data.go +++ b/vendor/github.com/mattermost/platform/model/channel_data.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/channel_list.go b/vendor/github.com/mattermost/platform/model/channel_list.go index 7a46de45..d5a4ccb7 100644 --- a/vendor/github.com/mattermost/platform/model/channel_list.go +++ b/vendor/github.com/mattermost/platform/model/channel_list.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -50,3 +50,14 @@ func ChannelListFromJson(data io.Reader) *ChannelList { return nil } } + +func ChannelSliceFromJson(data io.Reader) []*Channel { + decoder := json.NewDecoder(data) + var o []*Channel + err := decoder.Decode(&o) + if err == nil { + return o + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/channel_member.go b/vendor/github.com/mattermost/platform/model/channel_member.go index 5de58bc4..f6d58519 100644 --- a/vendor/github.com/mattermost/platform/model/channel_member.go +++ b/vendor/github.com/mattermost/platform/model/channel_member.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -19,11 +19,11 @@ const ( ) type ChannelUnread struct { - TeamId string - TotalMsgCount int64 - MsgCount int64 - MentionCount int64 - NotifyProps StringMap + TeamId string `json:"team_id"` + ChannelId string `json:"channel_id"` + MsgCount int64 `json:"msg_count"` + MentionCount int64 `json:"mention_count"` + NotifyProps StringMap `json:"-"` } type ChannelMember struct { @@ -47,6 +47,15 @@ func (o *ChannelMembers) ToJson() string { } } +func (o *ChannelUnread) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + func ChannelMembersFromJson(data io.Reader) *ChannelMembers { decoder := json.NewDecoder(data) var o ChannelMembers @@ -58,6 +67,17 @@ func ChannelMembersFromJson(data io.Reader) *ChannelMembers { } } +func ChannelUnreadFromJson(data io.Reader) *ChannelUnread { + decoder := json.NewDecoder(data) + var o ChannelUnread + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} + func (o *ChannelMember) ToJson() string { b, err := json.Marshal(o) if err != nil { diff --git a/vendor/github.com/mattermost/platform/model/channel_search.go b/vendor/github.com/mattermost/platform/model/channel_search.go index 2c041503..d915c164 100644 --- a/vendor/github.com/mattermost/platform/model/channel_search.go +++ b/vendor/github.com/mattermost/platform/model/channel_search.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/channel_stats.go b/vendor/github.com/mattermost/platform/model/channel_stats.go index 079769eb..758aa2b1 100644 --- a/vendor/github.com/mattermost/platform/model/channel_stats.go +++ b/vendor/github.com/mattermost/platform/model/channel_stats.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/channel_view.go b/vendor/github.com/mattermost/platform/model/channel_view.go index 8be7af17..8a7ead76 100644 --- a/vendor/github.com/mattermost/platform/model/channel_view.go +++ b/vendor/github.com/mattermost/platform/model/channel_view.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/client.go b/vendor/github.com/mattermost/platform/model/client.go index 24ee2c2b..564aa2e4 100644 --- a/vendor/github.com/mattermost/platform/model/client.go +++ b/vendor/github.com/mattermost/platform/model/client.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -1191,17 +1191,6 @@ func (c *Client) GetChannel(id, etag string) (*Result, *AppError) { } } -// SCHEDULED FOR DEPRECATION IN 3.7 - use GetMoreChannelsPage instead -func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) { - if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/more", "", etag); err != nil { - return nil, err - } else { - defer closeBody(r) - return &Result{r.Header.Get(HEADER_REQUEST_ID), - r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil - } -} - // GetMoreChannelsPage will return a page of open channels the user is not in based on // the provided offset and limit. Must be authenticated. func (c *Client) GetMoreChannelsPage(offset int, limit int) (*Result, *AppError) { @@ -1333,22 +1322,6 @@ func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) { } } -// UpdateLastViewedAt will mark a channel as read. -// The channelId indicates the channel to mark as read. If active is true, push notifications -// will be cleared if there are unread messages. The default for active is true. -// SCHEDULED FOR DEPRECATION IN 3.8 - use ViewChannel instead -func (c *Client) UpdateLastViewedAt(channelId string, active bool) (*Result, *AppError) { - data := make(map[string]interface{}) - data["active"] = active - if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_last_viewed_at", StringInterfaceToJson(data)); err != nil { - return nil, err - } else { - defer closeBody(r) - return &Result{r.Header.Get(HEADER_REQUEST_ID), - r.Header.Get(HEADER_ETAG_SERVER), nil}, nil - } -} - // ViewChannel performs all the actions related to viewing a channel. This includes marking // the channel and the previous one as read, and marking the channel as being actively viewed. // ChannelId is required but may be blank to indicate no channel is being viewed. @@ -1533,6 +1506,16 @@ func (c *Client) GetFlaggedPosts(offset int, limit int) (*Result, *AppError) { } } +func (c *Client) GetPinnedPosts(channelId string) (*Result, *AppError) { + if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", "", ""); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil + } +} + func (c *Client) UploadProfileFile(data []byte, contentType string) (*Result, *AppError) { return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType) } @@ -1782,22 +1765,6 @@ func (c *Client) GetStatusesByIds(userIds []string) (*Result, *AppError) { } } -// SetActiveChannel sets the the channel id the user is currently viewing. -// The channelId key is required but the value can be blank. Returns standard -// response. -// SCHEDULED FOR DEPRECATION IN 3.8 - use ViewChannel instead -func (c *Client) SetActiveChannel(channelId string) (*Result, *AppError) { - data := map[string]string{} - data["channel_id"] = channelId - if r, err := c.DoApiPost("/users/status/set_active_channel", MapToJson(data)); err != nil { - return nil, err - } else { - defer closeBody(r) - return &Result{r.Header.Get(HEADER_REQUEST_ID), - r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil - } -} - func (c *Client) GetMyTeam(etag string) (*Result, *AppError) { if r, err := c.DoApiGet(c.GetTeamRoute()+"/me", "", etag); err != nil { return nil, err @@ -1874,15 +1841,14 @@ func (c *Client) GetTeamStats(teamId string) (*Result, *AppError) { } } -// GetTeamStats will return a team stats object containing the number of users on the team -// based on the team id provided. Must be authenticated. +// GetTeamByName will return a team object based on the team name provided. Must be authenticated. func (c *Client) GetTeamByName(teamName string) (*Result, *AppError) { if r, err := c.DoApiGet(fmt.Sprintf("/teams/name/%v", teamName), "", ""); err != nil { return nil, err } else { defer closeBody(r) return &Result{r.Header.Get(HEADER_REQUEST_ID), - r.Header.Get(HEADER_ETAG_SERVER), TeamStatsFromJson(r.Body)}, nil + r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil } } @@ -2389,3 +2355,23 @@ func (c *Client) UpdateChannelRoles(channelId string, userId string, roles strin } } } + +func (c *Client) PinPost(channelId string, postId string) (*Result, *AppError) { + if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/posts/"+postId+"/pin", ""); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil + } +} + +func (c *Client) UnpinPost(channelId string, postId string) (*Result, *AppError) { + if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/posts/"+postId+"/unpin", ""); err != nil { + return nil, err + } else { + defer closeBody(r) + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/client4.go b/vendor/github.com/mattermost/platform/model/client4.go index d3bb6534..0f757853 100644 --- a/vendor/github.com/mattermost/platform/model/client4.go +++ b/vendor/github.com/mattermost/platform/model/client4.go @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -21,6 +21,7 @@ type Response struct { RequestId string Etag string ServerVersion string + Header http.Header } type Client4 struct { @@ -35,12 +36,31 @@ func NewAPIv4Client(url string) *Client4 { return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", ""} } +func BuildErrorResponse(r *http.Response, err *AppError) *Response { + var statusCode int + var header http.Header + if r != nil { + statusCode = r.StatusCode + header = r.Header + } else { + statusCode = 0 + header = make(http.Header, 0) + } + + return &Response{ + StatusCode: statusCode, + Error: err, + Header: header, + } +} + func BuildResponse(r *http.Response) *Response { return &Response{ StatusCode: r.StatusCode, RequestId: r.Header.Get(HEADER_REQUEST_ID), Etag: r.Header.Get(HEADER_ETAG_SERVER), ServerVersion: r.Header.Get(HEADER_VERSION_ID), + Header: r.Header, } } @@ -62,6 +82,10 @@ func (c *Client4) GetUserRoute(userId string) string { return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId) } +func (c *Client4) GetUserAccessTokenRoute(tokenId string) string { + return fmt.Sprintf(c.GetUsersRoute()+"/tokens/%v", tokenId) +} + func (c *Client4) GetUserByUsernameRoute(userName string) string { return fmt.Sprintf(c.GetUsersRoute()+"/username/%v", userName) } @@ -78,6 +102,10 @@ func (c *Client4) GetTeamRoute(teamId string) string { return fmt.Sprintf(c.GetTeamsRoute()+"/%v", teamId) } +func (c *Client4) GetTeamAutoCompleteCommandsRoute(teamId string) string { + return fmt.Sprintf(c.GetTeamsRoute()+"/%v/commands/autocomplete", teamId) +} + func (c *Client4) GetTeamByNameRoute(teamName string) string { return fmt.Sprintf(c.GetTeamsRoute()+"/name/%v", teamName) } @@ -94,10 +122,18 @@ func (c *Client4) GetTeamStatsRoute(teamId string) string { return fmt.Sprintf(c.GetTeamRoute(teamId) + "/stats") } +func (c *Client4) GetTeamImportRoute(teamId string) string { + return fmt.Sprintf(c.GetTeamRoute(teamId) + "/import") +} + func (c *Client4) GetChannelsRoute() string { return fmt.Sprintf("/channels") } +func (c *Client4) GetChannelsForTeamRoute(teamId string) string { + return fmt.Sprintf(c.GetTeamRoute(teamId) + "/channels") +} + func (c *Client4) GetChannelRoute(channelId string) string { return fmt.Sprintf(c.GetChannelsRoute()+"/%v", channelId) } @@ -122,6 +158,14 @@ func (c *Client4) GetPostsRoute() string { return fmt.Sprintf("/posts") } +func (c *Client4) GetConfigRoute() string { + return fmt.Sprintf("/config") +} + +func (c *Client4) GetLicenseRoute() string { + return fmt.Sprintf("/license") +} + func (c *Client4) GetPostRoute(postId string) string { return fmt.Sprintf(c.GetPostsRoute()+"/%v", postId) } @@ -138,32 +182,132 @@ func (c *Client4) GetSystemRoute() string { return fmt.Sprintf("/system") } +func (c *Client4) GetTestEmailRoute() string { + return fmt.Sprintf("/email/test") +} + +func (c *Client4) GetDatabaseRoute() string { + return fmt.Sprintf("/database") +} + +func (c *Client4) GetCacheRoute() string { + return fmt.Sprintf("/caches") +} + +func (c *Client4) GetClusterRoute() string { + return fmt.Sprintf("/cluster") +} + func (c *Client4) GetIncomingWebhooksRoute() string { return fmt.Sprintf("/hooks/incoming") } +func (c *Client4) GetIncomingWebhookRoute(hookID string) string { + return fmt.Sprintf(c.GetIncomingWebhooksRoute()+"/%v", hookID) +} + +func (c *Client4) GetComplianceReportsRoute() string { + return fmt.Sprintf("/compliance/reports") +} + +func (c *Client4) GetComplianceReportRoute(reportId string) string { + return fmt.Sprintf("/compliance/reports/%v", reportId) +} + +func (c *Client4) GetOutgoingWebhooksRoute() string { + return fmt.Sprintf("/hooks/outgoing") +} + +func (c *Client4) GetOutgoingWebhookRoute(hookID string) string { + return fmt.Sprintf(c.GetOutgoingWebhooksRoute()+"/%v", hookID) +} + func (c *Client4) GetPreferencesRoute(userId string) string { return fmt.Sprintf(c.GetUserRoute(userId) + "/preferences") } +func (c *Client4) GetUserStatusRoute(userId string) string { + return fmt.Sprintf(c.GetUserRoute(userId) + "/status") +} + +func (c *Client4) GetUserStatusesRoute() string { + return fmt.Sprintf(c.GetUsersRoute() + "/status") +} + +func (c *Client4) GetSamlRoute() string { + return fmt.Sprintf("/saml") +} + +func (c *Client4) GetLdapRoute() string { + return fmt.Sprintf("/ldap") +} + +func (c *Client4) GetBrandRoute() string { + return fmt.Sprintf("/brand") +} + +func (c *Client4) GetElasticsearchRoute() string { + return fmt.Sprintf("/elasticsearch") +} + +func (c *Client4) GetCommandsRoute() string { + return fmt.Sprintf("/commands") +} + +func (c *Client4) GetCommandRoute(commandId string) string { + return fmt.Sprintf(c.GetCommandsRoute()+"/%v", commandId) +} + +func (c *Client4) GetEmojisRoute() string { + return fmt.Sprintf("/emoji") +} + +func (c *Client4) GetEmojiRoute(emojiId string) string { + return fmt.Sprintf(c.GetEmojisRoute()+"/%v", emojiId) +} + +func (c *Client4) GetReactionsRoute() string { + return fmt.Sprintf("/reactions") +} + +func (c *Client4) GetOAuthAppsRoute() string { + return fmt.Sprintf("/oauth/apps") +} + +func (c *Client4) GetOAuthAppRoute(appId string) string { + return fmt.Sprintf("/oauth/apps/%v", appId) +} + +func (c *Client4) GetOpenGraphRoute() string { + return fmt.Sprintf("/opengraph") +} + +func (c *Client4) GetJobsRoute() string { + return fmt.Sprintf("/jobs") +} + +func (c *Client4) GetAnalyticsRoute() string { + return fmt.Sprintf("/analytics") +} + func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodGet, url, "", etag) + return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag) } func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodPost, url, data, "") + return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "") } func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodPut, url, data, "") + return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "") } func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) { - return c.DoApiRequest(http.MethodDelete, url, "", "") + return c.DoApiRequest(http.MethodDelete, c.ApiUrl+url, "", "") } func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) { - rq, _ := http.NewRequest(method, c.ApiUrl+url, strings.NewReader(data)) + rq, _ := http.NewRequest(method, url, strings.NewReader(data)) rq.Close = true if len(etag) > 0 { @@ -174,7 +318,7 @@ func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) } - if rp, err := c.HttpClient.Do(rq); err != nil { + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) } else if rp.StatusCode == 304 { return rp, nil @@ -195,13 +339,60 @@ func (c *Client4) DoUploadFile(url string, data []byte, contentType string) (*Fi rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) } - if rp, err := c.HttpClient.Do(rq); err != nil { - return nil, &Response{Error: NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)} - } else if rp.StatusCode >= 300 { - return nil, &Response{StatusCode: rp.StatusCode, Error: AppErrorFromJson(rp.Body)} + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { + return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)) } else { defer closeBody(rp) - return FileUploadResponseFromJson(rp.Body), BuildResponse(rp) + + if rp.StatusCode >= 300 { + return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else { + return FileUploadResponseFromJson(rp.Body), BuildResponse(rp) + } + } +} + +func (c *Client4) DoEmojiUploadFile(url string, data []byte, contentType string) (*Emoji, *Response) { + rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data)) + rq.Header.Set("Content-Type", contentType) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { + return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)) + } else { + defer closeBody(rp) + + if rp.StatusCode >= 300 { + return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else { + return EmojiFromJson(rp.Body), BuildResponse(rp) + } + } +} + +func (c *Client4) DoUploadImportTeam(url string, data []byte, contentType string) (map[string]string, *Response) { + rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data)) + rq.Header.Set("Content-Type", contentType) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { + return nil, BuildErrorResponse(rp, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)) + } else { + defer closeBody(rp) + + if rp.StatusCode >= 300 { + return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else { + return MapFromJson(rp.Body), BuildResponse(rp) + } } } @@ -259,7 +450,7 @@ func (c *Client4) LoginWithDevice(loginId string, password string, deviceId stri func (c *Client4) login(m map[string]string) (*User, *Response) { if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { c.AuthToken = r.Header.Get(HEADER_TOKEN) c.AuthType = HEADER_BEARER @@ -271,7 +462,7 @@ func (c *Client4) login(m map[string]string) (*User, *Response) { // Logout terminates the current user's session. func (c *Client4) Logout() (bool, *Response) { if r, err := c.DoApiPost("/users/logout", ""); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { c.AuthToken = "" c.AuthType = HEADER_BEARER @@ -281,12 +472,66 @@ func (c *Client4) Logout() (bool, *Response) { } } +// SwitchAccountType changes a user's login type from one type to another. +func (c *Client4) SwitchAccountType(switchRequest *SwitchRequest) (string, *Response) { + if r, err := c.DoApiPost(c.GetUsersRoute()+"/login/switch", switchRequest.ToJson()); err != nil { + return "", BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body)["follow_link"], BuildResponse(r) + } +} + // User Section // CreateUser creates a user in the system based on the provided user struct. func (c *Client4) CreateUser(user *User) (*User, *Response) { if r, err := c.DoApiPost(c.GetUsersRoute(), user.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// CreateUserWithHash creates a user in the system based on the provided user struct and hash created. +func (c *Client4) CreateUserWithHash(user *User, hash, data string) (*User, *Response) { + var query string + if hash != "" && data != "" { + query = fmt.Sprintf("?d=%v&h=%v", url.QueryEscape(data), hash) + } else { + err := NewAppError("MissingHashOrData", "api.user.create_user.missing_hash_or_data.app_error", nil, "", http.StatusBadRequest) + return nil, &Response{StatusCode: err.StatusCode, Error: err} + } + if r, err := c.DoApiPost(c.GetUsersRoute()+query, user.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// CreateUserWithInviteId creates a user in the system based on the provided invited id. +func (c *Client4) CreateUserWithInviteId(user *User, inviteId string) (*User, *Response) { + var query string + if inviteId != "" { + query = fmt.Sprintf("?iid=%v", url.QueryEscape(inviteId)) + } else { + err := NewAppError("MissingInviteId", "api.user.create_user.missing_invite_id.app_error", nil, "", http.StatusBadRequest) + return nil, &Response{StatusCode: err.StatusCode, Error: err} + } + if r, err := c.DoApiPost(c.GetUsersRoute()+query, user.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserFromJson(r.Body), BuildResponse(r) + } +} + +// GetMe returns the logged in user. +func (c *Client4) GetMe(etag string) (*User, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(ME), etag); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserFromJson(r.Body), BuildResponse(r) @@ -296,7 +541,7 @@ func (c *Client4) CreateUser(user *User) (*User, *Response) { // GetUser returns a user based on the provided user id string. func (c *Client4) GetUser(userId, etag string) (*User, *Response) { if r, err := c.DoApiGet(c.GetUserRoute(userId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserFromJson(r.Body), BuildResponse(r) @@ -306,7 +551,7 @@ func (c *Client4) GetUser(userId, etag string) (*User, *Response) { // GetUserByUsername returns a user based on the provided user name string. func (c *Client4) GetUserByUsername(userName, etag string) (*User, *Response) { if r, err := c.DoApiGet(c.GetUserByUsernameRoute(userName), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserFromJson(r.Body), BuildResponse(r) @@ -316,21 +561,58 @@ func (c *Client4) GetUserByUsername(userName, etag string) (*User, *Response) { // GetUserByEmail returns a user based on the provided user email string. func (c *Client4) GetUserByEmail(email, etag string) (*User, *Response) { if r, err := c.DoApiGet(c.GetUserByEmailRoute(email), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserFromJson(r.Body), BuildResponse(r) } } +// AutocompleteUsersInTeam returns the users on a team based on search term. +func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, etag string) (*UserAutocomplete, *Response) { + query := fmt.Sprintf("?in_team=%v&name=%v", teamId, username) + if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserAutocompleteFromJson(r.Body), BuildResponse(r) + } +} + +// AutocompleteUsersInChannel returns the users in a channel based on search term. +func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, username string, etag string) (*UserAutocomplete, *Response) { + query := fmt.Sprintf("?in_team=%v&in_channel=%v&name=%v", teamId, channelId, username) + if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserAutocompleteFromJson(r.Body), BuildResponse(r) + } +} + +// AutocompleteUsers returns the users in the system based on search term. +func (c *Client4) AutocompleteUsers(username string, etag string) (*UserAutocomplete, *Response) { + query := fmt.Sprintf("?name=%v", username) + if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserAutocompleteFromJson(r.Body), BuildResponse(r) + } +} + // GetProfileImage gets user's profile image. Must be logged in or be a system administrator. func (c *Client4) GetProfileImage(userId, etag string) ([]byte, *Response) { if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/image", etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} - } else if data, err := ioutil.ReadAll(r.Body); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: NewAppError("GetProfileImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)} + return nil, BuildErrorResponse(r, err) } else { - return data, BuildResponse(r) + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("GetProfileImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } } } @@ -338,7 +620,7 @@ func (c *Client4) GetProfileImage(userId, etag string) ([]byte, *Response) { func (c *Client4) GetUsers(page int, perPage int, etag string) ([]*User, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserListFromJson(r.Body), BuildResponse(r) @@ -349,7 +631,40 @@ func (c *Client4) GetUsers(page int, perPage int, etag string) ([]*User, *Respon func (c *Client4) GetUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { query := fmt.Sprintf("?in_team=%v&page=%v&per_page=%v", teamId, page, perPage) if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetNewUsersInTeam returns a page of users on a team. Page counting starts at 0. +func (c *Client4) GetNewUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?sort=create_at&in_team=%v&page=%v&per_page=%v", teamId, page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetRecentlyActiveUsersInTeam returns a page of users on a team. Page counting starts at 0. +func (c *Client4) GetRecentlyActiveUsersInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?sort=last_activity_at&in_team=%v&page=%v&per_page=%v", teamId, page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersNotInTeam returns a page of users who are not in a team. Page counting starts at 0. +func (c *Client4) GetUsersNotInTeam(teamId string, page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?not_in_team=%v&page=%v&per_page=%v", teamId, page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserListFromJson(r.Body), BuildResponse(r) @@ -360,7 +675,7 @@ func (c *Client4) GetUsersInTeam(teamId string, page int, perPage int, etag stri func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, etag string) ([]*User, *Response) { query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v", channelId, page, perPage) if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserListFromJson(r.Body), BuildResponse(r) @@ -371,7 +686,18 @@ func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, eta func (c *Client4) GetUsersNotInChannel(teamId, channelId string, page int, perPage int, etag string) ([]*User, *Response) { query := fmt.Sprintf("?in_team=%v¬_in_channel=%v&page=%v&per_page=%v", teamId, channelId, page, perPage) if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersWithoutTeam returns a page of users on the system that aren't on any teams. Page counting starts at 0. +func (c *Client4) GetUsersWithoutTeam(page int, perPage int, etag string) ([]*User, *Response) { + query := fmt.Sprintf("?without_team=1&page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserListFromJson(r.Body), BuildResponse(r) @@ -381,7 +707,27 @@ func (c *Client4) GetUsersNotInChannel(teamId, channelId string, page int, perPa // GetUsersByIds returns a list of users based on the provided user ids. func (c *Client4) GetUsersByIds(userIds []string) ([]*User, *Response) { if r, err := c.DoApiPost(c.GetUsersRoute()+"/ids", ArrayToJson(userIds)); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersByUsernames returns a list of users based on the provided usernames. +func (c *Client4) GetUsersByUsernames(usernames []string) ([]*User, *Response) { + if r, err := c.DoApiPost(c.GetUsersRoute()+"/usernames", ArrayToJson(usernames)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserListFromJson(r.Body), BuildResponse(r) + } +} + +// SearchUsers returns a list of users based on some search criteria. +func (c *Client4) SearchUsers(search *UserSearch) ([]*User, *Response) { + if r, err := c.DoApiPost(c.GetUsersRoute()+"/search", search.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserListFromJson(r.Body), BuildResponse(r) @@ -391,7 +737,7 @@ func (c *Client4) GetUsersByIds(userIds []string) ([]*User, *Response) { // UpdateUser updates a user in the system based on the provided user struct. func (c *Client4) UpdateUser(user *User) (*User, *Response) { if r, err := c.DoApiPut(c.GetUserRoute(user.Id), user.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserFromJson(r.Body), BuildResponse(r) @@ -401,18 +747,64 @@ func (c *Client4) UpdateUser(user *User) (*User, *Response) { // PatchUser partially updates a user in the system. Any missing fields are not updated. func (c *Client4) PatchUser(userId string, patch *UserPatch) (*User, *Response) { if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/patch", patch.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return UserFromJson(r.Body), BuildResponse(r) } } +// UpdateUserMfa activates multi-factor authentication for a user if activate +// is true and a valid code is provided. If activate is false, then code is not +// required and multi-factor authentication is disabled for the user. +func (c *Client4) UpdateUserMfa(userId, code string, activate bool) (bool, *Response) { + requestBody := make(map[string]interface{}) + requestBody["activate"] = activate + requestBody["code"] = code + + if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/mfa", StringInterfaceToJson(requestBody)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// CheckUserMfa checks whether a user has MFA active on their account or not based on the +// provided login id. +func (c *Client4) CheckUserMfa(loginId string) (bool, *Response) { + requestBody := make(map[string]interface{}) + requestBody["login_id"] = loginId + + if r, err := c.DoApiPost(c.GetUsersRoute()+"/mfa", StringInterfaceToJson(requestBody)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + data := StringInterfaceFromJson(r.Body) + if mfaRequired, ok := data["mfa_required"].(bool); !ok { + return false, BuildResponse(r) + } else { + return mfaRequired, BuildResponse(r) + } + } +} + +// GenerateMfaSecret will generate a new MFA secret for a user and return it as a string and +// as a base64 encoded image QR code. +func (c *Client4) GenerateMfaSecret(userId string) (*MfaSecret, *Response) { + if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/mfa/generate", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MfaSecretFromJson(r.Body), BuildResponse(r) + } +} + // UpdateUserPassword updates a user's password. Must be logged in as the user or be a system administrator. func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string) (bool, *Response) { requestBody := map[string]string{"current_password": currentPassword, "new_password": newPassword} if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -423,7 +815,20 @@ func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) { requestBody := map[string]string{"roles": roles} if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/roles", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UpdateUserActive updates status of a user whether active or not. +func (c *Client4) UpdateUserActive(userId string, active bool) (bool, *Response) { + requestBody := make(map[string]interface{}) + requestBody["active"] = active + + if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/active", StringInterfaceToJson(requestBody)); err != nil { + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -433,7 +838,7 @@ func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) { // DeleteUser deactivates a user in the system based on the provided user id string. func (c *Client4) DeleteUser(userId string) (bool, *Response) { if r, err := c.DoApiDelete(c.GetUserRoute(userId)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -445,7 +850,7 @@ func (c *Client4) DeleteUser(userId string) (bool, *Response) { func (c *Client4) SendPasswordResetEmail(email string) (bool, *Response) { requestBody := map[string]string{"email": email} if r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset/send", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -453,10 +858,10 @@ func (c *Client4) SendPasswordResetEmail(email string) (bool, *Response) { } // ResetPassword uses a recovery code to update reset a user's password. -func (c *Client4) ResetPassword(code, newPassword string) (bool, *Response) { - requestBody := map[string]string{"code": code, "new_password": newPassword} +func (c *Client4) ResetPassword(token, newPassword string) (bool, *Response) { + requestBody := map[string]string{"token": token, "new_password": newPassword} if r, err := c.DoApiPost(c.GetUsersRoute()+"/password/reset", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -466,7 +871,7 @@ func (c *Client4) ResetPassword(code, newPassword string) (bool, *Response) { // GetSessions returns a list of sessions based on the provided user id string. func (c *Client4) GetSessions(userId, etag string) ([]*Session, *Response) { if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/sessions", etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return SessionsFromJson(r.Body), BuildResponse(r) @@ -477,15 +882,26 @@ func (c *Client4) GetSessions(userId, etag string) ([]*Session, *Response) { func (c *Client4) RevokeSession(userId, sessionId string) (bool, *Response) { requestBody := map[string]string{"session_id": sessionId} if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/sessions/revoke", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) } } -// getTeamsUnreadForUser will return an array with TeamUnread objects that contain the amount of -// unread messages and mentions the current user has for the teams it belongs to. +// AttachDeviceId attaches a mobile device ID to the current session. +func (c *Client4) AttachDeviceId(deviceId string) (bool, *Response) { + requestBody := map[string]string{"device_id": deviceId} + if r, err := c.DoApiPut(c.GetUsersRoute()+"/sessions/device", MapToJson(requestBody)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetTeamsUnreadForUser will return an array with TeamUnread objects that contain the amount +// of unread messages and mentions the current user has for the teams it belongs to. // An optional team ID can be set to exclude that team from the results. Must be authenticated. func (c *Client4) GetTeamsUnreadForUser(userId, teamIdToExclude string) ([]*TeamUnread, *Response) { optional := "" @@ -494,29 +910,42 @@ func (c *Client4) GetTeamsUnreadForUser(userId, teamIdToExclude string) ([]*Team } if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/unread"+optional, ""); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamsUnreadFromJson(r.Body), BuildResponse(r) } } -// GetAudits returns a list of audit based on the provided user id string. -func (c *Client4) GetAudits(userId string, page int, perPage int, etag string) (Audits, *Response) { +// GetUserAudits returns a list of audit based on the provided user id string. +func (c *Client4) GetUserAudits(userId string, page int, perPage int, etag string) (Audits, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/audits"+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return AuditsFromJson(r.Body), BuildResponse(r) } } -// Verify user email user id and hash strings. -func (c *Client4) VerifyUserEmail(userId, hashId string) (bool, *Response) { - requestBody := map[string]string{"uid": userId, "hid": hashId} - if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/email/verify", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} +// VerifyUserEmail will verify a user's email using the supplied token. +func (c *Client4) VerifyUserEmail(token string) (bool, *Response) { + requestBody := map[string]string{"token": token} + if r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify", MapToJson(requestBody)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// SendVerificationEmail will send an email to the user with the provided email address, if +// that user exists. The email will contain a link that can be used to verify the user's +// email address. +func (c *Client4) SendVerificationEmail(email string) (bool, *Response) { + requestBody := map[string]string{"email": email} + if r, err := c.DoApiPost(c.GetUsersRoute()+"/email/verify/send", MapToJson(requestBody)); err != nil { + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -546,14 +975,71 @@ func (c *Client4) SetProfileImage(userId string, data []byte) (bool, *Response) rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) } - if rp, err := c.HttpClient.Do(rq); err != nil { + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { // set to http.StatusForbidden(403) return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetUserRoute(userId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)} - } else if rp.StatusCode >= 300 { - return false, &Response{StatusCode: rp.StatusCode, Error: AppErrorFromJson(rp.Body)} } else { defer closeBody(rp) - return CheckStatusOK(rp), BuildResponse(rp) + + if rp.StatusCode >= 300 { + return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else { + return CheckStatusOK(rp), BuildResponse(rp) + } + } +} + +// CreateUserAccessToken will generate a user access token that can be used in place +// of a session token to access the REST API. Must have the 'create_user_access_token' +// permission and if generating for another user, must have the 'edit_other_users' +// permission. A non-blank description is required. +func (c *Client4) CreateUserAccessToken(userId, description string) (*UserAccessToken, *Response) { + requestBody := map[string]string{"description": description} + if r, err := c.DoApiPost(c.GetUserRoute(userId)+"/tokens", MapToJson(requestBody)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserAccessTokenFromJson(r.Body), BuildResponse(r) + } +} + +// GetUserAccessToken will get a user access token's id, description and the user_id +// of the user it is for. The actual token will not be returned. Must have the +// 'read_user_access_token' permission and if getting for another user, must have the +// 'edit_other_users' permission. +func (c *Client4) GetUserAccessToken(tokenId string) (*UserAccessToken, *Response) { + if r, err := c.DoApiGet(c.GetUserAccessTokenRoute(tokenId), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserAccessTokenFromJson(r.Body), BuildResponse(r) + } +} + +// GetUserAccessTokensForUser will get a paged list of user access tokens showing id, +// description and user_id for each. The actual tokens will not be returned. Must have +// the 'read_user_access_token' permission and if getting for another user, must have the +// 'edit_other_users' permission. +func (c *Client4) GetUserAccessTokensForUser(userId string, page, perPage int) ([]*UserAccessToken, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/tokens"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return UserAccessTokenListFromJson(r.Body), BuildResponse(r) + } +} + +// RevokeUserAccessToken will revoke a user access token by id. Must have the +// 'revoke_user_access_token' permission and if revoking for another user, must have the +// 'edit_other_users' permission. +func (c *Client4) RevokeUserAccessToken(tokenId string) (bool, *Response) { + requestBody := map[string]string{"token_id": tokenId} + if r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/revoke", MapToJson(requestBody)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) } } @@ -562,7 +1048,7 @@ func (c *Client4) SetProfileImage(userId string, data []byte) (bool, *Response) // CreateTeam creates a team in the system based on the provided team struct. func (c *Client4) CreateTeam(team *Team) (*Team, *Response) { if r, err := c.DoApiPost(c.GetTeamsRoute(), team.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamFromJson(r.Body), BuildResponse(r) @@ -572,7 +1058,7 @@ func (c *Client4) CreateTeam(team *Team) (*Team, *Response) { // GetTeam returns a team based on the provided team id string. func (c *Client4) GetTeam(teamId, etag string) (*Team, *Response) { if r, err := c.DoApiGet(c.GetTeamRoute(teamId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamFromJson(r.Body), BuildResponse(r) @@ -583,7 +1069,7 @@ func (c *Client4) GetTeam(teamId, etag string) (*Team, *Response) { func (c *Client4) GetAllTeams(etag string, page int, perPage int) ([]*Team, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamListFromJson(r.Body), BuildResponse(r) @@ -593,18 +1079,38 @@ func (c *Client4) GetAllTeams(etag string, page int, perPage int) ([]*Team, *Res // GetTeamByName returns a team based on the provided team name string. func (c *Client4) GetTeamByName(name, etag string) (*Team, *Response) { if r, err := c.DoApiGet(c.GetTeamByNameRoute(name), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamFromJson(r.Body), BuildResponse(r) } } +// SearchTeams returns teams matching the provided search term. +func (c *Client4) SearchTeams(search *TeamSearch) ([]*Team, *Response) { + if r, err := c.DoApiPost(c.GetTeamsRoute()+"/search", search.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamListFromJson(r.Body), BuildResponse(r) + } +} + +// TeamExists returns true or false if the team exist or not. +func (c *Client4) TeamExists(name, etag string) (bool, *Response) { + if r, err := c.DoApiGet(c.GetTeamByNameRoute(name)+"/exists", etag); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapBoolFromJson(r.Body)["exists"], BuildResponse(r) + } +} + // GetTeamsForUser returns a list of teams a user is on. Must be logged in as the user // or be a system administrator. func (c *Client4) GetTeamsForUser(userId, etag string) ([]*Team, *Response) { if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams", etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamListFromJson(r.Body), BuildResponse(r) @@ -614,18 +1120,59 @@ func (c *Client4) GetTeamsForUser(userId, etag string) ([]*Team, *Response) { // GetTeamMember returns a team member based on the provided team and user id strings. func (c *Client4) GetTeamMember(teamId, userId, etag string) (*TeamMember, *Response) { if r, err := c.DoApiGet(c.GetTeamMemberRoute(teamId, userId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamMemberFromJson(r.Body), BuildResponse(r) } } -// UpdateTeamMemberRoles will update the roles on a team for a user +// UpdateTeamMemberRoles will update the roles on a team for a user. func (c *Client4) UpdateTeamMemberRoles(teamId, userId, newRoles string) (bool, *Response) { requestBody := map[string]string{"roles": newRoles} if r, err := c.DoApiPut(c.GetTeamMemberRoute(teamId, userId)+"/roles", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UpdateTeam will update a team. +func (c *Client4) UpdateTeam(team *Team) (*Team, *Response) { + if r, err := c.DoApiPut(c.GetTeamRoute(team.Id), team.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamFromJson(r.Body), BuildResponse(r) + } +} + +// PatchTeam partially updates a team. Any missing fields are not updated. +func (c *Client4) PatchTeam(teamId string, patch *TeamPatch) (*Team, *Response) { + if r, err := c.DoApiPut(c.GetTeamRoute(teamId)+"/patch", patch.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamFromJson(r.Body), BuildResponse(r) + } +} + +// SoftDeleteTeam deletes the team softly (archive only, not permanent delete). +func (c *Client4) SoftDeleteTeam(teamId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetTeamRoute(teamId)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// PermanentDeleteTeam deletes the team, should only be used when needed for +// compliance and the like +func (c *Client4) PermanentDeleteTeam(teamId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "?permanent=true"); err != nil { + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -636,30 +1183,202 @@ func (c *Client4) UpdateTeamMemberRoles(teamId, userId, newRoles string) (bool, func (c *Client4) GetTeamMembers(teamId string, page int, perPage int, etag string) ([]*TeamMember, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if r, err := c.DoApiGet(c.GetTeamMembersRoute(teamId)+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamMembersFromJson(r.Body), BuildResponse(r) + } +} + +// GetTeamMembersForUser returns the team members for a user. +func (c *Client4) GetTeamMembersForUser(userId string, etag string) ([]*TeamMember, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/teams/members", etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamMembersFromJson(r.Body), BuildResponse(r) + } +} + +// GetTeamMembersByIds will return an array of team members based on the +// team id and a list of user ids provided. Must be authenticated. +func (c *Client4) GetTeamMembersByIds(teamId string, userIds []string) ([]*TeamMember, *Response) { + if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v/members/ids", teamId), ArrayToJson(userIds)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamMembersFromJson(r.Body), BuildResponse(r) + } +} + +// AddTeamMember adds user to a team and return a team member. +func (c *Client4) AddTeamMember(teamId, userId string) (*TeamMember, *Response) { + member := &TeamMember{TeamId: teamId, UserId: userId} + + if r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId), member.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamMemberFromJson(r.Body), BuildResponse(r) + } +} + +// AddTeamMemberFromInvite adds a user to a team and return a team member using an invite id +// or an invite hash/data pair. +func (c *Client4) AddTeamMemberFromInvite(hash, dataToHash, inviteId string) (*TeamMember, *Response) { + var query string + + if inviteId != "" { + query += fmt.Sprintf("?invite_id=%v", inviteId) + } + + if hash != "" && dataToHash != "" { + query += fmt.Sprintf("?hash=%v&data=%v", hash, dataToHash) + } + + if r, err := c.DoApiPost(c.GetTeamsRoute()+"/members/invite"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamMemberFromJson(r.Body), BuildResponse(r) + } +} + +// AddTeamMembers adds a number of users to a team and returns the team members. +func (c *Client4) AddTeamMembers(teamId string, userIds []string) ([]*TeamMember, *Response) { + var members []*TeamMember + for _, userId := range userIds { + member := &TeamMember{TeamId: teamId, UserId: userId} + members = append(members, member) + } + + if r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch", TeamMembersToJson(members)); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamMembersFromJson(r.Body), BuildResponse(r) } } +// RemoveTeamMember will remove a user from a team. +func (c *Client4) RemoveTeamMember(teamId, userId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetTeamMemberRoute(teamId, userId)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // GetTeamStats returns a team stats based on the team id string. // Must be authenticated. func (c *Client4) GetTeamStats(teamId, etag string) (*TeamStats, *Response) { if r, err := c.DoApiGet(c.GetTeamStatsRoute(teamId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return TeamStatsFromJson(r.Body), BuildResponse(r) } } +// GetTeamUnread will return a TeamUnread object that contains the amount of +// unread messages and mentions the user has for the specified team. +// Must be authenticated. +func (c *Client4) GetTeamUnread(teamId, userId string) (*TeamUnread, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetTeamRoute(teamId)+"/unread", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamUnreadFromJson(r.Body), BuildResponse(r) + } +} + +// ImportTeam will import an exported team from other app into a existing team. +func (c *Client4) ImportTeam(data []byte, filesize int, importFrom, filename, teamId string) (map[string]string, *Response) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("file", filename); err != nil { + return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if part, err := writer.CreateFormField("filesize"); err != nil { + return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file_size.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, strings.NewReader(strconv.Itoa(filesize))); err != nil { + return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.file_size.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if part, err := writer.CreateFormField("importFrom"); err != nil { + return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.import_from.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, strings.NewReader(importFrom)); err != nil { + return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.import_from.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if err := writer.Close(); err != nil { + return nil, &Response{Error: NewAppError("UploadImportTeam", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + return c.DoUploadImportTeam(c.GetTeamImportRoute(teamId), body.Bytes(), writer.FormDataContentType()) +} + +// InviteUsersToTeam invite users by email to the team. +func (c *Client4) InviteUsersToTeam(teamId string, userEmails []string) (bool, *Response) { + if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email", ArrayToJson(userEmails)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetTeamInviteInfo returns a team object from an invite id containing sanitized information. +func (c *Client4) GetTeamInviteInfo(inviteId string) (*Team, *Response) { + if r, err := c.DoApiGet(c.GetTeamsRoute()+"/invite/"+inviteId, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return TeamFromJson(r.Body), BuildResponse(r) + } +} + // Channel Section // CreateChannel creates a channel based on the provided channel struct. func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) { if r, err := c.DoApiPost(c.GetChannelsRoute(), channel.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateChannel update a channel based on the provided channel struct. +func (c *Client4) UpdateChannel(channel *Channel) (*Channel, *Response) { + if r, err := c.DoApiPut(c.GetChannelRoute(channel.Id), channel.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// PatchChannel partially updates a channel. Any missing fields are not updated. +func (c *Client4) PatchChannel(channelId string, patch *ChannelPatch) (*Channel, *Response) { + if r, err := c.DoApiPut(c.GetChannelRoute(channelId)+"/patch", patch.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// RestoreChannel restores a previously deleted channel. Any missing fields are not updated. +func (c *Client4) RestoreChannel(channelId string) (*Channel, *Response) { + if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/restore", ""); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelFromJson(r.Body), BuildResponse(r) @@ -671,7 +1390,17 @@ func (c *Client4) CreateChannel(channel *Channel) (*Channel, *Response) { func (c *Client4) CreateDirectChannel(userId1, userId2 string) (*Channel, *Response) { requestBody := []string{userId1, userId2} if r, err := c.DoApiPost(c.GetChannelsRoute()+"/direct", ArrayToJson(requestBody)); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelFromJson(r.Body), BuildResponse(r) + } +} + +// CreateGroupChannel creates a group message channel based on userIds provided +func (c *Client4) CreateGroupChannel(userIds []string) (*Channel, *Response) { + if r, err := c.DoApiPost(c.GetChannelsRoute()+"/group", ArrayToJson(userIds)); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelFromJson(r.Body), BuildResponse(r) @@ -681,17 +1410,99 @@ func (c *Client4) CreateDirectChannel(userId1, userId2 string) (*Channel, *Respo // GetChannel returns a channel based on the provided channel id string. func (c *Client4) GetChannel(channelId, etag string) (*Channel, *Response) { if r, err := c.DoApiGet(c.GetChannelRoute(channelId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelFromJson(r.Body), BuildResponse(r) } } +// GetChannelStats returns statistics for a channel. +func (c *Client4) GetChannelStats(channelId string, etag string) (*ChannelStats, *Response) { + if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/stats", etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelStatsFromJson(r.Body), BuildResponse(r) + } +} + +// GetPinnedPosts gets a list of pinned posts. +func (c *Client4) GetPinnedPosts(channelId string, etag string) (*PostList, *Response) { + if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// GetPublicChannelsForTeam returns a list of public channels based on the provided team id string. +func (c *Client4) GetPublicChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelSliceFromJson(r.Body), BuildResponse(r) + } +} + +// GetDeletedChannelsForTeam returns a list of public channels based on the provided team id string. +func (c *Client4) GetDeletedChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) { + query := fmt.Sprintf("/deleted?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelSliceFromJson(r.Body), BuildResponse(r) + } +} + +// GetPublicChannelsByIdsForTeam returns a list of public channels based on provided team id string +func (c *Client4) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) ([]*Channel, *Response) { + if r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/ids", ArrayToJson(channelIds)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelSliceFromJson(r.Body), BuildResponse(r) + } +} + +// GetChannelsForTeamForUser returns a list channels of on a team for a user. +func (c *Client4) GetChannelsForTeamForUser(teamId, userId, etag string) ([]*Channel, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetTeamRoute(teamId)+"/channels", etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelSliceFromJson(r.Body), BuildResponse(r) + } +} + +// SearchChannels returns the channels on a team matching the provided search term. +func (c *Client4) SearchChannels(teamId string, search *ChannelSearch) ([]*Channel, *Response) { + if r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/search", search.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelSliceFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteChannel deletes channel based on the provided channel id string. +func (c *Client4) DeleteChannel(channelId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetChannelRoute(channelId)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // GetChannelByName returns a channel based on the provided channel name and team id strings. func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) { if r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelFromJson(r.Body), BuildResponse(r) @@ -701,7 +1512,7 @@ func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Ch // GetChannelByNameForTeamName returns a channel based on the provided channel name and team name strings. func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag string) (*Channel, *Response) { if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelFromJson(r.Body), BuildResponse(r) @@ -712,17 +1523,28 @@ func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag func (c *Client4) GetChannelMembers(channelId string, page, perPage int, etag string) (*ChannelMembers, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if r, err := c.DoApiGet(c.GetChannelMembersRoute(channelId)+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelMembersFromJson(r.Body), BuildResponse(r) } } +// GetChannelMembersByIds gets the channel members in a channel for a list of user ids. +func (c *Client4) GetChannelMembersByIds(channelId string, userIds []string) (*ChannelMembers, *Response) { + if r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"/ids", ArrayToJson(userIds)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelMembersFromJson(r.Body), BuildResponse(r) + + } +} + // GetChannelMember gets a channel member. func (c *Client4) GetChannelMember(channelId, userId, etag string) (*ChannelMember, *Response) { if r, err := c.DoApiGet(c.GetChannelMemberRoute(channelId, userId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelMemberFromJson(r.Body), BuildResponse(r) @@ -732,7 +1554,7 @@ func (c *Client4) GetChannelMember(channelId, userId, etag string) (*ChannelMemb // GetChannelMembersForUser gets all the channel members for a user on a team. func (c *Client4) GetChannelMembersForUser(userId, teamId, etag string) (*ChannelMembers, *Response) { if r, err := c.DoApiGet(fmt.Sprintf(c.GetUserRoute(userId)+"/teams/%v/channels/members", teamId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return ChannelMembersFromJson(r.Body), BuildResponse(r) @@ -743,28 +1565,60 @@ func (c *Client4) GetChannelMembersForUser(userId, teamId, etag string) (*Channe func (c *Client4) ViewChannel(userId string, view *ChannelView) (bool, *Response) { url := fmt.Sprintf(c.GetChannelsRoute()+"/members/%v/view", userId) if r, err := c.DoApiPost(url, view.ToJson()); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) } } +// GetChannelUnread will return a ChannelUnread object that contains the number of +// unread messages and mentions for a user. +func (c *Client4) GetChannelUnread(channelId, userId string) (*ChannelUnread, *Response) { + if r, err := c.DoApiGet(c.GetUserRoute(userId)+c.GetChannelRoute(channelId)+"/unread", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelUnreadFromJson(r.Body), BuildResponse(r) + } +} + // UpdateChannelRoles will update the roles on a channel for a user. func (c *Client4) UpdateChannelRoles(channelId, userId, roles string) (bool, *Response) { requestBody := map[string]string{"roles": roles} if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/roles", MapToJson(requestBody)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UpdateChannelNotifyProps will update the notification properties on a channel for a user. +func (c *Client4) UpdateChannelNotifyProps(channelId, userId string, props map[string]string) (bool, *Response) { + if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/notify_props", MapToJson(props)); err != nil { + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) } } +// AddChannelMember adds user to channel and return a channel member. +func (c *Client4) AddChannelMember(channelId, userId string) (*ChannelMember, *Response) { + requestBody := map[string]string{"user_id": userId} + if r, err := c.DoApiPost(c.GetChannelMembersRoute(channelId)+"", MapToJson(requestBody)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ChannelMemberFromJson(r.Body), BuildResponse(r) + } +} + // RemoveUserFromChannel will delete the channel member object for a user, effectively removing the user from a channel. func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Response) { if r, err := c.DoApiDelete(c.GetChannelMemberRoute(channelId, userId)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -776,7 +1630,7 @@ func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Respon // CreatePost creates a post based on the provided post struct. func (c *Client4) CreatePost(post *Post) (*Post, *Response) { if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return PostFromJson(r.Body), BuildResponse(r) @@ -786,17 +1640,47 @@ func (c *Client4) CreatePost(post *Post) (*Post, *Response) { // UpdatePost updates a post based on the provided post struct. func (c *Client4) UpdatePost(postId string, post *Post) (*Post, *Response) { if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostFromJson(r.Body), BuildResponse(r) + } +} + +// PatchPost partially updates a post. Any missing fields are not updated. +func (c *Client4) PatchPost(postId string, patch *PostPatch) (*Post, *Response) { + if r, err := c.DoApiPut(c.GetPostRoute(postId)+"/patch", patch.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return PostFromJson(r.Body), BuildResponse(r) } } +// PinPost pin a post based on provided post id string. +func (c *Client4) PinPost(postId string) (bool, *Response) { + if r, err := c.DoApiPost(c.GetPostRoute(postId)+"/pin", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UnpinPost unpin a post based on provided post id string. +func (c *Client4) UnpinPost(postId string) (bool, *Response) { + if r, err := c.DoApiPost(c.GetPostRoute(postId)+"/unpin", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // GetPost gets a single post. func (c *Client4) GetPost(postId string, etag string) (*Post, *Response) { if r, err := c.DoApiGet(c.GetPostRoute(postId), etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return PostFromJson(r.Body), BuildResponse(r) @@ -806,7 +1690,7 @@ func (c *Client4) GetPost(postId string, etag string) (*Post, *Response) { // DeletePost deletes a post from the provided post id string. func (c *Client4) DeletePost(postId string) (bool, *Response) { if r, err := c.DoApiDelete(c.GetPostRoute(postId)); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) @@ -816,7 +1700,7 @@ func (c *Client4) DeletePost(postId string) (bool, *Response) { // GetPostThread gets a post with all the other posts in the same thread. func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Response) { if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/thread", etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return PostListFromJson(r.Body), BuildResponse(r) @@ -827,7 +1711,81 @@ func (c *Client4) GetPostThread(postId string, etag string) (*PostList, *Respons func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag string) (*PostList, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// GetFlaggedPostsForUser returns flagged posts of a user based on user id string. +func (c *Client4) GetFlaggedPostsForUser(userId string, page int, perPage int) (*PostList, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// GetFlaggedPostsForUserInTeam returns flagged posts in team of a user based on user id string. +func (c *Client4) GetFlaggedPostsForUserInTeam(userId string, teamId string, page int, perPage int) (*PostList, *Response) { + if len(teamId) == 0 || len(teamId) != 26 { + return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError("GetFlaggedPostsForUserInTeam", "model.client.get_flagged_posts_in_team.missing_parameter.app_error", nil, "", http.StatusBadRequest)} + } + + query := fmt.Sprintf("?team_id=%v&page=%v&per_page=%v", teamId, page, perPage) + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// GetFlaggedPostsForUserInChannel returns flagged posts in channel of a user based on user id string. +func (c *Client4) GetFlaggedPostsForUserInChannel(userId string, channelId string, page int, perPage int) (*PostList, *Response) { + if len(channelId) == 0 || len(channelId) != 26 { + return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError("GetFlaggedPostsForUserInChannel", "model.client.get_flagged_posts_in_channel.missing_parameter.app_error", nil, "", http.StatusBadRequest)} + } + + query := fmt.Sprintf("?channel_id=%v&page=%v&per_page=%v", channelId, page, perPage) + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/posts/flagged"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// GetPostsSince gets posts created after a specified time as Unix time in milliseconds. +func (c *Client4) GetPostsSince(channelId string, time int64) (*PostList, *Response) { + query := fmt.Sprintf("?since=%v", time) + if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// GetPostsAfter gets a page of posts that were posted after the post provided. +func (c *Client4) GetPostsAfter(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&after=%v", page, perPage, postId) + if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return PostListFromJson(r.Body), BuildResponse(r) + } +} + +// GetPostsBefore gets a page of posts that were posted before the post provided. +func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, etag string) (*PostList, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&before=%v", page, perPage, postId) + if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/posts"+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return PostListFromJson(r.Body), BuildResponse(r) @@ -836,9 +1794,9 @@ func (c *Client4) GetPostsForChannel(channelId string, page, perPage int, etag s // SearchPosts returns any posts with matching terms string. func (c *Client4) SearchPosts(teamId string, terms string, isOrSearch bool) (*PostList, *Response) { - requestBody := map[string]string{"terms": terms, "is_or_search": strconv.FormatBool(isOrSearch)} - if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", MapToJson(requestBody)); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch} + if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", StringInterfaceToJson(requestBody)); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return PostListFromJson(r.Body), BuildResponse(r) @@ -874,53 +1832,298 @@ func (c *Client4) UploadFile(data []byte, channelId string, filename string) (*F // GetFile gets the bytes for a file by id. func (c *Client4) GetFile(fileId string) ([]byte, *Response) { if r, err := c.DoApiGet(c.GetFileRoute(fileId), ""); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} - } else if data, err := ioutil.ReadAll(r.Body); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: NewAppError("GetFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("GetFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// DownloadFile gets the bytes for a file by id, optionally adding headers to force the browser to download it +func (c *Client4) DownloadFile(fileId string, download bool) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("?download=%v", download), ""); err != nil { + return nil, BuildErrorResponse(r, err) } else { - return data, BuildResponse(r) + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("DownloadFile", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } } } // GetFileThumbnail gets the bytes for a file by id. func (c *Client4) GetFileThumbnail(fileId string) ([]byte, *Response) { if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/thumbnail", ""); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} - } else if data, err := ioutil.ReadAll(r.Body); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: NewAppError("GetFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)} + return nil, BuildErrorResponse(r, err) } else { - return data, BuildResponse(r) + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("GetFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// DownloadFileThumbnail gets the bytes for a file by id, optionally adding headers to force the browser to download it. +func (c *Client4) DownloadFileThumbnail(fileId string, download bool) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("/thumbnail?download=%v", download), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("DownloadFileThumbnail", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// GetFileLink gets the public link of a file by id. +func (c *Client4) GetFileLink(fileId string) (string, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/link", ""); err != nil { + return "", BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + return MapFromJson(r.Body)["link"], BuildResponse(r) + } +} + +// GetFilePreview gets the bytes for a file by id. +func (c *Client4) GetFilePreview(fileId string) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/preview", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("GetFilePreview", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// DownloadFilePreview gets the bytes for a file by id. +func (c *Client4) DownloadFilePreview(fileId string, download bool) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId)+fmt.Sprintf("/preview?download=%v", download), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("DownloadFilePreview", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// GetFileInfo gets all the file info objects. +func (c *Client4) GetFileInfo(fileId string) (*FileInfo, *Response) { + if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/info", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return FileInfoFromJson(r.Body), BuildResponse(r) } } // GetFileInfosForPost gets all the file info objects attached to a post. func (c *Client4) GetFileInfosForPost(postId string, etag string) ([]*FileInfo, *Response) { if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/files/info", etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return FileInfosFromJson(r.Body), BuildResponse(r) } } -// General Section +// General/System Section + +// GetPing will return ok if the running goRoutines are below the threshold and unhealthy for above. +func (c *Client4) GetPing() (string, *Response) { + if r, err := c.DoApiGet(c.GetSystemRoute()+"/ping", ""); r != nil && r.StatusCode == 500 { + defer r.Body.Close() + return "unhealthy", BuildErrorResponse(r, err) + } else if err != nil { + return "", BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body)["status"], BuildResponse(r) + } +} + +// TestEmail will attempt to connect to the configured SMTP server. +func (c *Client4) TestEmail() (bool, *Response) { + if r, err := c.DoApiPost(c.GetTestEmailRoute(), ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetConfig will retrieve the server config with some sanitized items. +func (c *Client4) GetConfig() (*Config, *Response) { + if r, err := c.DoApiGet(c.GetConfigRoute(), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ConfigFromJson(r.Body), BuildResponse(r) + } +} -// GetPing will ping the server and to see if it is up and running. -func (c *Client4) GetPing() (bool, *Response) { - if r, err := c.DoApiGet(c.GetSystemRoute()+"/ping", ""); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} +// ReloadConfig will reload the server configuration. +func (c *Client4) ReloadConfig() (bool, *Response) { + if r, err := c.DoApiPost(c.GetConfigRoute()+"/reload", ""); err != nil { + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return CheckStatusOK(r), BuildResponse(r) } } +// GetOldClientConfig will retrieve the parts of the server configuration needed by the +// client, formatted in the old format. +func (c *Client4) GetOldClientConfig(etag string) (map[string]string, *Response) { + if r, err := c.DoApiGet(c.GetConfigRoute()+"/client?format=old", etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body), BuildResponse(r) + } +} + +// GetOldClientLicense will retrieve the parts of the server license needed by the +// client, formatted in the old format. +func (c *Client4) GetOldClientLicense(etag string) (map[string]string, *Response) { + if r, err := c.DoApiGet(c.GetLicenseRoute()+"/client?format=old", etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body), BuildResponse(r) + } +} + +// DatabaseRecycle will recycle the connections. Discard current connection and get new one. +func (c *Client4) DatabaseRecycle() (bool, *Response) { + if r, err := c.DoApiPost(c.GetDatabaseRoute()+"/recycle", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// InvalidateCaches will purge the cache and can affect the performance while is cleaning. +func (c *Client4) InvalidateCaches() (bool, *Response) { + if r, err := c.DoApiPost(c.GetCacheRoute()+"/invalidate", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// UpdateConfig will update the server configuration. +func (c *Client4) UpdateConfig(config *Config) (*Config, *Response) { + if r, err := c.DoApiPut(c.GetConfigRoute(), config.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ConfigFromJson(r.Body), BuildResponse(r) + } +} + +// UploadLicenseFile will add a license file to the system. +func (c *Client4) UploadLicenseFile(data []byte) (bool, *Response) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("license", "test-license.mattermost-license"); err != nil { + return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if err := writer.Close(); err != nil { + return false, &Response{Error: NewAppError("UploadLicenseFile", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetLicenseRoute(), bytes.NewReader(body.Bytes())) + rq.Header.Set("Content-Type", writer.FormDataContentType()) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { + return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetLicenseRoute(), "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} + } else { + defer closeBody(rp) + + if rp.StatusCode >= 300 { + return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else { + return CheckStatusOK(rp), BuildResponse(rp) + } + } +} + +// RemoveLicenseFile will remove the server license it exists. Note that this will +// disable all enterprise features. +func (c *Client4) RemoveLicenseFile() (bool, *Response) { + if r, err := c.DoApiDelete(c.GetLicenseRoute()); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetAnalyticsOld will retrieve analytics using the old format. New format is not +// available but the "/analytics" endpoint is reserved for it. The "name" argument is optional +// and defaults to "standard". The "teamId" argument is optional and will limit results +// to a specific team. +func (c *Client4) GetAnalyticsOld(name, teamId string) (AnalyticsRows, *Response) { + query := fmt.Sprintf("?name=%v&teamId=%v", name, teamId) + if r, err := c.DoApiGet(c.GetAnalyticsRoute()+"/old"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return AnalyticsRowsFromJson(r.Body), BuildResponse(r) + } +} + // Webhooks Section // CreateIncomingWebhook creates an incoming webhook for a channel. func (c *Client4) CreateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) { if r, err := c.DoApiPost(c.GetIncomingWebhooksRoute(), hook.ToJson()); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return IncomingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateIncomingWebhook updates an incoming webhook for a channel. +func (c *Client4) UpdateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook, *Response) { + if r, err := c.DoApiPut(c.GetIncomingWebhookRoute(hook.Id), hook.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return IncomingWebhookFromJson(r.Body), BuildResponse(r) @@ -931,7 +2134,7 @@ func (c *Client4) CreateIncomingWebhook(hook *IncomingWebhook) (*IncomingWebhook func (c *Client4) GetIncomingWebhooks(page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return IncomingWebhookListFromJson(r.Body), BuildResponse(r) @@ -942,19 +2145,122 @@ func (c *Client4) GetIncomingWebhooks(page int, perPage int, etag string) ([]*In func (c *Client4) GetIncomingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*IncomingWebhook, *Response) { query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId) if r, err := c.DoApiGet(c.GetIncomingWebhooksRoute()+query, etag); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return IncomingWebhookListFromJson(r.Body), BuildResponse(r) } } +// GetIncomingWebhook returns an Incoming webhook given the hook ID +func (c *Client4) GetIncomingWebhook(hookID string, etag string) (*IncomingWebhook, *Response) { + if r, err := c.DoApiGet(c.GetIncomingWebhookRoute(hookID), etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return IncomingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteIncomingWebhook deletes and Incoming Webhook given the hook ID +func (c *Client4) DeleteIncomingWebhook(hookID string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetIncomingWebhookRoute(hookID)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// CreateOutgoingWebhook creates an outgoing webhook for a team or channel. +func (c *Client4) CreateOutgoingWebhook(hook *OutgoingWebhook) (*OutgoingWebhook, *Response) { + if r, err := c.DoApiPost(c.GetOutgoingWebhooksRoute(), hook.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OutgoingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateOutgoingWebhook creates an outgoing webhook for a team or channel. +func (c *Client4) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*OutgoingWebhook, *Response) { + if r, err := c.DoApiPut(c.GetOutgoingWebhookRoute(hook.Id), hook.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OutgoingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// GetOutgoingWebhooks returns a page of outgoing webhooks on the system. Page counting starts at 0. +func (c *Client4) GetOutgoingWebhooks(page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// GetOutgoingWebhook outgoing webhooks on the system requested by Hook Id. +func (c *Client4) GetOutgoingWebhook(hookId string) (*OutgoingWebhook, *Response) { + if r, err := c.DoApiGet(c.GetOutgoingWebhookRoute(hookId), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OutgoingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// GetOutgoingWebhooksForChannel returns a page of outgoing webhooks for a channel. Page counting starts at 0. +func (c *Client4) GetOutgoingWebhooksForChannel(channelId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&channel_id=%v", page, perPage, channelId) + if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// GetOutgoingWebhooksForTeam returns a page of outgoing webhooks for a team. Page counting starts at 0. +func (c *Client4) GetOutgoingWebhooksForTeam(teamId string, page int, perPage int, etag string) ([]*OutgoingWebhook, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v&team_id=%v", page, perPage, teamId) + if r, err := c.DoApiGet(c.GetOutgoingWebhooksRoute()+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OutgoingWebhookListFromJson(r.Body), BuildResponse(r) + } +} + +// RegenOutgoingHookToken regenerate the outgoing webhook token. +func (c *Client4) RegenOutgoingHookToken(hookId string) (*OutgoingWebhook, *Response) { + if r, err := c.DoApiPost(c.GetOutgoingWebhookRoute(hookId)+"/regen_token", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OutgoingWebhookFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteOutgoingWebhook delete the outgoing webhook on the system requested by Hook Id. +func (c *Client4) DeleteOutgoingWebhook(hookId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetOutgoingWebhookRoute(hookId)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // Preferences Section -// GetPreferences returns the user's preferences +// GetPreferences returns the user's preferences. func (c *Client4) GetPreferences(userId string) (Preferences, *Response) { if r, err := c.DoApiGet(c.GetPreferencesRoute(userId), ""); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { preferences, _ := PreferencesFromJson(r.Body) defer closeBody(r) @@ -962,31 +2268,31 @@ func (c *Client4) GetPreferences(userId string) (Preferences, *Response) { } } -// UpdatePreferences saves the user's preferences +// UpdatePreferences saves the user's preferences. func (c *Client4) UpdatePreferences(userId string, preferences *Preferences) (bool, *Response) { if r, err := c.DoApiPut(c.GetPreferencesRoute(userId), preferences.ToJson()); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return true, BuildResponse(r) } } -// DeletePreferences deletes the user's preferences +// DeletePreferences deletes the user's preferences. func (c *Client4) DeletePreferences(userId string, preferences *Preferences) (bool, *Response) { if r, err := c.DoApiPost(c.GetPreferencesRoute(userId)+"/delete", preferences.ToJson()); err != nil { - return false, &Response{StatusCode: r.StatusCode, Error: err} + return false, BuildErrorResponse(r, err) } else { defer closeBody(r) return true, BuildResponse(r) } } -// GetPreferencesByCategory returns the user's preferences from the provided category string +// GetPreferencesByCategory returns the user's preferences from the provided category string. func (c *Client4) GetPreferencesByCategory(userId string, category string) (Preferences, *Response) { url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s", category) if r, err := c.DoApiGet(url, ""); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { preferences, _ := PreferencesFromJson(r.Body) defer closeBody(r) @@ -994,13 +2300,712 @@ func (c *Client4) GetPreferencesByCategory(userId string, category string) (Pref } } -// GetPreferenceByCategoryAndName returns the user's preferences from the provided category and preference name string +// GetPreferenceByCategoryAndName returns the user's preferences from the provided category and preference name string. func (c *Client4) GetPreferenceByCategoryAndName(userId string, category string, preferenceName string) (*Preference, *Response) { url := fmt.Sprintf(c.GetPreferencesRoute(userId)+"/%s/name/%v", category, preferenceName) if r, err := c.DoApiGet(url, ""); err != nil { - return nil, &Response{StatusCode: r.StatusCode, Error: err} + return nil, BuildErrorResponse(r, err) } else { defer closeBody(r) return PreferenceFromJson(r.Body), BuildResponse(r) } } + +// SAML Section + +// GetSamlMetadata returns metadata for the SAML configuration. +func (c *Client4) GetSamlMetadata() (string, *Response) { + if r, err := c.DoApiGet(c.GetSamlRoute()+"/metadata", ""); err != nil { + return "", BuildErrorResponse(r, err) + } else { + defer closeBody(r) + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + return buf.String(), BuildResponse(r) + } +} + +func samlFileToMultipart(data []byte, filename string) ([]byte, *multipart.Writer, error) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("certificate", filename); err != nil { + return nil, nil, err + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return nil, nil, err + } + + if err := writer.Close(); err != nil { + return nil, nil, err + } + + return body.Bytes(), writer, nil +} + +// UploadSamlIdpCertificate will upload an IDP certificate for SAML and set the config to use it. +func (c *Client4) UploadSamlIdpCertificate(data []byte, filename string) (bool, *Response) { + body, writer, err := samlFileToMultipart(data, filename) + if err != nil { + return false, &Response{Error: NewAppError("UploadSamlIdpCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + _, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/idp", body, writer.FormDataContentType()) + return resp.Error == nil, resp +} + +// UploadSamlPublicCertificate will upload a public certificate for SAML and set the config to use it. +func (c *Client4) UploadSamlPublicCertificate(data []byte, filename string) (bool, *Response) { + body, writer, err := samlFileToMultipart(data, filename) + if err != nil { + return false, &Response{Error: NewAppError("UploadSamlPublicCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + _, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/public", body, writer.FormDataContentType()) + return resp.Error == nil, resp +} + +// UploadSamlPrivateCertificate will upload a private key for SAML and set the config to use it. +func (c *Client4) UploadSamlPrivateCertificate(data []byte, filename string) (bool, *Response) { + body, writer, err := samlFileToMultipart(data, filename) + if err != nil { + return false, &Response{Error: NewAppError("UploadSamlPrivateCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + _, resp := c.DoUploadFile(c.GetSamlRoute()+"/certificate/private", body, writer.FormDataContentType()) + return resp.Error == nil, resp +} + +// DeleteSamlIdpCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML. +func (c *Client4) DeleteSamlIdpCertificate() (bool, *Response) { + if r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/idp"); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// DeleteSamlPublicCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML. +func (c *Client4) DeleteSamlPublicCertificate() (bool, *Response) { + if r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/public"); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// DeleteSamlPrivateCertificate deletes the SAML IDP certificate from the server and updates the config to not use it and disable SAML. +func (c *Client4) DeleteSamlPrivateCertificate() (bool, *Response) { + if r, err := c.DoApiDelete(c.GetSamlRoute() + "/certificate/private"); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetSamlCertificateStatus returns metadata for the SAML configuration. +func (c *Client4) GetSamlCertificateStatus() (*SamlCertificateStatus, *Response) { + if r, err := c.DoApiGet(c.GetSamlRoute()+"/certificate/status", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return SamlCertificateStatusFromJson(r.Body), BuildResponse(r) + } +} + +// Compliance Section + +// CreateComplianceReport creates an incoming webhook for a channel. +func (c *Client4) CreateComplianceReport(report *Compliance) (*Compliance, *Response) { + if r, err := c.DoApiPost(c.GetComplianceReportsRoute(), report.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ComplianceFromJson(r.Body), BuildResponse(r) + } +} + +// GetComplianceReports returns list of compliance reports. +func (c *Client4) GetComplianceReports(page, perPage int) (Compliances, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetComplianceReportsRoute()+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CompliancesFromJson(r.Body), BuildResponse(r) + } +} + +// GetComplianceReport returns a compliance report. +func (c *Client4) GetComplianceReport(reportId string) (*Compliance, *Response) { + if r, err := c.DoApiGet(c.GetComplianceReportRoute(reportId), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ComplianceFromJson(r.Body), BuildResponse(r) + } +} + +// DownloadComplianceReport returns a full compliance report as a file. +func (c *Client4) DownloadComplianceReport(reportId string) ([]byte, *Response) { + var rq *http.Request + rq, _ = http.NewRequest("GET", c.ApiUrl+c.GetComplianceReportRoute(reportId), nil) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { + return nil, &Response{Error: NewAppError("DownloadComplianceReport", "model.client.connecting.app_error", nil, err.Error(), http.StatusBadRequest)} + } else { + defer closeBody(rp) + + if rp.StatusCode >= 300 { + return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else if data, err := ioutil.ReadAll(rp.Body); err != nil { + return nil, BuildErrorResponse(rp, NewAppError("DownloadComplianceReport", "model.client.read_file.app_error", nil, err.Error(), rp.StatusCode)) + } else { + return data, BuildResponse(rp) + } + } +} + +// Cluster Section + +// GetClusterStatus returns the status of all the configured cluster nodes. +func (c *Client4) GetClusterStatus() ([]*ClusterInfo, *Response) { + if r, err := c.DoApiGet(c.GetClusterRoute()+"/status", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ClusterInfosFromJson(r.Body), BuildResponse(r) + } +} + +// LDAP Section + +// SyncLdap will force a sync with the configured LDAP server. +func (c *Client4) SyncLdap() (bool, *Response) { + if r, err := c.DoApiPost(c.GetLdapRoute()+"/sync", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// TestLdap will attempt to connect to the configured LDAP server and return OK if configured +// correctly. +func (c *Client4) TestLdap() (bool, *Response) { + if r, err := c.DoApiPost(c.GetLdapRoute()+"/test", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// Audits Section + +// GetAudits returns a list of audits for the whole system. +func (c *Client4) GetAudits(page int, perPage int, etag string) (Audits, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet("/audits"+query, etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return AuditsFromJson(r.Body), BuildResponse(r) + } +} + +// Brand Section + +// GetBrandImage retrieves the previously uploaded brand image. +func (c *Client4) GetBrandImage() ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetBrandRoute()+"/image", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + if r.StatusCode >= 300 { + return nil, BuildErrorResponse(r, AppErrorFromJson(r.Body)) + } else if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("GetBrandImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// UploadBrandImage sets the brand image for the system. +func (c *Client4) UploadBrandImage(data []byte) (bool, *Response) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("image", "brand.png"); err != nil { + return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil { + return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.no_file.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + if err := writer.Close(); err != nil { + return false, &Response{Error: NewAppError("UploadBrandImage", "model.client.set_profile_user.writer.app_error", nil, err.Error(), http.StatusBadRequest)} + } + + rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetBrandRoute()+"/image", bytes.NewReader(body.Bytes())) + rq.Header.Set("Content-Type", writer.FormDataContentType()) + rq.Close = true + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil { + return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetBrandRoute()+"/image", "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)} + } else { + defer closeBody(rp) + + if rp.StatusCode >= 300 { + return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body)) + } else { + return CheckStatusOK(rp), BuildResponse(rp) + } + } +} + +// Logs Section + +// GetLogs page of logs as a string array. +func (c *Client4) GetLogs(page, perPage int) ([]string, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet("/logs"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ArrayFromJson(r.Body), BuildResponse(r) + } +} + +// PostLog is a convenience Web Service call so clients can log messages into +// the server-side logs. For example we typically log javascript error messages +// into the server-side. It returns the log message if the logging was successful. +func (c *Client4) PostLog(message map[string]string) (map[string]string, *Response) { + if r, err := c.DoApiPost("/logs", MapToJson(message)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body), BuildResponse(r) + } +} + +// OAuth Section + +// CreateOAuthApp will register a new OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) CreateOAuthApp(app *OAuthApp) (*OAuthApp, *Response) { + if r, err := c.DoApiPost(c.GetOAuthAppsRoute(), app.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// GetOAuthApps gets a page of registered OAuth 2.0 client applications with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) GetOAuthApps(page, perPage int) ([]*OAuthApp, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetOAuthAppsRoute()+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OAuthAppListFromJson(r.Body), BuildResponse(r) + } +} + +// GetOAuthApp gets a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) GetOAuthApp(appId string) (*OAuthApp, *Response) { + if r, err := c.DoApiGet(c.GetOAuthAppRoute(appId), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// GetOAuthAppInfo gets a sanitized version of a registered OAuth 2.0 client application with Mattermost acting as an OAuth 2.0 service provider. +func (c *Client4) GetOAuthAppInfo(appId string) (*OAuthApp, *Response) { + if r, err := c.DoApiGet(c.GetOAuthAppRoute(appId)+"/info", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteOAuthApp deletes a registered OAuth 2.0 client application. +func (c *Client4) DeleteOAuthApp(appId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetOAuthAppRoute(appId)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// RegenerateOAuthAppSecret regenerates the client secret for a registered OAuth 2.0 client application. +func (c *Client4) RegenerateOAuthAppSecret(appId string) (*OAuthApp, *Response) { + if r, err := c.DoApiPost(c.GetOAuthAppRoute(appId)+"/regen_secret", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OAuthAppFromJson(r.Body), BuildResponse(r) + } +} + +// GetAuthorizedOAuthAppsForUser gets a page of OAuth 2.0 client applications the user has authorized to use access their account. +func (c *Client4) GetAuthorizedOAuthAppsForUser(userId string, page, perPage int) ([]*OAuthApp, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetUserRoute(userId)+"/oauth/apps/authorized"+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return OAuthAppListFromJson(r.Body), BuildResponse(r) + } +} + +// AuthorizeOAuthApp will authorize an OAuth 2.0 client application to access a user's account and provide a redirect link to follow. +func (c *Client4) AuthorizeOAuthApp(authRequest *AuthorizeRequest) (string, *Response) { + if r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/authorize", authRequest.ToJson(), ""); err != nil { + return "", BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body)["redirect"], BuildResponse(r) + } +} + +// DeauthorizeOAuthApp will deauthorize an OAuth 2.0 client application from accessing a user's account. +func (c *Client4) DeauthorizeOAuthApp(appId string) (bool, *Response) { + requestData := map[string]string{"client_id": appId} + if r, err := c.DoApiRequest(http.MethodPost, c.Url+"/oauth/deauthorize", MapToJson(requestData), ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// Elasticsearch Section + +// TestElasticsearch will attempt to connect to the configured Elasticsearch server and return OK if configured +// correctly. +func (c *Client4) TestElasticsearch() (bool, *Response) { + if r, err := c.DoApiPost(c.GetElasticsearchRoute()+"/test", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// PurgeElasticsearchIndexes immediately deletes all Elasticsearch indexes. +func (c *Client4) PurgeElasticsearchIndexes() (bool, *Response) { + if r, err := c.DoApiPost(c.GetElasticsearchRoute()+"/test", ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// Commands Section + +// CreateCommand will create a new command if the user have the right permissions. +func (c *Client4) CreateCommand(cmd *Command) (*Command, *Response) { + if r, err := c.DoApiPost(c.GetCommandsRoute(), cmd.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CommandFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateCommand updates a command based on the provided Command struct +func (c *Client4) UpdateCommand(cmd *Command) (*Command, *Response) { + if r, err := c.DoApiPut(c.GetCommandRoute(cmd.Id), cmd.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CommandFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteCommand deletes a command based on the provided command id string +func (c *Client4) DeleteCommand(commandId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetCommandRoute(commandId)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// ListCommands will retrieve a list of commands available in the team. +func (c *Client4) ListCommands(teamId string, customOnly bool) ([]*Command, *Response) { + query := fmt.Sprintf("?team_id=%v&custom_only=%v", teamId, customOnly) + if r, err := c.DoApiGet(c.GetCommandsRoute()+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CommandListFromJson(r.Body), BuildResponse(r) + } +} + +// ExecuteCommand executes a given command. +func (c *Client4) ExecuteCommand(channelId, command string) (*CommandResponse, *Response) { + commandArgs := &CommandArgs{ChannelId: channelId, Command: command} + if r, err := c.DoApiPost(c.GetCommandsRoute()+"/execute", commandArgs.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CommandResponseFromJson(r.Body), BuildResponse(r) + } +} + +// ListCommands will retrieve a list of commands available in the team. +func (c *Client4) ListAutocompleteCommands(teamId string) ([]*Command, *Response) { + if r, err := c.DoApiGet(c.GetTeamAutoCompleteCommandsRoute(teamId), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CommandListFromJson(r.Body), BuildResponse(r) + } +} + +// RegenCommandToken will create a new token if the user have the right permissions. +func (c *Client4) RegenCommandToken(commandId string) (string, *Response) { + if r, err := c.DoApiPut(c.GetCommandRoute(commandId)+"/regen_token", ""); err != nil { + return "", BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body)["token"], BuildResponse(r) + } +} + +// Status Section + +// GetUserStatus returns a user based on the provided user id string. +func (c *Client4) GetUserStatus(userId, etag string) (*Status, *Response) { + if r, err := c.DoApiGet(c.GetUserStatusRoute(userId), etag); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return StatusFromJson(r.Body), BuildResponse(r) + } +} + +// GetUsersStatusesByIds returns a list of users status based on the provided user ids. +func (c *Client4) GetUsersStatusesByIds(userIds []string) ([]*Status, *Response) { + if r, err := c.DoApiPost(c.GetUserStatusesRoute()+"/ids", ArrayToJson(userIds)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return StatusListFromJson(r.Body), BuildResponse(r) + } +} + +// UpdateUserStatus sets a user's status based on the provided user id string. +func (c *Client4) UpdateUserStatus(userId string, userStatus *Status) (*Status, *Response) { + if r, err := c.DoApiPut(c.GetUserStatusRoute(userId), userStatus.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return StatusFromJson(r.Body), BuildResponse(r) + + } +} + +// Webrtc Section + +// GetWebrtcToken returns a valid token, stun server and turn server with credentials to +// use with the Mattermost WebRTC service. +func (c *Client4) GetWebrtcToken() (*WebrtcInfoResponse, *Response) { + if r, err := c.DoApiGet("/webrtc/token", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return WebrtcInfoResponseFromJson(r.Body), BuildResponse(r) + } +} + +// Emoji Section + +// CreateEmoji will save an emoji to the server if the current user has permission +// to do so. If successful, the provided emoji will be returned with its Id field +// filled in. Otherwise, an error will be returned. +func (c *Client4) CreateEmoji(emoji *Emoji, image []byte, filename string) (*Emoji, *Response) { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + if part, err := writer.CreateFormFile("image", filename); err != nil { + return nil, &Response{StatusCode: http.StatusForbidden, Error: NewLocAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error())} + } else if _, err = io.Copy(part, bytes.NewBuffer(image)); err != nil { + return nil, &Response{StatusCode: http.StatusForbidden, Error: NewLocAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error())} + } + + if err := writer.WriteField("emoji", emoji.ToJson()); err != nil { + return nil, &Response{StatusCode: http.StatusForbidden, Error: NewLocAppError("CreateEmoji", "model.client.create_emoji.emoji.app_error", nil, err.Error())} + } + + if err := writer.Close(); err != nil { + return nil, &Response{StatusCode: http.StatusForbidden, Error: NewLocAppError("CreateEmoji", "model.client.create_emoji.writer.app_error", nil, err.Error())} + } + + return c.DoEmojiUploadFile(c.GetEmojisRoute(), body.Bytes(), writer.FormDataContentType()) +} + +// GetEmojiList returns a page of custom emoji on the system. +func (c *Client4) GetEmojiList(page, perPage int) ([]*Emoji, *Response) { + query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage) + if r, err := c.DoApiGet(c.GetEmojisRoute()+query, ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return EmojiListFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteEmoji delete an custom emoji on the provided emoji id string. +func (c *Client4) DeleteEmoji(emojiId string) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetEmojiRoute(emojiId)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// GetEmoji returns a custom emoji in the system on the provided emoji id string. +func (c *Client4) GetEmoji(emojiId string) (*Emoji, *Response) { + if r, err := c.DoApiGet(c.GetEmojiRoute(emojiId), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return EmojiFromJson(r.Body), BuildResponse(r) + } +} + +// GetEmojiImage returns the emoji image. +func (c *Client4) GetEmojiImage(emojiId string) ([]byte, *Response) { + if r, err := c.DoApiGet(c.GetEmojiRoute(emojiId)+"/image", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + + if data, err := ioutil.ReadAll(r.Body); err != nil { + return nil, BuildErrorResponse(r, NewAppError("GetEmojiImage", "model.client.read_file.app_error", nil, err.Error(), r.StatusCode)) + } else { + return data, BuildResponse(r) + } + } +} + +// Reaction Section + +// SaveReaction saves an emoji reaction for a post. Returns the saved reaction if successful, otherwise an error will be returned. +func (c *Client4) SaveReaction(reaction *Reaction) (*Reaction, *Response) { + if r, err := c.DoApiPost(c.GetReactionsRoute(), reaction.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ReactionFromJson(r.Body), BuildResponse(r) + } +} + +// GetReactions returns a list of reactions to a post. +func (c *Client4) GetReactions(postId string) ([]*Reaction, *Response) { + if r, err := c.DoApiGet(c.GetPostRoute(postId)+"/reactions", ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return ReactionsFromJson(r.Body), BuildResponse(r) + } +} + +// DeleteReaction deletes reaction of a user in a post. +func (c *Client4) DeleteReaction(reaction *Reaction) (bool, *Response) { + if r, err := c.DoApiDelete(c.GetUserRoute(reaction.UserId) + c.GetPostRoute(reaction.PostId) + fmt.Sprintf("/reactions/%v", reaction.EmojiName)); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + +// Open Graph Metadata Section + +// OpenGraph return the open graph metadata for a particular url if the site have the metadata +func (c *Client4) OpenGraph(url string) (map[string]string, *Response) { + requestBody := make(map[string]string) + requestBody["url"] = url + + if r, err := c.DoApiPost(c.GetOpenGraphRoute(), MapToJson(requestBody)); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return MapFromJson(r.Body), BuildResponse(r) + } +} + +// Jobs Section + +// GetJob gets a single job. +func (c *Client4) GetJob(id string) (*Job, *Response) { + if r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/%v", id), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return JobFromJson(r.Body), BuildResponse(r) + } +} + +// Get all jobs, sorted with the job that was created most recently first. +func (c *Client4) GetJobs(page int, perPage int) ([]*Job, *Response) { + if r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("?page=%v&per_page=%v", page, perPage), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return JobsFromJson(r.Body), BuildResponse(r) + } +} + +// GetJobsByType gets all jobs of a given type, sorted with the job that was created most recently first. +func (c *Client4) GetJobsByType(jobType string, page int, perPage int) ([]*Job, *Response) { + if r, err := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/type/%v?page=%v&per_page=%v", jobType, page, perPage), ""); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return JobsFromJson(r.Body), BuildResponse(r) + } +} + +// CreateJob creates a job based on the provided job struct. +func (c *Client4) CreateJob(job *Job) (*Job, *Response) { + if r, err := c.DoApiPost(c.GetJobsRoute(), job.ToJson()); err != nil { + return nil, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return JobFromJson(r.Body), BuildResponse(r) + } +} + +// CancelJob requests the cancellation of the job with the provided Id. +func (c *Client4) CancelJob(jobId string) (bool, *Response) { + if r, err := c.DoApiPost(c.GetJobsRoute()+fmt.Sprintf("/%v/cancel", jobId), ""); err != nil { + return false, BuildErrorResponse(r, err) + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} diff --git a/vendor/github.com/mattermost/platform/model/cluster_discovery.go b/vendor/github.com/mattermost/platform/model/cluster_discovery.go new file mode 100644 index 00000000..4b926965 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/cluster_discovery.go @@ -0,0 +1,132 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "os" +) + +const ( + CDS_OFFLINE_AFTER_MILLIS = 1000 * 60 * 30 // 30 minutes + CDS_TYPE_APP = "mattermost_app" +) + +type ClusterDiscovery struct { + Id string `json:"id"` + Type string `json:"type"` + ClusterName string `json:"cluster_name"` + Hostname string `json:"hostname"` + GossipPort int32 `json:"gossip_port"` + Port int32 `json:"port"` + CreateAt int64 `json:"create_at"` + LastPingAt int64 `json:"last_ping_at"` +} + +func (o *ClusterDiscovery) PreSave() { + if o.Id == "" { + o.Id = NewId() + } + + if o.CreateAt == 0 { + o.CreateAt = GetMillis() + o.LastPingAt = o.CreateAt + } +} + +func (o *ClusterDiscovery) AutoFillHostname() { + // attempt to set the hostname from the OS + if len(o.Hostname) == 0 { + if hn, err := os.Hostname(); err == nil { + o.Hostname = hn + } + } +} + +func (o *ClusterDiscovery) AutoFillIpAddress() { + // attempt to set the hostname to the first non-local IP address + if len(o.Hostname) == 0 { + o.Hostname = GetServerIpAddress() + } +} + +func (o *ClusterDiscovery) IsEqual(in *ClusterDiscovery) bool { + if in == nil { + return false + } + + if o.Type != in.Type { + return false + } + + if o.ClusterName != in.ClusterName { + return false + } + + if o.Hostname != in.Hostname { + return false + } + + return true +} + +func FilterClusterDiscovery(vs []*ClusterDiscovery, f func(*ClusterDiscovery) bool) []*ClusterDiscovery { + copy := make([]*ClusterDiscovery, 0) + for _, v := range vs { + if f(v) { + copy = append(copy, v) + } + } + + return copy +} + +func (o *ClusterDiscovery) IsValid() *AppError { + if len(o.Id) != 26 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.id.app_error", nil, "") + } + + if len(o.ClusterName) == 0 { + return NewLocAppError("ClusterDiscovery.IsValid", "ClusterName must be set", nil, "") + } + + if len(o.Type) == 0 { + return NewLocAppError("ClusterDiscovery.IsValid", "Type must be set", nil, "") + } + + if len(o.Hostname) == 0 { + return NewLocAppError("ClusterDiscovery.IsValid", "Hostname must be set", nil, "") + } + + if o.CreateAt == 0 { + return NewLocAppError("ClusterDiscovery.IsValid", "CreateAt must be set", nil, "") + } + + if o.LastPingAt == 0 { + return NewLocAppError("ClusterDiscovery.IsValid", "LastPingAt must be set", nil, "") + } + + return nil +} + +func (o *ClusterDiscovery) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } + + return string(b) +} + +func ClusterDiscoveryFromJson(data io.Reader) *ClusterDiscovery { + decoder := json.NewDecoder(data) + var me ClusterDiscovery + err := decoder.Decode(&me) + if err == nil { + return &me + } + + return nil +} diff --git a/vendor/github.com/mattermost/platform/model/cluster_info.go b/vendor/github.com/mattermost/platform/model/cluster_info.go index 7c3384ae..1e468044 100644 --- a/vendor/github.com/mattermost/platform/model/cluster_info.go +++ b/vendor/github.com/mattermost/platform/model/cluster_info.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -6,16 +6,14 @@ package model import ( "encoding/json" "io" + "strings" ) type ClusterInfo struct { - Id string `json:"id"` - Version string `json:"version"` - ConfigHash string `json:"config_hash"` - InterNodeUrl string `json:"internode_url"` - Hostname string `json:"hostname"` - LastSuccessfulPing int64 `json:"last_ping"` - IsAlive bool `json:"is_alive"` + Version string `json:"version"` + ConfigHash string `json:"config_hash"` + IpAddress string `json:"ipaddress"` + Hostname string `json:"hostname"` } func (me *ClusterInfo) ToJson() string { @@ -27,6 +25,11 @@ func (me *ClusterInfo) ToJson() string { } } +func (me *ClusterInfo) Copy() *ClusterInfo { + json := me.ToJson() + return ClusterInfoFromJson(strings.NewReader(json)) +} + func ClusterInfoFromJson(data io.Reader) *ClusterInfo { decoder := json.NewDecoder(data) var me ClusterInfo @@ -38,14 +41,6 @@ func ClusterInfoFromJson(data io.Reader) *ClusterInfo { } } -func (me *ClusterInfo) HaveEstablishedInitialContact() bool { - if me.Id != "" { - return true - } - - return false -} - func ClusterInfosToJson(objmap []*ClusterInfo) string { if b, err := json.Marshal(objmap); err != nil { return "" diff --git a/vendor/github.com/mattermost/platform/model/cluster_message.go b/vendor/github.com/mattermost/platform/model/cluster_message.go new file mode 100644 index 00000000..a6dec2e7 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/cluster_message.go @@ -0,0 +1,55 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + CLUSTER_EVENT_PUBLISH = "publish" + CLUSTER_EVENT_UPDATE_STATUS = "update_status" + CLUSTER_EVENT_INVALIDATE_ALL_CACHES = "inv_all_caches" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS = "inv_reactions" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_WEBHOOK = "inv_webhook" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_POSTS = "inv_channel_posts" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBERS_NOTIFY_PROPS = "inv_channel_members_notify_props" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBERS = "inv_channel_members" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_BY_NAME = "inv_channel_name" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL = "inv_channel" + CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER = "inv_user" + CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user" + + CLUSTER_SEND_BEST_EFFORT = "best_effort" + CLUSTER_SEND_RELIABLE = "reliable" +) + +type ClusterMessage struct { + Event string `json:"event"` + SendType string `json:"-"` + WaitForAllToSend bool `json:"-"` + Data string `json:"data,omitempty"` + Props map[string]string `json:"props,omitempty"` +} + +func (o *ClusterMessage) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func ClusterMessageFromJson(data io.Reader) *ClusterMessage { + decoder := json.NewDecoder(data) + var o ClusterMessage + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/cluster_stats.go b/vendor/github.com/mattermost/platform/model/cluster_stats.go index f2efa323..879487c6 100644 --- a/vendor/github.com/mattermost/platform/model/cluster_stats.go +++ b/vendor/github.com/mattermost/platform/model/cluster_stats.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/command.go b/vendor/github.com/mattermost/platform/model/command.go index decb647b..47378bbe 100644 --- a/vendor/github.com/mattermost/platform/model/command.go +++ b/vendor/github.com/mattermost/platform/model/command.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/command_args.go b/vendor/github.com/mattermost/platform/model/command_args.go index 4da5dc76..76b03a2f 100644 --- a/vendor/github.com/mattermost/platform/model/command_args.go +++ b/vendor/github.com/mattermost/platform/model/command_args.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -6,13 +6,20 @@ package model import ( "encoding/json" "io" + + goi18n "github.com/nicksnyder/go-i18n/i18n" ) type CommandArgs struct { - ChannelId string `json:"channel_id"` - RootId string `json:"root_id"` - ParentId string `json:"parent_id"` - Command string `json:"command"` + UserId string `json:"user_id"` + ChannelId string `json:"channel_id"` + TeamId string `json:"team_id"` + RootId string `json:"root_id"` + ParentId string `json:"parent_id"` + Command string `json:"command"` + SiteURL string `json:"-"` + T goi18n.TranslateFunc `json:"-"` + Session Session `json:"-"` } func (o *CommandArgs) ToJson() string { diff --git a/vendor/github.com/mattermost/platform/model/command_response.go b/vendor/github.com/mattermost/platform/model/command_response.go index f6977235..27d39e17 100644 --- a/vendor/github.com/mattermost/platform/model/command_response.go +++ b/vendor/github.com/mattermost/platform/model/command_response.go @@ -1,11 +1,10 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model import ( "encoding/json" - "fmt" "io" ) @@ -40,14 +39,8 @@ func CommandResponseFromJson(data io.Reader) *CommandResponse { return nil } - // Ensure attachment fields are stored as strings - for _, attachment := range o.Attachments { - for _, field := range attachment.Fields { - if field.Value != nil { - field.Value = fmt.Sprintf("%v", field.Value) - } - } - } + o.Text = ExpandAnnouncement(o.Text) + o.Attachments = ProcessSlackAttachments(o.Attachments) return &o } diff --git a/vendor/github.com/mattermost/platform/model/compliance.go b/vendor/github.com/mattermost/platform/model/compliance.go index 4a96a597..14f8f4a5 100644 --- a/vendor/github.com/mattermost/platform/model/compliance.go +++ b/vendor/github.com/mattermost/platform/model/compliance.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/compliance_post.go b/vendor/github.com/mattermost/platform/model/compliance_post.go index 027e534b..3751c586 100644 --- a/vendor/github.com/mattermost/platform/model/compliance_post.go +++ b/vendor/github.com/mattermost/platform/model/compliance_post.go @@ -1,9 +1,10 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model import ( + "regexp" "time" ) @@ -64,6 +65,15 @@ func CompliancePostHeader() []string { } } +func cleanComplianceStrings(in string) string { + if matched, _ := regexp.MatchString("^\\s*(=|\\+|\\-)", in); matched { + return "'" + in + + } else { + return in + } +} + func (me *CompliancePost) Row() []string { postDeleteAt := "" @@ -77,15 +87,15 @@ func (me *CompliancePost) Row() []string { } return []string{ - me.TeamName, - me.TeamDisplayName, + cleanComplianceStrings(me.TeamName), + cleanComplianceStrings(me.TeamDisplayName), - me.ChannelName, - me.ChannelDisplayName, + cleanComplianceStrings(me.ChannelName), + cleanComplianceStrings(me.ChannelDisplayName), - me.UserUsername, - me.UserEmail, - me.UserNickname, + cleanComplianceStrings(me.UserUsername), + cleanComplianceStrings(me.UserEmail), + cleanComplianceStrings(me.UserNickname), me.PostId, time.Unix(0, me.PostCreateAt*int64(1000*1000)).Format(time.RFC3339), @@ -95,7 +105,7 @@ func (me *CompliancePost) Row() []string { me.PostRootId, me.PostParentId, me.PostOriginalId, - me.PostMessage, + cleanComplianceStrings(me.PostMessage), me.PostType, me.PostProps, me.PostHashtags, diff --git a/vendor/github.com/mattermost/platform/model/config.go b/vendor/github.com/mattermost/platform/model/config.go index 86693b57..1717d61a 100644 --- a/vendor/github.com/mattermost/platform/model/config.go +++ b/vendor/github.com/mattermost/platform/model/config.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -6,6 +6,7 @@ package model import ( "encoding/json" "io" + "net/http" "net/url" ) @@ -32,12 +33,17 @@ const ( WEBSERVER_MODE_GZIP = "gzip" WEBSERVER_MODE_DISABLED = "disabled" - GENERIC_NOTIFICATION = "generic" - FULL_NOTIFICATION = "full" + GENERIC_NO_CHANNEL_NOTIFICATION = "generic_no_channel" + GENERIC_NOTIFICATION = "generic" + FULL_NOTIFICATION = "full" DIRECT_MESSAGE_ANY = "any" DIRECT_MESSAGE_TEAM = "team" + SHOW_USERNAME = "username" + SHOW_NICKNAME_FULLNAME = "nickname_full_name" + SHOW_FULLNAME = "full_name" + PERMISSIONS_ALL = "all" PERMISSIONS_CHANNEL_ADMIN = "channel_admin" PERMISSIONS_TEAM_ADMIN = "team_admin" @@ -60,6 +66,9 @@ const ( EMAIL_BATCHING_BUFFER_SIZE = 256 EMAIL_BATCHING_INTERVAL = 30 + EMAIL_NOTIFICATION_CONTENTS_FULL = "full" + EMAIL_NOTIFICATION_CONTENTS_GENERIC = "generic" + SITENAME_MAX_LENGTH = 30 SERVICE_SETTINGS_DEFAULT_SITE_URL = "" @@ -75,12 +84,15 @@ const ( EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = "" - SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/" - SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/" - SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/" - SUPPORT_SETTINGS_DEFAULT_HELP_LINK = "https://about.mattermost.com/default-help/" - SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/" - SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com" + SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/" + SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/" + SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/" + SUPPORT_SETTINGS_DEFAULT_HELP_LINK = "https://about.mattermost.com/default-help/" + SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/" + SUPPORT_SETTINGS_DEFAULT_ADMINISTRATORS_GUIDE_LINK = "https://about.mattermost.com/administrators-guide/" + SUPPORT_SETTINGS_DEFAULT_TROUBLESHOOTING_FORUM_LINK = "https://about.mattermost.com/troubleshooting-forum/" + SUPPORT_SETTINGS_DEFAULT_COMMERCIAL_SUPPORT_LINK = "https://about.mattermost.com/commercial-support/" + SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com" LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = "" LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = "" @@ -107,10 +119,20 @@ const ( WEBRTC_SETTINGS_DEFAULT_TURN_URI = "" ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS = 2500 + + ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_COLOR = "#f2a93b" + ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_TEXT_COLOR = "#333333" + + ELASTICSEARCH_SETTINGS_DEFAULT_CONNECTION_URL = "" + ELASTICSEARCH_SETTINGS_DEFAULT_USERNAME = "" + ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD = "" + ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_REPLICAS = 1 + ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_SHARDS = 1 ) type ServiceSettings struct { SiteURL *string + LicenseFileLocation *string ListenAddress string ConnectionSecurity *string TLSCertFile *string @@ -121,6 +143,7 @@ type ServiceSettings struct { ReadTimeout *int WriteTimeout *int MaximumLoginAttempts int + GoroutineHealthThreshold *int GoogleDeveloperKey string EnableOAuthServiceProvider bool EnableIncomingWebhooks bool @@ -129,6 +152,7 @@ type ServiceSettings struct { EnableOnlyAdminIntegrations *bool EnablePostUsernameOverride bool EnablePostIconOverride bool + EnableAPIv3 *bool EnableLinkPreviews *bool EnableTesting bool EnableDeveloper *bool @@ -136,6 +160,7 @@ type ServiceSettings struct { EnableInsecureOutgoingConnections *bool EnableMultifactorAuthentication *bool EnforceMultifactorAuthentication *bool + EnableUserAccessTokens *bool AllowCorsFrom *string SessionLengthWebInDays *int SessionLengthMobileInDays *int @@ -145,19 +170,28 @@ type ServiceSettings struct { WebsocketPort *int WebserverMode *string EnableCustomEmoji *bool + EnableEmojiPicker *bool RestrictCustomEmojiCreation *string RestrictPostDelete *string AllowEditPost *string PostEditTimeLimit *int TimeBetweenUserTypingUpdatesMilliseconds *int64 + EnablePostSearch *bool EnableUserTypingMessages *bool + EnableChannelViewedMessages *bool + EnableUserStatuses *bool ClusterLogTimeoutMilliseconds *int } type ClusterSettings struct { - Enable *bool - InterNodeListenAddress *string - InterNodeUrls []string + Enable *bool + ClusterName *string + OverrideHostname *string + UseIpAddress *bool + UseExperimentalGossip *bool + ReadOnlyConfig *bool + GossipPort *int + StreamingPort *int } type MetricsSettings struct { @@ -181,13 +215,15 @@ type SSOSettings struct { } type SqlSettings struct { - DriverName string - DataSource string - DataSourceReplicas []string - MaxIdleConns int - MaxOpenConns int - Trace bool - AtRestEncryptKey string + DriverName string + DataSource string + DataSourceReplicas []string + DataSourceSearchReplicas []string + MaxIdleConns int + MaxOpenConns int + Trace bool + AtRestEncryptKey string + QueryTimeout *int } type LogSettings struct { @@ -210,17 +246,14 @@ type PasswordSettings struct { } type FileSettings struct { + EnableFileAttachments *bool + EnableMobileUpload *bool + EnableMobileDownload *bool MaxFileSize *int64 DriverName string Directory string EnablePublicLink bool PublicLinkSalt *string - ThumbnailWidth int - ThumbnailHeight int - PreviewWidth int - PreviewHeight int - ProfileWidth int - ProfileHeight int InitialFont string AmazonS3AccessKeyId string AmazonS3SecretAccessKey string @@ -228,30 +261,34 @@ type FileSettings struct { AmazonS3Region string AmazonS3Endpoint string AmazonS3SSL *bool + AmazonS3SignV2 *bool + AmazonS3SSE *bool } type EmailSettings struct { - EnableSignUpWithEmail bool - EnableSignInWithEmail *bool - EnableSignInWithUsername *bool - SendEmailNotifications bool - RequireEmailVerification bool - FeedbackName string - FeedbackEmail string - FeedbackOrganization *string - SMTPUsername string - SMTPPassword string - SMTPServer string - SMTPPort string - ConnectionSecurity string - InviteSalt string - PasswordResetSalt string - SendPushNotifications *bool - PushNotificationServer *string - PushNotificationContents *string - EnableEmailBatching *bool - EmailBatchingBufferSize *int - EmailBatchingInterval *int + EnableSignUpWithEmail bool + EnableSignInWithEmail *bool + EnableSignInWithUsername *bool + SendEmailNotifications bool + RequireEmailVerification bool + FeedbackName string + FeedbackEmail string + FeedbackOrganization *string + EnableSMTPAuth *bool + SMTPUsername string + SMTPPassword string + SMTPServer string + SMTPPort string + ConnectionSecurity string + InviteSalt string + SendPushNotifications *bool + PushNotificationServer *string + PushNotificationContents *string + EnableEmailBatching *bool + EmailBatchingBufferSize *int + EmailBatchingInterval *int + SkipServerCertificateVerification *bool + EmailNotificationContentsType *string } type RateLimitSettings struct { @@ -269,35 +306,48 @@ type PrivacySettings struct { } type SupportSettings struct { - TermsOfServiceLink *string - PrivacyPolicyLink *string - AboutLink *string - HelpLink *string - ReportAProblemLink *string - SupportEmail *string + TermsOfServiceLink *string + PrivacyPolicyLink *string + AboutLink *string + HelpLink *string + ReportAProblemLink *string + AdministratorsGuideLink *string + TroubleshootingForumLink *string + CommercialSupportLink *string + SupportEmail *string +} + +type AnnouncementSettings struct { + EnableBanner *bool + BannerText *string + BannerColor *string + BannerTextColor *string + AllowBannerDismissal *bool } type TeamSettings struct { - SiteName string - MaxUsersPerTeam int - EnableTeamCreation bool - EnableUserCreation bool - EnableOpenServer *bool - RestrictCreationToDomains string - EnableCustomBrand *bool - CustomBrandText *string - CustomDescriptionText *string - RestrictDirectMessage *string - RestrictTeamInvite *string - RestrictPublicChannelManagement *string - RestrictPrivateChannelManagement *string - RestrictPublicChannelCreation *string - RestrictPrivateChannelCreation *string - RestrictPublicChannelDeletion *string - RestrictPrivateChannelDeletion *string - UserStatusAwayTimeout *int64 - MaxChannelsPerTeam *int64 - MaxNotificationsPerChannel *int64 + SiteName string + MaxUsersPerTeam int + EnableTeamCreation bool + EnableUserCreation bool + EnableOpenServer *bool + RestrictCreationToDomains string + EnableCustomBrand *bool + CustomBrandText *string + CustomDescriptionText *string + RestrictDirectMessage *string + RestrictTeamInvite *string + RestrictPublicChannelManagement *string + RestrictPrivateChannelManagement *string + RestrictPublicChannelCreation *string + RestrictPrivateChannelCreation *string + RestrictPublicChannelDeletion *string + RestrictPrivateChannelDeletion *string + RestrictPrivateChannelManageMembers *string + UserStatusAwayTimeout *int64 + MaxChannelsPerTeam *int64 + MaxNotificationsPerChannel *int64 + TeammateNameDisplay *string } type LdapSettings struct { @@ -389,29 +439,58 @@ type WebrtcSettings struct { TurnSharedKey *string } +type ElasticsearchSettings struct { + ConnectionUrl *string + Username *string + Password *string + EnableIndexing *bool + EnableSearching *bool + Sniff *bool + PostIndexReplicas *int + PostIndexShards *int +} + +type DataRetentionSettings struct { + Enable *bool +} + +type JobSettings struct { + RunJobs *bool + RunScheduler *bool +} + +type PluginSettings struct { + Plugins map[string]interface{} +} + type Config struct { - ServiceSettings ServiceSettings - TeamSettings TeamSettings - SqlSettings SqlSettings - LogSettings LogSettings - PasswordSettings PasswordSettings - FileSettings FileSettings - EmailSettings EmailSettings - RateLimitSettings RateLimitSettings - PrivacySettings PrivacySettings - SupportSettings SupportSettings - GitLabSettings SSOSettings - GoogleSettings SSOSettings - Office365Settings SSOSettings - LdapSettings LdapSettings - ComplianceSettings ComplianceSettings - LocalizationSettings LocalizationSettings - SamlSettings SamlSettings - NativeAppSettings NativeAppSettings - ClusterSettings ClusterSettings - MetricsSettings MetricsSettings - AnalyticsSettings AnalyticsSettings - WebrtcSettings WebrtcSettings + ServiceSettings ServiceSettings + TeamSettings TeamSettings + SqlSettings SqlSettings + LogSettings LogSettings + PasswordSettings PasswordSettings + FileSettings FileSettings + EmailSettings EmailSettings + RateLimitSettings RateLimitSettings + PrivacySettings PrivacySettings + SupportSettings SupportSettings + AnnouncementSettings AnnouncementSettings + GitLabSettings SSOSettings + GoogleSettings SSOSettings + Office365Settings SSOSettings + LdapSettings LdapSettings + ComplianceSettings ComplianceSettings + LocalizationSettings LocalizationSettings + SamlSettings SamlSettings + NativeAppSettings NativeAppSettings + ClusterSettings ClusterSettings + MetricsSettings MetricsSettings + AnalyticsSettings AnalyticsSettings + WebrtcSettings WebrtcSettings + ElasticsearchSettings ElasticsearchSettings + DataRetentionSettings DataRetentionSettings + JobSettings JobSettings + PluginSettings PluginSettings } func (o *Config) ToJson() string { @@ -453,27 +532,52 @@ func (o *Config) SetDefaults() { o.SqlSettings.AtRestEncryptKey = NewRandomString(32) } + if o.SqlSettings.QueryTimeout == nil { + o.SqlSettings.QueryTimeout = new(int) + *o.SqlSettings.QueryTimeout = 30 + } + if o.FileSettings.AmazonS3Endpoint == "" { // Defaults to "s3.amazonaws.com" o.FileSettings.AmazonS3Endpoint = "s3.amazonaws.com" } - if o.FileSettings.AmazonS3Region == "" { - // Defaults to "us-east-1" region. - o.FileSettings.AmazonS3Region = "us-east-1" - } - if o.FileSettings.AmazonS3SSL == nil { o.FileSettings.AmazonS3SSL = new(bool) *o.FileSettings.AmazonS3SSL = true // Secure by default. } + if o.FileSettings.AmazonS3SignV2 == nil { + o.FileSettings.AmazonS3SignV2 = new(bool) + // Signature v2 is not enabled by default. + } + + if o.FileSettings.AmazonS3SSE == nil { + o.FileSettings.AmazonS3SSE = new(bool) + *o.FileSettings.AmazonS3SSE = false // Not Encrypted by default. + } + + if o.FileSettings.EnableFileAttachments == nil { + o.FileSettings.EnableFileAttachments = new(bool) + *o.FileSettings.EnableFileAttachments = true + } + + if o.FileSettings.EnableMobileUpload == nil { + o.FileSettings.EnableMobileUpload = new(bool) + *o.FileSettings.EnableMobileUpload = true + } + + if o.FileSettings.EnableMobileDownload == nil { + o.FileSettings.EnableMobileDownload = new(bool) + *o.FileSettings.EnableMobileDownload = true + } + if o.FileSettings.MaxFileSize == nil { o.FileSettings.MaxFileSize = new(int64) *o.FileSettings.MaxFileSize = 52428800 // 50 MB } - if len(*o.FileSettings.PublicLinkSalt) == 0 { + if o.FileSettings.PublicLinkSalt == nil || len(*o.FileSettings.PublicLinkSalt) == 0 { o.FileSettings.PublicLinkSalt = new(string) *o.FileSettings.PublicLinkSalt = NewRandomString(32) } @@ -483,12 +587,12 @@ func (o *Config) SetDefaults() { o.FileSettings.InitialFont = "luximbi.ttf" } - if len(o.EmailSettings.InviteSalt) == 0 { - o.EmailSettings.InviteSalt = NewRandomString(32) + if o.FileSettings.Directory == "" { + o.FileSettings.Directory = "./data/" } - if len(o.EmailSettings.PasswordResetSalt) == 0 { - o.EmailSettings.PasswordResetSalt = NewRandomString(32) + if len(o.EmailSettings.InviteSalt) == 0 { + o.EmailSettings.InviteSalt = NewRandomString(32) } if o.ServiceSettings.SiteURL == nil { @@ -496,6 +600,15 @@ func (o *Config) SetDefaults() { *o.ServiceSettings.SiteURL = SERVICE_SETTINGS_DEFAULT_SITE_URL } + if o.ServiceSettings.LicenseFileLocation == nil { + o.ServiceSettings.LicenseFileLocation = new(string) + } + + if o.ServiceSettings.EnableAPIv3 == nil { + o.ServiceSettings.EnableAPIv3 = new(bool) + *o.ServiceSettings.EnableAPIv3 = true + } + if o.ServiceSettings.EnableLinkPreviews == nil { o.ServiceSettings.EnableLinkPreviews = new(bool) *o.ServiceSettings.EnableLinkPreviews = false @@ -526,6 +639,11 @@ func (o *Config) SetDefaults() { *o.ServiceSettings.EnforceMultifactorAuthentication = false } + if o.ServiceSettings.EnableUserAccessTokens == nil { + o.ServiceSettings.EnableUserAccessTokens = new(bool) + *o.ServiceSettings.EnableUserAccessTokens = false + } + if o.PasswordSettings.MinimumLength == nil { o.PasswordSettings.MinimumLength = new(int) *o.PasswordSettings.MinimumLength = PASSWORD_MINIMUM_LENGTH @@ -594,13 +712,21 @@ func (o *Config) SetDefaults() { if o.TeamSettings.RestrictPublicChannelCreation == nil { o.TeamSettings.RestrictPublicChannelCreation = new(string) // If this setting does not exist, assume migration from <3.6, so use management setting as default. - *o.TeamSettings.RestrictPublicChannelCreation = *o.TeamSettings.RestrictPublicChannelManagement + if *o.TeamSettings.RestrictPublicChannelManagement == PERMISSIONS_CHANNEL_ADMIN { + *o.TeamSettings.RestrictPublicChannelCreation = PERMISSIONS_TEAM_ADMIN + } else { + *o.TeamSettings.RestrictPublicChannelCreation = *o.TeamSettings.RestrictPublicChannelManagement + } } if o.TeamSettings.RestrictPrivateChannelCreation == nil { o.TeamSettings.RestrictPrivateChannelCreation = new(string) // If this setting does not exist, assume migration from <3.6, so use management setting as default. - *o.TeamSettings.RestrictPrivateChannelCreation = *o.TeamSettings.RestrictPrivateChannelManagement + if *o.TeamSettings.RestrictPrivateChannelManagement == PERMISSIONS_CHANNEL_ADMIN { + *o.TeamSettings.RestrictPrivateChannelCreation = PERMISSIONS_TEAM_ADMIN + } else { + *o.TeamSettings.RestrictPrivateChannelCreation = *o.TeamSettings.RestrictPrivateChannelManagement + } } if o.TeamSettings.RestrictPublicChannelDeletion == nil { @@ -615,6 +741,11 @@ func (o *Config) SetDefaults() { *o.TeamSettings.RestrictPrivateChannelDeletion = *o.TeamSettings.RestrictPrivateChannelManagement } + if o.TeamSettings.RestrictPrivateChannelManageMembers == nil { + o.TeamSettings.RestrictPrivateChannelManageMembers = new(string) + *o.TeamSettings.RestrictPrivateChannelManageMembers = PERMISSIONS_ALL + } + if o.TeamSettings.UserStatusAwayTimeout == nil { o.TeamSettings.UserStatusAwayTimeout = new(int64) *o.TeamSettings.UserStatusAwayTimeout = TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT @@ -680,8 +811,31 @@ func (o *Config) SetDefaults() { *o.EmailSettings.EmailBatchingInterval = EMAIL_BATCHING_INTERVAL } + if o.EmailSettings.EnableSMTPAuth == nil { + o.EmailSettings.EnableSMTPAuth = new(bool) + if o.EmailSettings.ConnectionSecurity == CONN_SECURITY_NONE { + *o.EmailSettings.EnableSMTPAuth = false + } else { + *o.EmailSettings.EnableSMTPAuth = true + } + } + + if o.EmailSettings.ConnectionSecurity == CONN_SECURITY_PLAIN { + o.EmailSettings.ConnectionSecurity = CONN_SECURITY_NONE + } + + if o.EmailSettings.SkipServerCertificateVerification == nil { + o.EmailSettings.SkipServerCertificateVerification = new(bool) + *o.EmailSettings.SkipServerCertificateVerification = false + } + + if o.EmailSettings.EmailNotificationContentsType == nil { + o.EmailSettings.EmailNotificationContentsType = new(string) + *o.EmailSettings.EmailNotificationContentsType = EMAIL_NOTIFICATION_CONTENTS_FULL + } + if !IsSafeLink(o.SupportSettings.TermsOfServiceLink) { - o.SupportSettings.TermsOfServiceLink = nil + *o.SupportSettings.TermsOfServiceLink = SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK } if o.SupportSettings.TermsOfServiceLink == nil { @@ -690,7 +844,7 @@ func (o *Config) SetDefaults() { } if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) { - o.SupportSettings.PrivacyPolicyLink = nil + *o.SupportSettings.PrivacyPolicyLink = "" } if o.SupportSettings.PrivacyPolicyLink == nil { @@ -699,7 +853,7 @@ func (o *Config) SetDefaults() { } if !IsSafeLink(o.SupportSettings.AboutLink) { - o.SupportSettings.AboutLink = nil + *o.SupportSettings.AboutLink = "" } if o.SupportSettings.AboutLink == nil { @@ -708,7 +862,7 @@ func (o *Config) SetDefaults() { } if !IsSafeLink(o.SupportSettings.HelpLink) { - o.SupportSettings.HelpLink = nil + *o.SupportSettings.HelpLink = "" } if o.SupportSettings.HelpLink == nil { @@ -717,7 +871,7 @@ func (o *Config) SetDefaults() { } if !IsSafeLink(o.SupportSettings.ReportAProblemLink) { - o.SupportSettings.ReportAProblemLink = nil + *o.SupportSettings.ReportAProblemLink = "" } if o.SupportSettings.ReportAProblemLink == nil { @@ -725,11 +879,63 @@ func (o *Config) SetDefaults() { *o.SupportSettings.ReportAProblemLink = SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK } + if !IsSafeLink(o.SupportSettings.AdministratorsGuideLink) { + *o.SupportSettings.AdministratorsGuideLink = "" + } + + if o.SupportSettings.AdministratorsGuideLink == nil { + o.SupportSettings.AdministratorsGuideLink = new(string) + *o.SupportSettings.AdministratorsGuideLink = SUPPORT_SETTINGS_DEFAULT_ADMINISTRATORS_GUIDE_LINK + } + + if !IsSafeLink(o.SupportSettings.TroubleshootingForumLink) { + *o.SupportSettings.TroubleshootingForumLink = "" + } + + if o.SupportSettings.TroubleshootingForumLink == nil { + o.SupportSettings.TroubleshootingForumLink = new(string) + *o.SupportSettings.TroubleshootingForumLink = SUPPORT_SETTINGS_DEFAULT_TROUBLESHOOTING_FORUM_LINK + } + + if !IsSafeLink(o.SupportSettings.CommercialSupportLink) { + *o.SupportSettings.CommercialSupportLink = "" + } + + if o.SupportSettings.CommercialSupportLink == nil { + o.SupportSettings.CommercialSupportLink = new(string) + *o.SupportSettings.CommercialSupportLink = SUPPORT_SETTINGS_DEFAULT_COMMERCIAL_SUPPORT_LINK + } + if o.SupportSettings.SupportEmail == nil { o.SupportSettings.SupportEmail = new(string) *o.SupportSettings.SupportEmail = SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL } + if o.AnnouncementSettings.EnableBanner == nil { + o.AnnouncementSettings.EnableBanner = new(bool) + *o.AnnouncementSettings.EnableBanner = false + } + + if o.AnnouncementSettings.BannerText == nil { + o.AnnouncementSettings.BannerText = new(string) + *o.AnnouncementSettings.BannerText = "" + } + + if o.AnnouncementSettings.BannerColor == nil { + o.AnnouncementSettings.BannerColor = new(string) + *o.AnnouncementSettings.BannerColor = ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_COLOR + } + + if o.AnnouncementSettings.BannerTextColor == nil { + o.AnnouncementSettings.BannerTextColor = new(string) + *o.AnnouncementSettings.BannerTextColor = ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_TEXT_COLOR + } + + if o.AnnouncementSettings.AllowBannerDismissal == nil { + o.AnnouncementSettings.AllowBannerDismissal = new(bool) + *o.AnnouncementSettings.AllowBannerDismissal = true + } + if o.LdapSettings.Enable == nil { o.LdapSettings.Enable = new(bool) *o.LdapSettings.Enable = false @@ -884,7 +1090,12 @@ func (o *Config) SetDefaults() { if o.ServiceSettings.EnableCustomEmoji == nil { o.ServiceSettings.EnableCustomEmoji = new(bool) - *o.ServiceSettings.EnableCustomEmoji = true + *o.ServiceSettings.EnableCustomEmoji = false + } + + if o.ServiceSettings.EnableEmojiPicker == nil { + o.ServiceSettings.EnableEmojiPicker = new(bool) + *o.ServiceSettings.EnableEmojiPicker = true } if o.ServiceSettings.RestrictCustomEmojiCreation == nil { @@ -907,18 +1118,44 @@ func (o *Config) SetDefaults() { *o.ServiceSettings.PostEditTimeLimit = 300 } - if o.ClusterSettings.InterNodeListenAddress == nil { - o.ClusterSettings.InterNodeListenAddress = new(string) - *o.ClusterSettings.InterNodeListenAddress = ":8075" - } - if o.ClusterSettings.Enable == nil { o.ClusterSettings.Enable = new(bool) *o.ClusterSettings.Enable = false } - if o.ClusterSettings.InterNodeUrls == nil { - o.ClusterSettings.InterNodeUrls = []string{} + if o.ClusterSettings.ClusterName == nil { + o.ClusterSettings.ClusterName = new(string) + *o.ClusterSettings.ClusterName = "" + } + + if o.ClusterSettings.OverrideHostname == nil { + o.ClusterSettings.OverrideHostname = new(string) + *o.ClusterSettings.OverrideHostname = "" + } + + if o.ClusterSettings.UseIpAddress == nil { + o.ClusterSettings.UseIpAddress = new(bool) + *o.ClusterSettings.UseIpAddress = true + } + + if o.ClusterSettings.UseExperimentalGossip == nil { + o.ClusterSettings.UseExperimentalGossip = new(bool) + *o.ClusterSettings.UseExperimentalGossip = false + } + + if o.ClusterSettings.ReadOnlyConfig == nil { + o.ClusterSettings.ReadOnlyConfig = new(bool) + *o.ClusterSettings.ReadOnlyConfig = true + } + + if o.ClusterSettings.GossipPort == nil { + o.ClusterSettings.GossipPort = new(int) + *o.ClusterSettings.GossipPort = 8074 + } + + if o.ClusterSettings.StreamingPort == nil { + o.ClusterSettings.StreamingPort = new(int) + *o.ClusterSettings.StreamingPort = 8075 } if o.MetricsSettings.ListenAddress == nil { @@ -978,12 +1215,12 @@ func (o *Config) SetDefaults() { if o.SamlSettings.Verify == nil { o.SamlSettings.Verify = new(bool) - *o.SamlSettings.Verify = false + *o.SamlSettings.Verify = true } if o.SamlSettings.Encrypt == nil { o.SamlSettings.Encrypt = new(bool) - *o.SamlSettings.Encrypt = false + *o.SamlSettings.Encrypt = true } if o.SamlSettings.IdpUrl == nil { @@ -1056,6 +1293,15 @@ func (o *Config) SetDefaults() { *o.SamlSettings.LocaleAttribute = SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE } + if o.TeamSettings.TeammateNameDisplay == nil { + o.TeamSettings.TeammateNameDisplay = new(string) + *o.TeamSettings.TeammateNameDisplay = SHOW_USERNAME + + if *o.SamlSettings.Enable || *o.LdapSettings.Enable { + *o.TeamSettings.TeammateNameDisplay = SHOW_FULLNAME + } + } + if o.NativeAppSettings.AppDownloadLink == nil { o.NativeAppSettings.AppDownloadLink = new(string) *o.NativeAppSettings.AppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK @@ -1076,6 +1322,11 @@ func (o *Config) SetDefaults() { *o.RateLimitSettings.Enable = false } + if o.ServiceSettings.GoroutineHealthThreshold == nil { + o.ServiceSettings.GoroutineHealthThreshold = new(int) + *o.ServiceSettings.GoroutineHealthThreshold = -1 + } + if o.RateLimitSettings.MaxBurst == nil { o.RateLimitSettings.MaxBurst = new(int) *o.RateLimitSettings.MaxBurst = 100 @@ -1131,16 +1382,90 @@ func (o *Config) SetDefaults() { *o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = 5000 } + if o.ServiceSettings.EnablePostSearch == nil { + o.ServiceSettings.EnablePostSearch = new(bool) + *o.ServiceSettings.EnablePostSearch = true + } + if o.ServiceSettings.EnableUserTypingMessages == nil { o.ServiceSettings.EnableUserTypingMessages = new(bool) *o.ServiceSettings.EnableUserTypingMessages = true } + if o.ServiceSettings.EnableChannelViewedMessages == nil { + o.ServiceSettings.EnableChannelViewedMessages = new(bool) + *o.ServiceSettings.EnableChannelViewedMessages = true + } + + if o.ServiceSettings.EnableUserStatuses == nil { + o.ServiceSettings.EnableUserStatuses = new(bool) + *o.ServiceSettings.EnableUserStatuses = true + } + if o.ServiceSettings.ClusterLogTimeoutMilliseconds == nil { o.ServiceSettings.ClusterLogTimeoutMilliseconds = new(int) *o.ServiceSettings.ClusterLogTimeoutMilliseconds = 2000 } + if o.ElasticsearchSettings.ConnectionUrl == nil { + o.ElasticsearchSettings.ConnectionUrl = new(string) + *o.ElasticsearchSettings.ConnectionUrl = ELASTICSEARCH_SETTINGS_DEFAULT_CONNECTION_URL + } + + if o.ElasticsearchSettings.Username == nil { + o.ElasticsearchSettings.Username = new(string) + *o.ElasticsearchSettings.Username = ELASTICSEARCH_SETTINGS_DEFAULT_USERNAME + } + + if o.ElasticsearchSettings.Password == nil { + o.ElasticsearchSettings.Password = new(string) + *o.ElasticsearchSettings.Password = ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD + } + + if o.ElasticsearchSettings.EnableIndexing == nil { + o.ElasticsearchSettings.EnableIndexing = new(bool) + *o.ElasticsearchSettings.EnableIndexing = false + } + + if o.ElasticsearchSettings.EnableSearching == nil { + o.ElasticsearchSettings.EnableSearching = new(bool) + *o.ElasticsearchSettings.EnableSearching = false + } + + if o.ElasticsearchSettings.Sniff == nil { + o.ElasticsearchSettings.Sniff = new(bool) + *o.ElasticsearchSettings.Sniff = true + } + + if o.ElasticsearchSettings.PostIndexReplicas == nil { + o.ElasticsearchSettings.PostIndexReplicas = new(int) + *o.ElasticsearchSettings.PostIndexReplicas = ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_REPLICAS + } + + if o.ElasticsearchSettings.PostIndexShards == nil { + o.ElasticsearchSettings.PostIndexShards = new(int) + *o.ElasticsearchSettings.PostIndexShards = ELASTICSEARCH_SETTINGS_DEFAULT_POST_INDEX_SHARDS + } + + if o.DataRetentionSettings.Enable == nil { + o.DataRetentionSettings.Enable = new(bool) + *o.DataRetentionSettings.Enable = false + } + + if o.JobSettings.RunJobs == nil { + o.JobSettings.RunJobs = new(bool) + *o.JobSettings.RunJobs = true + } + + if o.JobSettings.RunScheduler == nil { + o.JobSettings.RunScheduler = new(bool) + *o.JobSettings.RunScheduler = true + } + + if o.PluginSettings.Plugins == nil { + o.PluginSettings.Plugins = make(map[string]interface{}) + } + o.defaultWebrtcSettings() } @@ -1184,6 +1509,10 @@ func (o *Config) IsValid() *AppError { return NewLocAppError("Config.IsValid", "model.config.is_valid.restrict_direct_message.app_error", nil, "") } + if !(*o.TeamSettings.TeammateNameDisplay == SHOW_FULLNAME || *o.TeamSettings.TeammateNameDisplay == SHOW_NICKNAME_FULLNAME || *o.TeamSettings.TeammateNameDisplay == SHOW_USERNAME) { + return NewLocAppError("Config.IsValid", "model.config.is_valid.teammate_name_display.app_error", nil, "") + } + if len(o.SqlSettings.AtRestEncryptKey) < 32 { return NewLocAppError("Config.IsValid", "model.config.is_valid.encrypt_sql.app_error", nil, "") } @@ -1196,6 +1525,10 @@ func (o *Config) IsValid() *AppError { return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_idle.app_error", nil, "") } + if *o.SqlSettings.QueryTimeout <= 0 { + return NewAppError("Config.IsValid", "model.config.is_valid.sql_query_timeout.app_error", nil, "", http.StatusBadRequest) + } + if len(o.SqlSettings.DataSource) == 0 { return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_data_src.app_error", nil, "") } @@ -1212,30 +1545,6 @@ func (o *Config) IsValid() *AppError { return NewLocAppError("Config.IsValid", "model.config.is_valid.file_driver.app_error", nil, "") } - if o.FileSettings.PreviewHeight < 0 { - return NewLocAppError("Config.IsValid", "model.config.is_valid.file_preview_height.app_error", nil, "") - } - - if o.FileSettings.PreviewWidth <= 0 { - return NewLocAppError("Config.IsValid", "model.config.is_valid.file_preview_width.app_error", nil, "") - } - - if o.FileSettings.ProfileHeight <= 0 { - return NewLocAppError("Config.IsValid", "model.config.is_valid.file_profile_height.app_error", nil, "") - } - - if o.FileSettings.ProfileWidth <= 0 { - return NewLocAppError("Config.IsValid", "model.config.is_valid.file_profile_width.app_error", nil, "") - } - - if o.FileSettings.ThumbnailHeight <= 0 { - return NewLocAppError("Config.IsValid", "model.config.is_valid.file_thumb_height.app_error", nil, "") - } - - if o.FileSettings.ThumbnailWidth <= 0 { - return NewLocAppError("Config.IsValid", "model.config.is_valid.file_thumb_width.app_error", nil, "") - } - if len(*o.FileSettings.PublicLinkSalt) < 32 { return NewLocAppError("Config.IsValid", "model.config.is_valid.file_salt.app_error", nil, "") } @@ -1248,10 +1557,6 @@ func (o *Config) IsValid() *AppError { return NewLocAppError("Config.IsValid", "model.config.is_valid.email_salt.app_error", nil, "") } - if len(o.EmailSettings.PasswordResetSalt) < 32 { - return NewLocAppError("Config.IsValid", "model.config.is_valid.email_reset_salt.app_error", nil, "") - } - if *o.EmailSettings.EmailBatchingBufferSize <= 0 { return NewLocAppError("Config.IsValid", "model.config.is_valid.email_batching_buffer_size.app_error", nil, "") } @@ -1260,6 +1565,10 @@ func (o *Config) IsValid() *AppError { return NewLocAppError("Config.IsValid", "model.config.is_valid.email_batching_interval.app_error", nil, "") } + if !(*o.EmailSettings.EmailNotificationContentsType == EMAIL_NOTIFICATION_CONTENTS_FULL || *o.EmailSettings.EmailNotificationContentsType == EMAIL_NOTIFICATION_CONTENTS_GENERIC) { + return NewLocAppError("Config.IsValid", "model.config.is_valid.email_notification_contents_type.app_error", nil, "") + } + if o.RateLimitSettings.MemoryStoreSize <= 0 { return NewLocAppError("Config.IsValid", "model.config.is_valid.rate_mem.app_error", nil, "") } @@ -1376,6 +1685,16 @@ func (o *Config) IsValid() *AppError { return NewLocAppError("Config.IsValid", "model.config.is_valid.time_between_user_typing.app_error", nil, "") } + if *o.ElasticsearchSettings.EnableIndexing { + if len(*o.ElasticsearchSettings.ConnectionUrl) == 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.elastic_search.connection_url.app_error", nil, "") + } + } + + if *o.ElasticsearchSettings.EnableSearching && !*o.ElasticsearchSettings.EnableIndexing { + return NewLocAppError("Config.IsValid", "model.config.is_valid.elastic_search.enable_searching.app_error", nil, "") + } + return nil } @@ -1398,7 +1717,6 @@ func (o *Config) Sanitize() { } o.EmailSettings.InviteSalt = FAKE_SETTING - o.EmailSettings.PasswordResetSalt = FAKE_SETTING if len(o.EmailSettings.SMTPPassword) > 0 { o.EmailSettings.SMTPPassword = FAKE_SETTING } @@ -1413,6 +1731,12 @@ func (o *Config) Sanitize() { for i := range o.SqlSettings.DataSourceReplicas { o.SqlSettings.DataSourceReplicas[i] = FAKE_SETTING } + + for i := range o.SqlSettings.DataSourceSearchReplicas { + o.SqlSettings.DataSourceSearchReplicas[i] = FAKE_SETTING + } + + *o.ElasticsearchSettings.Password = FAKE_SETTING } func (o *Config) defaultWebrtcSettings() { diff --git a/vendor/github.com/mattermost/platform/model/emoji.go b/vendor/github.com/mattermost/platform/model/emoji.go index a66053aa..5ade868c 100644 --- a/vendor/github.com/mattermost/platform/model/emoji.go +++ b/vendor/github.com/mattermost/platform/model/emoji.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -34,7 +34,7 @@ func (emoji *Emoji) IsValid() *AppError { return NewLocAppError("Emoji.IsValid", "model.emoji.user_id.app_error", nil, "") } - if len(emoji.Name) == 0 || len(emoji.Name) > 64 { + if len(emoji.Name) == 0 || len(emoji.Name) > 64 || !IsValidAlphaNumHyphenUnderscore(emoji.Name, false) { return NewLocAppError("Emoji.IsValid", "model.emoji.name.app_error", nil, "") } diff --git a/vendor/github.com/mattermost/platform/model/file.go b/vendor/github.com/mattermost/platform/model/file.go index 20f6236d..f9258146 100644 --- a/vendor/github.com/mattermost/platform/model/file.go +++ b/vendor/github.com/mattermost/platform/model/file.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/file_info.go b/vendor/github.com/mattermost/platform/model/file_info.go index 687473d4..8b568412 100644 --- a/vendor/github.com/mattermost/platform/model/file_info.go +++ b/vendor/github.com/mattermost/platform/model/file_info.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/gitlab.go b/vendor/github.com/mattermost/platform/model/gitlab.go index 3dfb1016..8777614c 100644 --- a/vendor/github.com/mattermost/platform/model/gitlab.go +++ b/vendor/github.com/mattermost/platform/model/gitlab.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go b/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go index 270d62d8..7f1447ed 100644 --- a/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go +++ b/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package oauthgitlab diff --git a/vendor/github.com/mattermost/platform/model/incoming_webhook.go b/vendor/github.com/mattermost/platform/model/incoming_webhook.go index 2cc26cbc..ce755f88 100644 --- a/vendor/github.com/mattermost/platform/model/incoming_webhook.go +++ b/vendor/github.com/mattermost/platform/model/incoming_webhook.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -6,10 +6,9 @@ package model import ( "bytes" "encoding/json" - "fmt" "io" + "net/http" "regexp" - "strings" ) const ( @@ -81,35 +80,36 @@ func IncomingWebhookListFromJson(data io.Reader) []*IncomingWebhook { func (o *IncomingWebhook) IsValid() *AppError { if len(o.Id) != 26 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.id.app_error", nil, "") + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.id.app_error", nil, "", http.StatusBadRequest) + } if o.CreateAt == 0 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.create_at.app_error", nil, "id="+o.Id) + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if o.UpdateAt == 0 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.update_at.app_error", nil, "id="+o.Id) + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if len(o.UserId) != 26 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.user_id.app_error", nil, "") + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.user_id.app_error", nil, "", http.StatusBadRequest) } if len(o.ChannelId) != 26 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.channel_id.app_error", nil, "") + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.channel_id.app_error", nil, "", http.StatusBadRequest) } if len(o.TeamId) != 26 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "") + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "", http.StatusBadRequest) } if len(o.DisplayName) > 64 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "") + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "", http.StatusBadRequest) } if len(o.Description) > 128 { - return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "") + return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "", http.StatusBadRequest) } return nil @@ -193,39 +193,6 @@ func decodeIncomingWebhookRequest(by []byte) (*IncomingWebhookRequest, error) { } } -// To mention @channel via a webhook in Slack, the message should contain -// <!channel>, as explained at the bottom of this article: -// https://get.slack.help/hc/en-us/articles/202009646-Making-announcements -func expandAnnouncement(text string) string { - c1 := "<!channel>" - c2 := "@channel" - if strings.Contains(text, c1) { - return strings.Replace(text, c1, c2, -1) - } - return text -} - -// Expand announcements in incoming webhooks from Slack. Those announcements -// can be found in the text attribute, or in the pretext, text, title and value -// attributes of the attachment structure. The Slack attachment structure is -// documented here: https://api.slack.com/docs/attachments -func expandAnnouncements(i *IncomingWebhookRequest) { - i.Text = expandAnnouncement(i.Text) - - for _, attachment := range i.Attachments { - attachment.Pretext = expandAnnouncement(attachment.Pretext) - attachment.Text = expandAnnouncement(attachment.Text) - attachment.Title = expandAnnouncement(attachment.Title) - - for _, field := range attachment.Fields { - if field.Value != nil { - // Ensure the value is set to a string if it is set - field.Value = expandAnnouncement(fmt.Sprintf("%v", field.Value)) - } - } - } -} - func IncomingWebhookRequestFromJson(data io.Reader) *IncomingWebhookRequest { buf := new(bytes.Buffer) buf.ReadFrom(data) @@ -241,7 +208,8 @@ func IncomingWebhookRequestFromJson(data io.Reader) *IncomingWebhookRequest { } } - expandAnnouncements(o) + o.Text = ExpandAnnouncement(o.Text) + o.Attachments = ProcessSlackAttachments(o.Attachments) return o } diff --git a/vendor/github.com/mattermost/platform/model/initial_load.go b/vendor/github.com/mattermost/platform/model/initial_load.go index afb0a276..71ba1769 100644 --- a/vendor/github.com/mattermost/platform/model/initial_load.go +++ b/vendor/github.com/mattermost/platform/model/initial_load.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/job.go b/vendor/github.com/mattermost/platform/model/job.go index 09d74aa0..004331a1 100644 --- a/vendor/github.com/mattermost/platform/model/job.go +++ b/vendor/github.com/mattermost/platform/model/job.go @@ -1,100 +1,117 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model import ( - "fmt" - "time" + "encoding/json" + "io" + "net/http" ) -type TaskFunc func() +const ( + JOB_TYPE_DATA_RETENTION = "data_retention" + JOB_TYPE_ELASTICSEARCH_POST_INDEXING = "elasticsearch_post_indexing" -type ScheduledTask struct { - Name string `json:"name"` - Interval time.Duration `json:"interval"` - Recurring bool `json:"recurring"` - function TaskFunc - timer *time.Timer -} - -var tasks = make(map[string]*ScheduledTask) - -func addTask(task *ScheduledTask) { - tasks[task.Name] = task -} + JOB_STATUS_PENDING = "pending" + JOB_STATUS_IN_PROGRESS = "in_progress" + JOB_STATUS_SUCCESS = "success" + JOB_STATUS_ERROR = "error" + JOB_STATUS_CANCEL_REQUESTED = "cancel_requested" + JOB_STATUS_CANCELED = "canceled" +) -func removeTaskByName(name string) { - delete(tasks, name) +type Job struct { + Id string `json:"id"` + Type string `json:"type"` + Priority int64 `json:"priority"` + CreateAt int64 `json:"create_at"` + StartAt int64 `json:"start_at"` + LastActivityAt int64 `json:"last_activity_at"` + Status string `json:"status"` + Progress int64 `json:"progress"` + Data map[string]interface{} `json:"data"` } -func GetTaskByName(name string) *ScheduledTask { - if task, ok := tasks[name]; ok { - return task +func (j *Job) IsValid() *AppError { + if len(j.Id) != 26 { + return NewAppError("Job.IsValid", "model.job.is_valid.id.app_error", nil, "id="+j.Id, http.StatusBadRequest) } - return nil -} -func GetAllTasks() *map[string]*ScheduledTask { - return &tasks -} - -func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask { - task := &ScheduledTask{ - Name: name, - Interval: timeToExecution, - Recurring: false, - function: function, + if j.CreateAt == 0 { + return NewAppError("Job.IsValid", "model.job.is_valid.create_at.app_error", nil, "id="+j.Id, http.StatusBadRequest) } - taskRunner := func() { - go task.function() - removeTaskByName(task.Name) + switch j.Type { + case JOB_TYPE_DATA_RETENTION: + case JOB_TYPE_ELASTICSEARCH_POST_INDEXING: + default: + return NewAppError("Job.IsValid", "model.job.is_valid.type.app_error", nil, "id="+j.Id, http.StatusBadRequest) } - task.timer = time.AfterFunc(timeToExecution, taskRunner) - - addTask(task) + switch j.Status { + case JOB_STATUS_PENDING: + case JOB_STATUS_IN_PROGRESS: + case JOB_STATUS_SUCCESS: + case JOB_STATUS_ERROR: + case JOB_STATUS_CANCEL_REQUESTED: + case JOB_STATUS_CANCELED: + default: + return NewAppError("Job.IsValid", "model.job.is_valid.status.app_error", nil, "id="+j.Id, http.StatusBadRequest) + } - return task + return nil } -func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask { - task := &ScheduledTask{ - Name: name, - Interval: interval, - Recurring: true, - function: function, +func (js *Job) ToJson() string { + if b, err := json.Marshal(js); err != nil { + return "" + } else { + return string(b) } +} - taskRecurer := func() { - go task.function() - task.timer.Reset(task.Interval) +func JobFromJson(data io.Reader) *Job { + var job Job + if err := json.NewDecoder(data).Decode(&job); err == nil { + return &job + } else { + return nil } +} - task.timer = time.AfterFunc(interval, taskRecurer) - - addTask(task) +func JobsToJson(jobs []*Job) string { + if b, err := json.Marshal(jobs); err != nil { + return "" + } else { + return string(b) + } +} - return task +func JobsFromJson(data io.Reader) []*Job { + var jobs []*Job + if err := json.NewDecoder(data).Decode(&jobs); err == nil { + return jobs + } else { + return nil + } } -func (task *ScheduledTask) Cancel() { - task.timer.Stop() - removeTaskByName(task.Name) +func (js *Job) DataToJson() string { + if b, err := json.Marshal(js.Data); err != nil { + return "" + } else { + return string(b) + } } -// Executes the task immediatly. A recurring task will be run regularally after interval. -func (task *ScheduledTask) Execute() { - task.function() - task.timer.Reset(task.Interval) +type Worker interface { + Run() + Stop() + JobChannel() chan<- Job } -func (task *ScheduledTask) String() string { - return fmt.Sprintf( - "%s\nInterval: %s\nRecurring: %t\n", - task.Name, - task.Interval.String(), - task.Recurring, - ) +type Scheduler interface { + Run() + Stop() } diff --git a/vendor/github.com/mattermost/platform/model/ldap.go b/vendor/github.com/mattermost/platform/model/ldap.go index 71b1d8a6..1453a4ad 100644 --- a/vendor/github.com/mattermost/platform/model/ldap.go +++ b/vendor/github.com/mattermost/platform/model/ldap.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/license.go b/vendor/github.com/mattermost/platform/model/license.go index 09da61eb..ea108972 100644 --- a/vendor/github.com/mattermost/platform/model/license.go +++ b/vendor/github.com/mattermost/platform/model/license.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -37,36 +37,42 @@ type Customer struct { } type Features struct { - Users *int `json:"users"` - LDAP *bool `json:"ldap"` - MFA *bool `json:"mfa"` - GoogleOAuth *bool `json:"google_oauth"` - Office365OAuth *bool `json:"office365_oauth"` - Compliance *bool `json:"compliance"` - Cluster *bool `json:"cluster"` - Metrics *bool `json:"metrics"` - CustomBrand *bool `json:"custom_brand"` - MHPNS *bool `json:"mhpns"` - SAML *bool `json:"saml"` - PasswordRequirements *bool `json:"password_requirements"` + Users *int `json:"users"` + LDAP *bool `json:"ldap"` + MFA *bool `json:"mfa"` + GoogleOAuth *bool `json:"google_oauth"` + Office365OAuth *bool `json:"office365_oauth"` + Compliance *bool `json:"compliance"` + Cluster *bool `json:"cluster"` + Metrics *bool `json:"metrics"` + CustomBrand *bool `json:"custom_brand"` + MHPNS *bool `json:"mhpns"` + SAML *bool `json:"saml"` + PasswordRequirements *bool `json:"password_requirements"` + Elasticsearch *bool `json:"elastic_search"` + Announcement *bool `json:"announcement"` + EmailNotificationContents *bool `json:"email_notification_contents"` + // after we enabled more features for webrtc we'll need to control them with this FutureFeatures *bool `json:"future_features"` } func (f *Features) ToMap() map[string]interface{} { return map[string]interface{}{ - "ldap": *f.LDAP, - "mfa": *f.MFA, - "google": *f.GoogleOAuth, - "office365": *f.Office365OAuth, - "compliance": *f.Compliance, - "cluster": *f.Cluster, - "metrics": *f.Metrics, - "custom_brand": *f.CustomBrand, - "mhpns": *f.MHPNS, - "saml": *f.SAML, - "password": *f.PasswordRequirements, - "future": *f.FutureFeatures, + "ldap": *f.LDAP, + "mfa": *f.MFA, + "google": *f.GoogleOAuth, + "office365": *f.Office365OAuth, + "compliance": *f.Compliance, + "cluster": *f.Cluster, + "metrics": *f.Metrics, + "custom_brand": *f.CustomBrand, + "mhpns": *f.MHPNS, + "saml": *f.SAML, + "password": *f.PasswordRequirements, + "elastic_search": *f.Elasticsearch, + "email_notification_contents": *f.EmailNotificationContents, + "future": *f.FutureFeatures, } } @@ -135,6 +141,21 @@ func (f *Features) SetDefaults() { f.PasswordRequirements = new(bool) *f.PasswordRequirements = *f.FutureFeatures } + + if f.Elasticsearch == nil { + f.Elasticsearch = new(bool) + *f.Elasticsearch = *f.FutureFeatures + } + + if f.Announcement == nil { + f.Announcement = new(bool) + *f.Announcement = true + } + + if f.EmailNotificationContents == nil { + f.EmailNotificationContents = new(bool) + *f.EmailNotificationContents = *f.FutureFeatures + } } func (l *License) IsExpired() bool { diff --git a/vendor/github.com/mattermost/platform/model/mfa_secret.go b/vendor/github.com/mattermost/platform/model/mfa_secret.go new file mode 100644 index 00000000..99580f5f --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/mfa_secret.go @@ -0,0 +1,34 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type MfaSecret struct { + Secret string `json:"secret"` + QRCode string `json:"qr_code"` +} + +func (me *MfaSecret) ToJson() string { + b, err := json.Marshal(me) + if err != nil { + return "" + } else { + return string(b) + } +} + +func MfaSecretFromJson(data io.Reader) *MfaSecret { + decoder := json.NewDecoder(data) + var me MfaSecret + err := decoder.Decode(&me) + if err == nil { + return &me + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/oauth.go b/vendor/github.com/mattermost/platform/model/oauth.go index cfe643c9..3139aefe 100644 --- a/vendor/github.com/mattermost/platform/model/oauth.go +++ b/vendor/github.com/mattermost/platform/model/oauth.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "net/http" "unicode/utf8" ) @@ -15,6 +16,7 @@ const ( OAUTH_ACTION_LOGIN = "login" OAUTH_ACTION_EMAIL_TO_SSO = "email_to_sso" OAUTH_ACTION_SSO_TO_EMAIL = "sso_to_email" + OAUTH_ACTION_MOBILE = "mobile" ) type OAuthApp struct { @@ -36,50 +38,50 @@ type OAuthApp struct { func (a *OAuthApp) IsValid() *AppError { if len(a.Id) != 26 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.app_id.app_error", nil, "") + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.app_id.app_error", nil, "", http.StatusBadRequest) } if a.CreateAt == 0 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.create_at.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.create_at.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if a.UpdateAt == 0 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.update_at.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.update_at.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.CreatorId) != 26 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.creator_id.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.creator_id.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.ClientSecret) == 0 || len(a.ClientSecret) > 128 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.client_secret.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.client_secret.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.Name) == 0 || len(a.Name) > 64 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.name.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.name.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.CallbackUrls) == 0 || len(fmt.Sprintf("%s", a.CallbackUrls)) > 1024 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } for _, callback := range a.CallbackUrls { if !IsValidHttpUrl(callback) { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "") + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "", http.StatusBadRequest) } } if len(a.Homepage) == 0 || len(a.Homepage) > 256 || !IsValidHttpUrl(a.Homepage) { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if utf8.RuneCountInString(a.Description) > 512 { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } if len(a.IconURL) > 0 { if len(a.IconURL) > 512 || !IsValidHttpUrl(a.IconURL) { - return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id) + return NewAppError("OAuthApp.IsValid", "model.oauth.is_valid.icon_url.app_error", nil, "app_id="+a.Id, http.StatusBadRequest) } } diff --git a/vendor/github.com/mattermost/platform/model/outgoing_webhook.go b/vendor/github.com/mattermost/platform/model/outgoing_webhook.go index ec2ed75c..59408c24 100644 --- a/vendor/github.com/mattermost/platform/model/outgoing_webhook.go +++ b/vendor/github.com/mattermost/platform/model/outgoing_webhook.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "net/http" "net/url" "strconv" "strings" @@ -41,6 +42,7 @@ type OutgoingWebhookPayload struct { PostId string `json:"post_id"` Text string `json:"text"` TriggerWord string `json:"trigger_word"` + FileIds string `json:"file_ids"` } func (o *OutgoingWebhookPayload) ToJSON() string { @@ -65,6 +67,7 @@ func (o *OutgoingWebhookPayload) ToFormValues() string { v.Set("post_id", o.PostId) v.Set("text", o.Text) v.Set("trigger_word", o.TriggerWord) + v.Set("file_ids", o.FileIds) return v.Encode() } @@ -112,69 +115,69 @@ func OutgoingWebhookListFromJson(data io.Reader) []*OutgoingWebhook { func (o *OutgoingWebhook) IsValid() *AppError { if len(o.Id) != 26 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.id.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.id.app_error", nil, "", http.StatusBadRequest) } if len(o.Token) != 26 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.token.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.token.app_error", nil, "", http.StatusBadRequest) } if o.CreateAt == 0 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.create_at.app_error", nil, "id="+o.Id) + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if o.UpdateAt == 0 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.update_at.app_error", nil, "id="+o.Id) + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest) } if len(o.CreatorId) != 26 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.user_id.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) } if len(o.ChannelId) != 0 && len(o.ChannelId) != 26 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.channel_id.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest) } if len(o.TeamId) != 26 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.team_id.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.team_id.app_error", nil, "", http.StatusBadRequest) } if len(fmt.Sprintf("%s", o.TriggerWords)) > 1024 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.words.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.words.app_error", nil, "", http.StatusBadRequest) } if len(o.TriggerWords) != 0 { for _, triggerWord := range o.TriggerWords { if len(triggerWord) == 0 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.trigger_words.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.trigger_words.app_error", nil, "", http.StatusBadRequest) } } } if len(o.CallbackURLs) == 0 || len(fmt.Sprintf("%s", o.CallbackURLs)) > 1024 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.callback.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.callback.app_error", nil, "", http.StatusBadRequest) } for _, callback := range o.CallbackURLs { if !IsValidHttpUrl(callback) { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.url.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.url.app_error", nil, "", http.StatusBadRequest) } } if len(o.DisplayName) > 64 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.display_name.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.display_name.app_error", nil, "", http.StatusBadRequest) } if len(o.Description) > 128 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "", http.StatusBadRequest) } if len(o.ContentType) > 128 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "", http.StatusBadRequest) } if o.TriggerWhen > 1 { - return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "") + return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "", http.StatusBadRequest) } return nil @@ -197,8 +200,8 @@ func (o *OutgoingWebhook) PreUpdate() { o.UpdateAt = GetMillis() } -func (o *OutgoingWebhook) HasTriggerWord(word string) bool { - if len(o.TriggerWords) == 0 || len(word) == 0 { +func (o *OutgoingWebhook) TriggerWordExactMatch(word string) bool { + if len(word) == 0 { return false } @@ -212,7 +215,7 @@ func (o *OutgoingWebhook) HasTriggerWord(word string) bool { } func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool { - if len(o.TriggerWords) == 0 || len(word) == 0 { + if len(word) == 0 { return false } @@ -224,3 +227,27 @@ func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool { return false } + +func (o *OutgoingWebhook) GetTriggerWord(word string, isExactMatch bool) (triggerWord string) { + if len(word) == 0 { + return + } + + if isExactMatch { + for _, trigger := range o.TriggerWords { + if trigger == word { + triggerWord = trigger + break + } + } + } else { + for _, trigger := range o.TriggerWords { + if strings.HasPrefix(word, trigger) { + triggerWord = trigger + break + } + } + } + + return triggerWord +} diff --git a/vendor/github.com/mattermost/platform/model/password_recovery.go b/vendor/github.com/mattermost/platform/model/password_recovery.go deleted file mode 100644 index 303d4a12..00000000 --- a/vendor/github.com/mattermost/platform/model/password_recovery.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package model - -const ( - PASSWORD_RECOVERY_CODE_SIZE = 128 - PASSWORD_RECOVER_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour -) - -type PasswordRecovery struct { - UserId string - Code string - CreateAt int64 -} - -func (p *PasswordRecovery) IsValid() *AppError { - - if len(p.UserId) != 26 { - return NewLocAppError("User.IsValid", "model.password_recovery.is_valid.user_id.app_error", nil, "") - } - - if len(p.Code) != PASSWORD_RECOVERY_CODE_SIZE { - return NewLocAppError("User.IsValid", "model.password_recovery.is_valid.code.app_error", nil, "") - } - - if p.CreateAt == 0 { - return NewLocAppError("User.IsValid", "model.password_recovery.is_valid.create_at.app_error", nil, "") - } - - return nil -} - -func (p *PasswordRecovery) PreSave() { - p.Code = NewRandomString(PASSWORD_RECOVERY_CODE_SIZE) - p.CreateAt = GetMillis() -} diff --git a/vendor/github.com/mattermost/platform/model/post.go b/vendor/github.com/mattermost/platform/model/post.go index a7729e0c..55e6f591 100644 --- a/vendor/github.com/mattermost/platform/model/post.go +++ b/vendor/github.com/mattermost/platform/model/post.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -38,6 +38,7 @@ type Post struct { UpdateAt int64 `json:"update_at"` EditAt int64 `json:"edit_at"` DeleteAt int64 `json:"delete_at"` + IsPinned bool `json:"is_pinned"` UserId string `json:"user_id"` ChannelId string `json:"channel_id"` RootId string `json:"root_id"` @@ -53,6 +54,20 @@ type Post struct { HasReactions bool `json:"has_reactions,omitempty"` } +type PostPatch struct { + IsPinned *bool `json:"is_pinned"` + Message *string `json:"message"` + Props *StringInterface `json:"props"` + FileIds *StringArray `json:"file_ids"` + HasReactions *bool `json:"has_reactions"` +} + +type PostForIndexing struct { + Post + TeamId string `json:"team_id"` + ParentCreateAt *int64 `json:"parent_create_at"` +} + func (o *Post) ToJson() string { b, err := json.Marshal(o) if err != nil { @@ -189,3 +204,45 @@ func (o *Post) AddProp(key string, value interface{}) { func (o *Post) IsSystemMessage() bool { return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX } + +func (p *Post) Patch(patch *PostPatch) { + if patch.IsPinned != nil { + p.IsPinned = *patch.IsPinned + } + + if patch.Message != nil { + p.Message = *patch.Message + } + + if patch.Props != nil { + p.Props = *patch.Props + } + + if patch.FileIds != nil { + p.FileIds = *patch.FileIds + } + + if patch.HasReactions != nil { + p.HasReactions = *patch.HasReactions + } +} + +func (o *PostPatch) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } + + return string(b) +} + +func PostPatchFromJson(data io.Reader) *PostPatch { + decoder := json.NewDecoder(data) + var post PostPatch + err := decoder.Decode(&post) + if err != nil { + return nil + } + + return &post +} diff --git a/vendor/github.com/mattermost/platform/model/post_list.go b/vendor/github.com/mattermost/platform/model/post_list.go index 9b56f023..63f6d682 100644 --- a/vendor/github.com/mattermost/platform/model/post_list.go +++ b/vendor/github.com/mattermost/platform/model/post_list.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/preference.go b/vendor/github.com/mattermost/platform/model/preference.go index 94807d2c..6bbe7326 100644 --- a/vendor/github.com/mattermost/platform/model/preference.go +++ b/vendor/github.com/mattermost/platform/model/preference.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -16,14 +16,10 @@ const ( PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step" PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings" PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post" + PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel" - PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings" - PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews" - PREFERENCE_NAME_DISPLAY_NAME_FORMAT = "name_format" - PREFERENCE_VALUE_DISPLAY_NAME_NICKNAME = "nickname_full_name" - PREFERENCE_VALUE_DISPLAY_NAME_FULL = "full_name" - PREFERENCE_VALUE_DISPLAY_NAME_USERNAME = "username" - PREFERENCE_DEFAULT_DISPLAY_NAME_FORMAT = PREFERENCE_VALUE_DISPLAY_NAME_USERNAME + PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings" + PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews" PREFERENCE_CATEGORY_THEME = "theme" // the name for theme props is the team id @@ -37,7 +33,9 @@ const ( PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications" PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval" - PREFERENCE_DEFAULT_EMAIL_INTERVAL = "30" // default to match the interval of the "immediate" setting (ie 30 seconds) + + PREFERENCE_EMAIL_INTERVAL_NO_BATCHING_SECONDS = "30" // the "immediate" setting is actually 30s + PREFERENCE_EMAIL_INTERVAL_BATCHING_SECONDS = "900" // fifteen minutes is 900 seconds ) type Preference struct { diff --git a/vendor/github.com/mattermost/platform/model/preferences.go b/vendor/github.com/mattermost/platform/model/preferences.go index f11b5fd8..b982e309 100644 --- a/vendor/github.com/mattermost/platform/model/preferences.go +++ b/vendor/github.com/mattermost/platform/model/preferences.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/push_notification.go b/vendor/github.com/mattermost/platform/model/push_notification.go index 753495b2..654d1d9a 100644 --- a/vendor/github.com/mattermost/platform/model/push_notification.go +++ b/vendor/github.com/mattermost/platform/model/push_notification.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -36,6 +36,10 @@ type PushNotification struct { ChannelId string `json:"channel_id"` ChannelName string `json:"channel_name"` Type string `json:"type"` + SenderId string `json:"sender_id"` + OverrideUsername string `json:"override_username"` + OverrideIconUrl string `json:"override_icon_url"` + FromWebhook string `json:"from_webhook"` } func (me *PushNotification) ToJson() string { diff --git a/vendor/github.com/mattermost/platform/model/push_response.go b/vendor/github.com/mattermost/platform/model/push_response.go index 0271bc94..095986f3 100644 --- a/vendor/github.com/mattermost/platform/model/push_response.go +++ b/vendor/github.com/mattermost/platform/model/push_response.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/reaction.go b/vendor/github.com/mattermost/platform/model/reaction.go index afbdd1e8..3d334c21 100644 --- a/vendor/github.com/mattermost/platform/model/reaction.go +++ b/vendor/github.com/mattermost/platform/model/reaction.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -6,6 +6,7 @@ package model import ( "encoding/json" "io" + "regexp" ) type Reaction struct { @@ -60,7 +61,9 @@ func (o *Reaction) IsValid() *AppError { return NewLocAppError("Reaction.IsValid", "model.reaction.is_valid.post_id.app_error", nil, "post_id="+o.PostId) } - if len(o.EmojiName) == 0 || len(o.EmojiName) > 64 { + validName := regexp.MustCompile(`^[a-zA-Z0-9\-\+_]+$`) + + if len(o.EmojiName) == 0 || len(o.EmojiName) > 64 || !validName.MatchString(o.EmojiName) { return NewLocAppError("Reaction.IsValid", "model.reaction.is_valid.emoji_name.app_error", nil, "emoji_name="+o.EmojiName) } diff --git a/vendor/github.com/mattermost/platform/model/saml.go b/vendor/github.com/mattermost/platform/model/saml.go index 16d3845d..f5826a95 100644 --- a/vendor/github.com/mattermost/platform/model/saml.go +++ b/vendor/github.com/mattermost/platform/model/saml.go @@ -1,8 +1,13 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model +import ( + "encoding/json" + "io" +) + const ( USER_AUTH_SERVICE_SAML = "saml" USER_AUTH_SERVICE_SAML_TEXT = "With SAML" @@ -16,3 +21,29 @@ type SamlAuthRequest struct { URL string RelayState string } + +type SamlCertificateStatus struct { + IdpCertificateFile bool `json:"idp_certificate_file"` + PrivateKeyFile bool `json:"private_key_file"` + PublicCertificateFile bool `json:"public_certificate_file"` +} + +func (s *SamlCertificateStatus) ToJson() string { + b, err := json.Marshal(s) + if err != nil { + return "" + } else { + return string(b) + } +} + +func SamlCertificateStatusFromJson(data io.Reader) *SamlCertificateStatus { + decoder := json.NewDecoder(data) + var status SamlCertificateStatus + err := decoder.Decode(&status) + if err == nil { + return &status + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/scheduled_task.go b/vendor/github.com/mattermost/platform/model/scheduled_task.go new file mode 100644 index 00000000..453828bd --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/scheduled_task.go @@ -0,0 +1,110 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "fmt" + "sync" + "time" +) + +type TaskFunc func() + +type ScheduledTask struct { + Name string `json:"name"` + Interval time.Duration `json:"interval"` + Recurring bool `json:"recurring"` + function TaskFunc + timer *time.Timer +} + +var taskMutex = sync.Mutex{} +var tasks = make(map[string]*ScheduledTask) + +func addTask(task *ScheduledTask) { + taskMutex.Lock() + defer taskMutex.Unlock() + tasks[task.Name] = task +} + +func removeTaskByName(name string) { + taskMutex.Lock() + defer taskMutex.Unlock() + delete(tasks, name) +} + +func GetTaskByName(name string) *ScheduledTask { + taskMutex.Lock() + defer taskMutex.Unlock() + if task, ok := tasks[name]; ok { + return task + } + return nil +} + +func GetAllTasks() *map[string]*ScheduledTask { + taskMutex.Lock() + defer taskMutex.Unlock() + return &tasks +} + +func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask { + task := &ScheduledTask{ + Name: name, + Interval: timeToExecution, + Recurring: false, + function: function, + } + + taskRunner := func() { + go task.function() + removeTaskByName(task.Name) + } + + task.timer = time.AfterFunc(timeToExecution, taskRunner) + + addTask(task) + + return task +} + +func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask { + task := &ScheduledTask{ + Name: name, + Interval: interval, + Recurring: true, + function: function, + } + + taskRecurer := func() { + go task.function() + task.timer.Reset(task.Interval) + } + + task.timer = time.AfterFunc(interval, taskRecurer) + + addTask(task) + + return task +} + +func (task *ScheduledTask) Cancel() { + task.timer.Stop() + removeTaskByName(task.Name) +} + +// Executes the task immediatly. A recurring task will be run regularally after interval. +func (task *ScheduledTask) Execute() { + task.function() + task.timer.Reset(task.Interval) +} + +func (task *ScheduledTask) String() string { + return fmt.Sprintf( + "%s\nInterval: %s\nRecurring: %t\n", + task.Name, + task.Interval.String(), + task.Recurring, + ) +} diff --git a/vendor/github.com/mattermost/platform/model/search_params.go b/vendor/github.com/mattermost/platform/model/search_params.go index 1772c902..070ac6d2 100644 --- a/vendor/github.com/mattermost/platform/model/search_params.go +++ b/vendor/github.com/mattermost/platform/model/search_params.go @@ -1,9 +1,10 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model import ( + "encoding/json" "regexp" "strings" ) @@ -19,6 +20,15 @@ type SearchParams struct { OrTerms bool } +func (o *SearchParams) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + var searchFlags = [...]string{"from", "channel", "in"} func splitWordsNoQuotes(text string) []string { @@ -165,7 +175,7 @@ func ParseSearchParams(text string) []*SearchParams { if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0) { paramsList = append(paramsList, &SearchParams{ Terms: "", - IsHashtag: true, + IsHashtag: false, InChannels: inChannels, FromUsers: fromUsers, }) diff --git a/vendor/github.com/mattermost/platform/model/security_bulletin.go b/vendor/github.com/mattermost/platform/model/security_bulletin.go index 8d9be6d3..b8c1dc48 100644 --- a/vendor/github.com/mattermost/platform/model/security_bulletin.go +++ b/vendor/github.com/mattermost/platform/model/security_bulletin.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/session.go b/vendor/github.com/mattermost/platform/model/session.go index 277e910f..960c18cb 100644 --- a/vendor/github.com/mattermost/platform/model/session.go +++ b/vendor/github.com/mattermost/platform/model/session.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -10,11 +10,17 @@ import ( ) const ( - SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" - SESSION_CACHE_SIZE = 35000 - SESSION_PROP_PLATFORM = "platform" - SESSION_PROP_OS = "os" - SESSION_PROP_BROWSER = "browser" + SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" + SESSION_COOKIE_USER = "MMUSERID" + SESSION_CACHE_SIZE = 35000 + SESSION_PROP_PLATFORM = "platform" + SESSION_PROP_OS = "os" + SESSION_PROP_BROWSER = "browser" + SESSION_PROP_TYPE = "type" + SESSION_PROP_USER_ACCESS_TOKEN_ID = "user_access_token_id" + SESSION_TYPE_USER_ACCESS_TOKEN = "UserAccessToken" + SESSION_ACTIVITY_TIMEOUT = 1000 * 60 * 5 // 5 minutes + SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years ) type Session struct { @@ -56,7 +62,9 @@ func (me *Session) PreSave() { me.Id = NewId() } - me.Token = NewId() + if me.Token == "" { + me.Token = NewId() + } me.CreateAt = GetMillis() me.LastActivityAt = me.CreateAt diff --git a/vendor/github.com/mattermost/platform/model/slack_attachment.go b/vendor/github.com/mattermost/platform/model/slack_attachment.go index e1639f2a..85583821 100644 --- a/vendor/github.com/mattermost/platform/model/slack_attachment.go +++ b/vendor/github.com/mattermost/platform/model/slack_attachment.go @@ -1,8 +1,13 @@ -// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model +import ( + "fmt" + "strings" +) + type SlackAttachment struct { Id int64 `json:"id"` Fallback string `json:"fallback"` @@ -27,3 +32,48 @@ type SlackAttachmentField struct { Value interface{} `json:"value"` Short bool `json:"short"` } + +// To mention @channel via a webhook in Slack, the message should contain +// <!channel>, as explained at the bottom of this article: +// https://get.slack.help/hc/en-us/articles/202009646-Making-announcements +func ExpandAnnouncement(text string) string { + c1 := "<!channel>" + c2 := "@channel" + if strings.Contains(text, c1) { + return strings.Replace(text, c1, c2, -1) + } + return text +} + +// Expand announcements in incoming webhooks from Slack. Those announcements +// can be found in the text attribute, or in the pretext, text, title and value +// attributes of the attachment structure. The Slack attachment structure is +// documented here: https://api.slack.com/docs/attachments +func ProcessSlackAttachments(a []*SlackAttachment) []*SlackAttachment { + var nonNilAttachments []*SlackAttachment + for _, attachment := range a { + if attachment == nil { + continue + } + nonNilAttachments = append(nonNilAttachments, attachment) + + attachment.Pretext = ExpandAnnouncement(attachment.Pretext) + attachment.Text = ExpandAnnouncement(attachment.Text) + attachment.Title = ExpandAnnouncement(attachment.Title) + + var nonNilFields []*SlackAttachmentField + for _, field := range attachment.Fields { + if field == nil { + continue + } + nonNilFields = append(nonNilFields, field) + + if field.Value != nil { + // Ensure the value is set to a string if it is set + field.Value = ExpandAnnouncement(fmt.Sprintf("%v", field.Value)) + } + } + attachment.Fields = nonNilFields + } + return nonNilAttachments +} diff --git a/vendor/github.com/mattermost/platform/model/status.go b/vendor/github.com/mattermost/platform/model/status.go index fc155788..d838eea0 100644 --- a/vendor/github.com/mattermost/platform/model/status.go +++ b/vendor/github.com/mattermost/platform/model/status.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -22,7 +22,7 @@ type Status struct { Status string `json:"status"` Manual bool `json:"manual"` LastActivityAt int64 `json:"last_activity_at"` - ActiveChannel string `json:"active_channel" db:"-"` + ActiveChannel string `json:"-" db:"-"` } func (o *Status) ToJson() string { @@ -45,6 +45,26 @@ func StatusFromJson(data io.Reader) *Status { } } +func StatusListToJson(u []*Status) string { + b, err := json.Marshal(u) + if err != nil { + return "" + } else { + return string(b) + } +} + +func StatusListFromJson(data io.Reader) []*Status { + decoder := json.NewDecoder(data) + var statuses []*Status + err := decoder.Decode(&statuses) + if err == nil { + return statuses + } else { + return nil + } +} + func StatusMapToInterfaceMap(statusMap map[string]*Status) map[string]interface{} { interfaceMap := map[string]interface{}{} for _, s := range statusMap { diff --git a/vendor/github.com/mattermost/platform/model/suggest_command.go b/vendor/github.com/mattermost/platform/model/suggest_command.go index 7bc35369..3d066499 100644 --- a/vendor/github.com/mattermost/platform/model/suggest_command.go +++ b/vendor/github.com/mattermost/platform/model/suggest_command.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/switch_request.go b/vendor/github.com/mattermost/platform/model/switch_request.go new file mode 100644 index 00000000..10039294 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/switch_request.go @@ -0,0 +1,62 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type SwitchRequest struct { + CurrentService string `json:"current_service"` + NewService string `json:"new_service"` + Email string `json:"email"` + Password string `json:"password"` + NewPassword string `json:"new_password"` + MfaCode string `json:"mfa_code"` + LdapId string `json:"ldap_id"` +} + +func (o *SwitchRequest) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func SwitchRequestFromJson(data io.Reader) *SwitchRequest { + decoder := json.NewDecoder(data) + var o SwitchRequest + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} + +func (o *SwitchRequest) EmailToOAuth() bool { + return o.CurrentService == USER_AUTH_SERVICE_EMAIL && + (o.NewService == USER_AUTH_SERVICE_SAML || + o.NewService == USER_AUTH_SERVICE_GITLAB || + o.NewService == SERVICE_GOOGLE || + o.NewService == SERVICE_OFFICE365) +} + +func (o *SwitchRequest) OAuthToEmail() bool { + return (o.CurrentService == USER_AUTH_SERVICE_SAML || + o.CurrentService == USER_AUTH_SERVICE_GITLAB || + o.CurrentService == SERVICE_GOOGLE || + o.CurrentService == SERVICE_OFFICE365) && o.NewService == USER_AUTH_SERVICE_EMAIL +} + +func (o *SwitchRequest) EmailToLdap() bool { + return o.CurrentService == USER_AUTH_SERVICE_EMAIL && o.NewService == USER_AUTH_SERVICE_LDAP +} + +func (o *SwitchRequest) LdapToEmail() bool { + return o.CurrentService == USER_AUTH_SERVICE_LDAP && o.NewService == USER_AUTH_SERVICE_EMAIL +} diff --git a/vendor/github.com/mattermost/platform/model/system.go b/vendor/github.com/mattermost/platform/model/system.go index 68d542c1..e2f4283a 100644 --- a/vendor/github.com/mattermost/platform/model/system.go +++ b/vendor/github.com/mattermost/platform/model/system.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/team.go b/vendor/github.com/mattermost/platform/model/team.go index 99444bc5..4fe03f2f 100644 --- a/vendor/github.com/mattermost/platform/model/team.go +++ b/vendor/github.com/mattermost/platform/model/team.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -41,6 +41,14 @@ type Team struct { AllowOpenInvite bool `json:"allow_open_invite"` } +type TeamPatch struct { + DisplayName *string `json:"display_name"` + Description *string `json:"description"` + CompanyName *string `json:"company_name"` + InviteId *string `json:"invite_id"` + AllowOpenInvite *bool `json:"allow_open_invite"` +} + type Invites struct { Invites []map[string]string `json:"invites"` } @@ -225,7 +233,7 @@ func IsReservedTeamName(s string) bool { func IsValidTeamName(s string) bool { - if !IsValidAlphaNum(s, false) { + if !IsValidAlphaNum(s) { return false } @@ -278,3 +286,45 @@ func (o *Team) SanitizeForNotLoggedIn() { o.InviteId = "" } } + +func (t *Team) Patch(patch *TeamPatch) { + if patch.DisplayName != nil { + t.DisplayName = *patch.DisplayName + } + + if patch.Description != nil { + t.Description = *patch.Description + } + + if patch.CompanyName != nil { + t.CompanyName = *patch.CompanyName + } + + if patch.InviteId != nil { + t.InviteId = *patch.InviteId + } + + if patch.AllowOpenInvite != nil { + t.AllowOpenInvite = *patch.AllowOpenInvite + } +} + +func (t *TeamPatch) ToJson() string { + b, err := json.Marshal(t) + if err != nil { + return "" + } + + return string(b) +} + +func TeamPatchFromJson(data io.Reader) *TeamPatch { + decoder := json.NewDecoder(data) + var team TeamPatch + err := decoder.Decode(&team) + if err != nil { + return nil + } + + return &team +} diff --git a/vendor/github.com/mattermost/platform/model/team_member.go b/vendor/github.com/mattermost/platform/model/team_member.go index 36a567a4..3a0befa3 100644 --- a/vendor/github.com/mattermost/platform/model/team_member.go +++ b/vendor/github.com/mattermost/platform/model/team_member.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -31,6 +31,15 @@ func (o *TeamMember) ToJson() string { } } +func (o *TeamUnread) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + func TeamMemberFromJson(data io.Reader) *TeamMember { decoder := json.NewDecoder(data) var o TeamMember @@ -42,6 +51,17 @@ func TeamMemberFromJson(data io.Reader) *TeamMember { } } +func TeamUnreadFromJson(data io.Reader) *TeamUnread { + decoder := json.NewDecoder(data) + var o TeamUnread + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} + func TeamMembersToJson(o []*TeamMember) string { if b, err := json.Marshal(o); err != nil { return "[]" diff --git a/vendor/github.com/mattermost/platform/model/team_search.go b/vendor/github.com/mattermost/platform/model/team_search.go new file mode 100644 index 00000000..e0676022 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/team_search.go @@ -0,0 +1,35 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type TeamSearch struct { + Term string `json:"term"` +} + +// ToJson convert a TeamSearch to json string +func (c *TeamSearch) ToJson() string { + b, err := json.Marshal(c) + if err != nil { + return "" + } + + return string(b) +} + +// TeamSearchFromJson decodes the input and returns a TeamSearch +func TeamSearchFromJson(data io.Reader) *TeamSearch { + decoder := json.NewDecoder(data) + var cs TeamSearch + err := decoder.Decode(&cs) + if err == nil { + return &cs + } + + return nil +} diff --git a/vendor/github.com/mattermost/platform/model/team_stats.go b/vendor/github.com/mattermost/platform/model/team_stats.go index 9042e76d..e94ed37b 100644 --- a/vendor/github.com/mattermost/platform/model/team_stats.go +++ b/vendor/github.com/mattermost/platform/model/team_stats.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model diff --git a/vendor/github.com/mattermost/platform/model/token.go b/vendor/github.com/mattermost/platform/model/token.go new file mode 100644 index 00000000..a4d10c7f --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/token.go @@ -0,0 +1,40 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import "net/http" + +const ( + TOKEN_SIZE = 64 + MAX_TOKEN_EXIPRY_TIME = 1000 * 60 * 60 * 24 // 24 hour + TOKEN_TYPE_OAUTH = "oauth" +) + +type Token struct { + Token string + CreateAt int64 + Type string + Extra string +} + +func NewToken(tokentype, extra string) *Token { + return &Token{ + Token: NewRandomString(TOKEN_SIZE), + CreateAt: GetMillis(), + Type: tokentype, + Extra: extra, + } +} + +func (t *Token) IsValid() *AppError { + if len(t.Token) != TOKEN_SIZE { + return NewAppError("Token.IsValid", "model.token.is_valid.size", nil, "", http.StatusInternalServerError) + } + + if t.CreateAt == 0 { + return NewAppError("Token.IsValid", "model.token.is_valid.expiry", nil, "", http.StatusInternalServerError) + } + + return nil +} diff --git a/vendor/github.com/mattermost/platform/model/user.go b/vendor/github.com/mattermost/platform/model/user.go index d62ccccd..ab4b21e2 100644 --- a/vendor/github.com/mattermost/platform/model/user.go +++ b/vendor/github.com/mattermost/platform/model/user.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -10,33 +10,42 @@ import ( "net/http" "regexp" "strings" - "unicode" "unicode/utf8" "golang.org/x/crypto/bcrypt" ) const ( - USER_NOTIFY_ALL = "all" - USER_NOTIFY_MENTION = "mention" - USER_NOTIFY_NONE = "none" - DESKTOP_NOTIFY_PROP = "desktop" - MARK_UNREAD_NOTIFY_PROP = "mark_unread" - PUSH_NOTIFY_PROP = "push" - EMAIL_NOTIFY_PROP = "email" - - DEFAULT_LOCALE = "en" - USER_AUTH_SERVICE_EMAIL = "email" - USER_AUTH_SERVICE_USERNAME = "username" + ME = "me" + USER_NOTIFY_ALL = "all" + USER_NOTIFY_MENTION = "mention" + USER_NOTIFY_NONE = "none" + DESKTOP_NOTIFY_PROP = "desktop" + DESKTOP_SOUND_NOTIFY_PROP = "desktop_sound" + DESKTOP_DURATION_NOTIFY_PROP = "desktop_duration" + MARK_UNREAD_NOTIFY_PROP = "mark_unread" + PUSH_NOTIFY_PROP = "push" + PUSH_STATUS_NOTIFY_PROP = "push_status" + EMAIL_NOTIFY_PROP = "email" + CHANNEL_MENTIONS_NOTIFY_PROP = "channel" + COMMENTS_NOTIFY_PROP = "comments" + MENTION_KEYS_NOTIFY_PROP = "mention_keys" + COMMENTS_NOTIFY_NEVER = "never" + COMMENTS_NOTIFY_ROOT = "root" + COMMENTS_NOTIFY_ANY = "any" + + DEFAULT_LOCALE = "en" + USER_AUTH_SERVICE_EMAIL = "email" USER_EMAIL_MAX_LENGTH = 128 USER_NICKNAME_MAX_RUNES = 64 - USER_POSITION_MAX_RUNES = 35 + USER_POSITION_MAX_RUNES = 64 USER_FIRST_NAME_MAX_RUNES = 64 USER_LAST_NAME_MAX_RUNES = 64 USER_AUTH_DATA_MAX_LENGTH = 128 USER_NAME_MAX_LENGTH = 64 USER_NAME_MIN_LENGTH = 1 + USER_PASSWORD_MAX_LENGTH = 72 ) type User struct { @@ -68,15 +77,15 @@ type User struct { } type UserPatch struct { - Username *string `json:"username"` - Nickname *string `json:"nickname"` - FirstName *string `json:"first_name"` - LastName *string `json:"last_name"` - Position *string `json:"position"` - Email *string `json:"email"` - Props *StringMap `json:"props,omitempty"` - NotifyProps *StringMap `json:"notify_props,omitempty"` - Locale *string `json:"locale"` + Username *string `json:"username"` + Nickname *string `json:"nickname"` + FirstName *string `json:"first_name"` + LastName *string `json:"last_name"` + Position *string `json:"position"` + Email *string `json:"email"` + Props StringMap `json:"props,omitempty"` + NotifyProps StringMap `json:"notify_props,omitempty"` + Locale *string `json:"locale"` } // IsValid validates the user and returns an error if it isn't configured @@ -84,56 +93,69 @@ type UserPatch struct { func (u *User) IsValid() *AppError { if len(u.Id) != 26 { - return NewAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "", http.StatusBadRequest) + return InvalidUserError("id", "") } if u.CreateAt == 0 { - return NewAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("create_at", u.Id) } if u.UpdateAt == 0 { - return NewAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("update_at", u.Id) } if !IsValidUsername(u.Username) { - return NewAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("username", u.Id) } if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 { - return NewAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("email", u.Id) } if utf8.RuneCountInString(u.Nickname) > USER_NICKNAME_MAX_RUNES { - return NewAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("nickname", u.Id) } if utf8.RuneCountInString(u.Position) > USER_POSITION_MAX_RUNES { - return NewAppError("User.IsValid", "model.user.is_valid.position.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("position", u.Id) } if utf8.RuneCountInString(u.FirstName) > USER_FIRST_NAME_MAX_RUNES { - return NewAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("first_name", u.Id) } if utf8.RuneCountInString(u.LastName) > USER_LAST_NAME_MAX_RUNES { - return NewAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("last_name", u.Id) } if u.AuthData != nil && len(*u.AuthData) > USER_AUTH_DATA_MAX_LENGTH { - return NewAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("auth_data", u.Id) } if u.AuthData != nil && len(*u.AuthData) > 0 && len(u.AuthService) == 0 { - return NewAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("auth_data_type", u.Id) } if len(u.Password) > 0 && u.AuthData != nil && len(*u.AuthData) > 0 { - return NewAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id, http.StatusBadRequest) + return InvalidUserError("auth_data_pwd", u.Id) + } + + if len(u.Password) > USER_PASSWORD_MAX_LENGTH { + return InvalidUserError("password_limit", u.Id) } return nil } +func InvalidUserError(fieldName string, userId string) *AppError { + id := fmt.Sprintf("model.user.is_valid.%s.app_error", fieldName) + details := "" + if userId != "" { + details = "user_id=" + userId + } + return NewAppError("User.IsValid", id, nil, details, http.StatusBadRequest) +} + // PreSave will set the Id and Username if missing. It will also fill // in the CreateAt, UpdateAt times. It will also hash the password. It should // be run before saving the user to the db. @@ -143,7 +165,7 @@ func (u *User) PreSave() { } if u.Username == "" { - u.Username = "n" + NewId() + u.Username = NewId() } if u.AuthData != nil && *u.AuthData == "" { @@ -259,11 +281,11 @@ func (u *User) Patch(patch *UserPatch) { } if patch.Props != nil { - u.Props = *patch.Props + u.Props = patch.Props } if patch.NotifyProps != nil { - u.NotifyProps = *patch.NotifyProps + u.NotifyProps = patch.NotifyProps } if patch.Locale != nil { @@ -327,7 +349,6 @@ func (u *User) ClearNonProfileFields() { u.Props = StringMap{} u.NotifyProps = StringMap{} u.LastPasswordUpdate = 0 - u.LastPictureUpdate = 0 u.FailedAttempts = 0 } @@ -371,26 +392,16 @@ func (u *User) GetFullName() string { } } -func (u *User) GetDisplayName() string { - if u.Nickname != "" { - return u.Nickname - } else if fullName := u.GetFullName(); fullName != "" { - return fullName - } else { - return u.Username - } -} - -func (u *User) GetDisplayNameForPreference(nameFormat string) string { +func (u *User) GetDisplayName(nameFormat string) string { displayName := u.Username - if nameFormat == PREFERENCE_VALUE_DISPLAY_NAME_NICKNAME { + if nameFormat == SHOW_NICKNAME_FULLNAME { if u.Nickname != "" { displayName = u.Nickname } else if fullName := u.GetFullName(); fullName != "" { displayName = fullName } - } else if nameFormat == PREFERENCE_VALUE_DISPLAY_NAME_FULL { + } else if nameFormat == SHOW_FULLNAME { if fullName := u.GetFullName(); fullName != "" { displayName = fullName } @@ -445,31 +456,25 @@ func IsInRole(userRoles string, inRole string) bool { if r == inRole { return true } - } return false } func (u *User) IsSSOUser() bool { - if u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL { - return true - } - return false + return u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL } func (u *User) IsOAuthUser() bool { - if u.AuthService == USER_AUTH_SERVICE_GITLAB { - return true - } - return false + return u.AuthService == USER_AUTH_SERVICE_GITLAB } func (u *User) IsLDAPUser() bool { - if u.AuthService == USER_AUTH_SERVICE_LDAP { - return true - } - return false + return u.AuthService == USER_AUTH_SERVICE_LDAP +} + +func (u *User) IsSAMLUser() bool { + return u.AuthService == USER_AUTH_SERVICE_SAML } // UserFromJson will decode the input and return a User @@ -573,10 +578,6 @@ func IsValidUsername(s string) bool { return false } - if !unicode.IsLetter(rune(s[0])) { - return false - } - for _, restrictedUsername := range restrictedUsernames { if s == restrictedUsername { return false @@ -612,3 +613,21 @@ func CleanUsername(s string) string { return s } + +func IsValidUserNotifyLevel(notifyLevel string) bool { + return notifyLevel == CHANNEL_NOTIFY_ALL || + notifyLevel == CHANNEL_NOTIFY_MENTION || + notifyLevel == CHANNEL_NOTIFY_NONE +} + +func IsValidPushStatusNotifyLevel(notifyLevel string) bool { + return notifyLevel == STATUS_ONLINE || + notifyLevel == STATUS_AWAY || + notifyLevel == STATUS_OFFLINE +} + +func IsValidCommentsNotifyLevel(notifyLevel string) bool { + return notifyLevel == COMMENTS_NOTIFY_ANY || + notifyLevel == COMMENTS_NOTIFY_ROOT || + notifyLevel == COMMENTS_NOTIFY_NEVER +} diff --git a/vendor/github.com/mattermost/platform/model/user_access_token.go b/vendor/github.com/mattermost/platform/model/user_access_token.go new file mode 100644 index 00000000..090780fd --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/user_access_token.go @@ -0,0 +1,81 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "net/http" +) + +type UserAccessToken struct { + Id string `json:"id"` + Token string `json:"token,omitempty"` + UserId string `json:"user_id"` + Description string `json:"description"` +} + +func (t *UserAccessToken) IsValid() *AppError { + if len(t.Id) != 26 { + return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.id.app_error", nil, "", http.StatusBadRequest) + } + + if len(t.Token) != 26 { + return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.token.app_error", nil, "", http.StatusBadRequest) + } + + if len(t.UserId) != 26 { + return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.user_id.app_error", nil, "", http.StatusBadRequest) + } + + if len(t.Description) > 255 { + return NewAppError("UserAccessToken.IsValid", "model.user_access_token.is_valid.description.app_error", nil, "", http.StatusBadRequest) + } + + return nil +} + +func (t *UserAccessToken) PreSave() { + t.Id = NewId() +} + +func (t *UserAccessToken) ToJson() string { + b, err := json.Marshal(t) + if err != nil { + return "" + } else { + return string(b) + } +} + +func UserAccessTokenFromJson(data io.Reader) *UserAccessToken { + decoder := json.NewDecoder(data) + var t UserAccessToken + err := decoder.Decode(&t) + if err == nil { + return &t + } else { + return nil + } +} + +func UserAccessTokenListToJson(t []*UserAccessToken) string { + b, err := json.Marshal(t) + if err != nil { + return "" + } else { + return string(b) + } +} + +func UserAccessTokenListFromJson(data io.Reader) []*UserAccessToken { + decoder := json.NewDecoder(data) + var t []*UserAccessToken + err := decoder.Decode(&t) + if err == nil { + return t + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/user_autocomplete.go b/vendor/github.com/mattermost/platform/model/user_autocomplete.go index b7449a79..43c030b8 100644 --- a/vendor/github.com/mattermost/platform/model/user_autocomplete.go +++ b/vendor/github.com/mattermost/platform/model/user_autocomplete.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -17,6 +17,31 @@ type UserAutocompleteInTeam struct { InTeam []*User `json:"in_team"` } +type UserAutocomplete struct { + Users []*User `json:"users"` + OutOfChannel []*User `json:"out_of_channel,omitempty"` +} + +func (o *UserAutocomplete) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func UserAutocompleteFromJson(data io.Reader) *UserAutocomplete { + decoder := json.NewDecoder(data) + autocomplete := new(UserAutocomplete) + err := decoder.Decode(&autocomplete) + if err == nil { + return autocomplete + } else { + return nil + } +} + func (o *UserAutocompleteInChannel) ToJson() string { b, err := json.Marshal(o) if err != nil { diff --git a/vendor/github.com/mattermost/platform/model/user_search.go b/vendor/github.com/mattermost/platform/model/user_search.go index 4bbd2bd7..6213b16e 100644 --- a/vendor/github.com/mattermost/platform/model/user_search.go +++ b/vendor/github.com/mattermost/platform/model/user_search.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -11,9 +11,11 @@ import ( type UserSearch struct { Term string `json:"term"` TeamId string `json:"team_id"` + NotInTeamId string `json:"not_in_team_id"` InChannelId string `json:"in_channel_id"` NotInChannelId string `json:"not_in_channel_id"` AllowInactive bool `json:"allow_inactive"` + WithoutTeam bool `json:"without_team"` } // ToJson convert a User to a json string diff --git a/vendor/github.com/mattermost/platform/model/utils.go b/vendor/github.com/mattermost/platform/model/utils.go index 08809a47..090644ec 100644 --- a/vendor/github.com/mattermost/platform/model/utils.go +++ b/vendor/github.com/mattermost/platform/model/utils.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -11,9 +11,11 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/mail" "net/url" "regexp" + "strconv" "strings" "time" @@ -31,7 +33,6 @@ const ( type StringInterface map[string]interface{} type StringMap map[string]string type StringArray []string -type EncryptStringMap map[string]string type AppError struct { Id string `json:"id"` @@ -156,6 +157,15 @@ func MapToJson(objmap map[string]string) string { } } +// MapToJson converts a map to a json string +func MapBoolToJson(objmap map[string]bool) string { + if b, err := json.Marshal(objmap); err != nil { + return "" + } else { + return string(b) + } +} + // MapFromJson will decode the key/value pair map func MapFromJson(data io.Reader) map[string]string { decoder := json.NewDecoder(data) @@ -168,6 +178,18 @@ func MapFromJson(data io.Reader) map[string]string { } } +// MapFromJson will decode the key/value pair map +func MapBoolFromJson(data io.Reader) map[string]bool { + decoder := json.NewDecoder(data) + + var objmap map[string]bool + if err := decoder.Decode(&objmap); err != nil { + return make(map[string]bool) + } else { + return objmap + } +} + func ArrayToJson(objmap []string) string { if b, err := json.Marshal(objmap); err != nil { return "" @@ -243,6 +265,23 @@ func StringFromJson(data io.Reader) string { } } +func GetServerIpAddress() string { + if addrs, err := net.InterfaceAddrs(); err != nil { + return "" + } else { + for _, addr := range addrs { + + if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() { + if ip.IP.To4() != nil { + return ip.IP.String() + } + } + } + } + + return "" +} + func IsLower(s string) bool { if strings.ToLower(s) == s { return true @@ -276,7 +315,7 @@ var reservedName = []string{ func IsValidChannelIdentifier(s string) bool { - if !IsValidAlphaNum(s, true) { + if !IsValidAlphaNumHyphenUnderscore(s, true) { return false } @@ -287,22 +326,20 @@ func IsValidChannelIdentifier(s string) bool { return true } -var validAlphaNumUnderscore = regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`) -var validAlphaNum = regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`) +func IsValidAlphaNum(s string) bool { + validAlphaNum := regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`) -func IsValidAlphaNum(s string, allowUnderscores bool) bool { - var match bool - if allowUnderscores { - match = validAlphaNumUnderscore.MatchString(s) - } else { - match = validAlphaNum.MatchString(s) - } + return validAlphaNum.MatchString(s) +} - if !match { - return false +func IsValidAlphaNumHyphenUnderscore(s string, withFormat bool) bool { + if withFormat { + validAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`) + return validAlphaNumHyphenUnderscore.MatchString(s) } - return true + validSimpleAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`) + return validSimpleAlphaNumHyphenUnderscore.MatchString(s) } func Etag(parts ...interface{}) string { @@ -382,8 +419,6 @@ func ClearMentionTags(post string) string { var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`) var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`) -var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true, '^': true, '#': true, '$': true, '&': true} - func IsValidHttpUrl(rawUrl string) bool { if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 { return false @@ -445,3 +480,15 @@ func IsValidWebsocketUrl(rawUrl string) bool { return true } + +func IsValidTrueOrFalseString(value string) bool { + return value == "true" || value == "false" +} + +func IsValidNumberString(value string) bool { + if _, err := strconv.Atoi(value); err != nil { + return false + } + + return true +} diff --git a/vendor/github.com/mattermost/platform/model/version.go b/vendor/github.com/mattermost/platform/model/version.go index 32b1c3f5..b08af46b 100644 --- a/vendor/github.com/mattermost/platform/model/version.go +++ b/vendor/github.com/mattermost/platform/model/version.go @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -13,6 +13,11 @@ import ( // It should be maitained in chronological order with most current // release at the front of the list. var versions = []string{ + "4.1.0", + "4.0.0", + "3.10.0", + "3.9.0", + "3.8.0", "3.7.0", "3.6.0", "3.5.0", diff --git a/vendor/github.com/mattermost/platform/model/webrtc.go b/vendor/github.com/mattermost/platform/model/webrtc.go index e746d62a..fa15a4b7 100644 --- a/vendor/github.com/mattermost/platform/model/webrtc.go +++ b/vendor/github.com/mattermost/platform/model/webrtc.go @@ -1,3 +1,6 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + package model import ( @@ -5,6 +8,15 @@ import ( "io" ) +type WebrtcInfoResponse struct { + Token string `json:"token"` + GatewayUrl string `json:"gateway_url"` + StunUri string `json:"stun_uri,omitempty"` + TurnUri string `json:"turn_uri,omitempty"` + TurnPassword string `json:"turn_password,omitempty"` + TurnUsername string `json:"turn_username,omitempty"` +} + type GatewayResponse struct { Status string `json:"janus"` } @@ -19,3 +31,23 @@ func GatewayResponseFromJson(data io.Reader) *GatewayResponse { return nil } } + +func (o *WebrtcInfoResponse) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func WebrtcInfoResponseFromJson(data io.Reader) *WebrtcInfoResponse { + decoder := json.NewDecoder(data) + var o WebrtcInfoResponse + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/websocket_client.go b/vendor/github.com/mattermost/platform/model/websocket_client.go index 083fe110..2fa405f3 100644 --- a/vendor/github.com/mattermost/platform/model/websocket_client.go +++ b/vendor/github.com/mattermost/platform/model/websocket_client.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -15,6 +15,7 @@ const ( type WebSocketClient struct { Url string // The location of the server like "ws://localhost:8065" ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3" + ConnectUrl string // The websocket URL to connect to like "ws://localhost:8065/api/v3/path/to/websocket" Conn *websocket.Conn // The WebSocket connection AuthToken string // The token used to open the WebSocket Sequence int64 // The ever-incrementing sequence attached to each WebSocket action @@ -34,6 +35,32 @@ func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { client := &WebSocketClient{ url, url + API_URL_SUFFIX_V3, + url + API_URL_SUFFIX_V3 + "/users/websocket", + conn, + authToken, + 1, + make(chan *WebSocketEvent, 100), + make(chan *WebSocketResponse, 100), + nil, + } + + client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken}) + + return client, nil +} + +// NewWebSocketClient4 constructs a new WebSocket client with convienence +// methods for talking to the server. Uses the v4 endpoint. +func NewWebSocketClient4(url, authToken string) (*WebSocketClient, *AppError) { + conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/websocket", nil) + if err != nil { + return nil, NewLocAppError("NewWebSocketClient4", "model.websocket_client.connect_fail.app_error", nil, err.Error()) + } + + client := &WebSocketClient{ + url, + url + API_URL_SUFFIX, + url + API_URL_SUFFIX + "/websocket", conn, authToken, 1, @@ -49,9 +76,9 @@ func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { func (wsc *WebSocketClient) Connect() *AppError { var err error - wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ApiUrl+"/users/websocket", nil) + wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ConnectUrl, nil) if err != nil { - return NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error()) + return NewLocAppError("Connect", "model.websocket_client.connect_fail.app_error", nil, err.Error()) } wsc.EventChannel = make(chan *WebSocketEvent, 100) diff --git a/vendor/github.com/mattermost/platform/model/websocket_message.go b/vendor/github.com/mattermost/platform/model/websocket_message.go index 23820470..6b8c0342 100644 --- a/vendor/github.com/mattermost/platform/model/websocket_message.go +++ b/vendor/github.com/mattermost/platform/model/websocket_message.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model @@ -9,35 +9,41 @@ import ( ) const ( - WEBSOCKET_EVENT_TYPING = "typing" - WEBSOCKET_EVENT_POSTED = "posted" - WEBSOCKET_EVENT_POST_EDITED = "post_edited" - WEBSOCKET_EVENT_POST_DELETED = "post_deleted" - WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted" - WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created" - WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" - WEBSOCKET_EVENT_GROUP_ADDED = "group_added" - WEBSOCKET_EVENT_NEW_USER = "new_user" - WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" - WEBSOCKET_EVENT_UPDATE_TEAM = "update_team" - WEBSOCKET_EVENT_USER_ADDED = "user_added" - WEBSOCKET_EVENT_USER_UPDATED = "user_updated" - WEBSOCKET_EVENT_USER_REMOVED = "user_removed" - WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed" - WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message" - WEBSOCKET_EVENT_STATUS_CHANGE = "status_change" - WEBSOCKET_EVENT_HELLO = "hello" - WEBSOCKET_EVENT_WEBRTC = "webrtc" - WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge" - WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added" - WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed" + WEBSOCKET_EVENT_TYPING = "typing" + WEBSOCKET_EVENT_POSTED = "posted" + WEBSOCKET_EVENT_POST_EDITED = "post_edited" + WEBSOCKET_EVENT_POST_DELETED = "post_deleted" + WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted" + WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created" + WEBSOCKET_EVENT_CHANNEL_UPDATED = "channel_updated" + WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" + WEBSOCKET_EVENT_GROUP_ADDED = "group_added" + WEBSOCKET_EVENT_NEW_USER = "new_user" + WEBSOCKET_EVENT_ADDED_TO_TEAM = "added_to_team" + WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" + WEBSOCKET_EVENT_UPDATE_TEAM = "update_team" + WEBSOCKET_EVENT_USER_ADDED = "user_added" + WEBSOCKET_EVENT_USER_UPDATED = "user_updated" + WEBSOCKET_EVENT_MEMBERROLE_UPDATED = "memberrole_updated" + WEBSOCKET_EVENT_USER_REMOVED = "user_removed" + WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed" + WEBSOCKET_EVENT_PREFERENCES_CHANGED = "preferences_changed" + WEBSOCKET_EVENT_PREFERENCES_DELETED = "preferences_deleted" + WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message" + WEBSOCKET_EVENT_STATUS_CHANGE = "status_change" + WEBSOCKET_EVENT_HELLO = "hello" + WEBSOCKET_EVENT_WEBRTC = "webrtc" + WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge" + WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added" + WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed" + WEBSOCKET_EVENT_RESPONSE = "response" + WEBSOCKET_EVENT_EMOJI_ADDED = "emoji_added" + WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed" ) type WebSocketMessage interface { ToJson() string IsValid() bool - DoPreComputeJson() - GetPreComputeJson() []byte EventType() string } @@ -49,10 +55,10 @@ type WebsocketBroadcast struct { } type WebSocketEvent struct { - Event string `json:"event"` - Data map[string]interface{} `json:"data"` - Broadcast *WebsocketBroadcast `json:"broadcast"` - PreComputeJson []byte `json:"-"` + Event string `json:"event"` + Data map[string]interface{} `json:"data"` + Broadcast *WebsocketBroadcast `json:"broadcast"` + Sequence int64 `json:"seq"` } func (m *WebSocketEvent) Add(key string, value interface{}) { @@ -72,19 +78,6 @@ func (o *WebSocketEvent) EventType() string { return o.Event } -func (o *WebSocketEvent) DoPreComputeJson() { - b, err := json.Marshal(o) - if err != nil { - o.PreComputeJson = []byte("") - } else { - o.PreComputeJson = b - } -} - -func (o *WebSocketEvent) GetPreComputeJson() []byte { - return o.PreComputeJson -} - func (o *WebSocketEvent) ToJson() string { b, err := json.Marshal(o) if err != nil { @@ -106,11 +99,10 @@ func WebSocketEventFromJson(data io.Reader) *WebSocketEvent { } type WebSocketResponse struct { - Status string `json:"status"` - SeqReply int64 `json:"seq_reply,omitempty"` - Data map[string]interface{} `json:"data,omitempty"` - Error *AppError `json:"error,omitempty"` - PreComputeJson []byte `json:"-"` + Status string `json:"status"` + SeqReply int64 `json:"seq_reply,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` + Error *AppError `json:"error,omitempty"` } func (m *WebSocketResponse) Add(key string, value interface{}) { @@ -130,7 +122,7 @@ func (o *WebSocketResponse) IsValid() bool { } func (o *WebSocketResponse) EventType() string { - return "" + return WEBSOCKET_EVENT_RESPONSE } func (o *WebSocketResponse) ToJson() string { @@ -142,19 +134,6 @@ func (o *WebSocketResponse) ToJson() string { } } -func (o *WebSocketResponse) DoPreComputeJson() { - b, err := json.Marshal(o) - if err != nil { - o.PreComputeJson = []byte("") - } else { - o.PreComputeJson = b - } -} - -func (o *WebSocketResponse) GetPreComputeJson() []byte { - return o.PreComputeJson -} - func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse { decoder := json.NewDecoder(data) var o WebSocketResponse diff --git a/vendor/github.com/mattermost/platform/model/websocket_request.go b/vendor/github.com/mattermost/platform/model/websocket_request.go index d0f35f68..7dc0b433 100644 --- a/vendor/github.com/mattermost/platform/model/websocket_request.go +++ b/vendor/github.com/mattermost/platform/model/websocket_request.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package model |