Update mattermost vendor (3.7 => 4.1)

This commit is contained in:
Wim 2017-08-16 23:37:37 +02:00
parent f6297ebbb0
commit b963f83c6a
147 changed files with 10086 additions and 1099 deletions

View File

@ -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 einterfaces
@ -6,7 +6,7 @@ package einterfaces
import "github.com/mattermost/platform/model"
type AccountMigrationInterface interface {
MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string) *model.AppError
MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string, force bool) *model.AppError
}
var theAccountMigrationInterface AccountMigrationInterface

View File

@ -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 einterfaces

View File

@ -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 einterfaces
@ -7,26 +7,19 @@ import (
"github.com/mattermost/platform/model"
)
type ClusterMessageHandler func(msg *model.ClusterMessage)
type ClusterInterface interface {
StartInterNodeCommunication()
StopInterNodeCommunication()
GetClusterInfos() []*model.ClusterInfo
GetClusterStats() ([]*model.ClusterStats, *model.AppError)
ClearSessionCacheForUser(userId string)
InvalidateCacheForUser(userId string)
InvalidateCacheForChannel(channelId string)
InvalidateCacheForChannelByName(teamId, name string)
InvalidateCacheForChannelMembers(channelId string)
InvalidateCacheForChannelMembersNotifyProps(channelId string)
InvalidateCacheForChannelPosts(channelId string)
InvalidateCacheForWebhook(webhookId string)
InvalidateCacheForReactions(postId string)
Publish(event *model.WebSocketEvent)
UpdateStatus(status *model.Status)
GetLogs() ([]string, *model.AppError)
RegisterClusterMessageHandler(event string, crm ClusterMessageHandler)
GetClusterId() string
GetClusterInfos() []*model.ClusterInfo
SendClusterMessage(cluster *model.ClusterMessage)
NotifyMsg(buf []byte)
GetClusterStats() ([]*model.ClusterStats, *model.AppError)
GetLogs(page, perPage int) ([]string, *model.AppError)
ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError
InvalidateAllCaches() *model.AppError
}
var theClusterInterface ClusterInterface

View File

@ -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 einterfaces

View File

@ -0,0 +1,25 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package einterfaces
import "github.com/mattermost/platform/model"
type ElasticsearchInterface interface {
Start() *model.AppError
IndexPost(post *model.Post, teamId string) *model.AppError
SearchPosts(channels *model.ChannelList, searchParams []*model.SearchParams) ([]string, *model.AppError)
DeletePost(post *model.Post) *model.AppError
TestConfig(cfg *model.Config) *model.AppError
PurgeIndexes() *model.AppError
}
var theElasticsearchInterface ElasticsearchInterface
func RegisterElasticsearchInterface(newInterface ElasticsearchInterface) {
theElasticsearchInterface = newInterface
}
func GetElasticsearchInterface() ElasticsearchInterface {
return theElasticsearchInterface
}

View File

@ -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 einterfaces

View File

@ -0,0 +1,23 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package jobs
import (
"github.com/mattermost/platform/model"
)
type DataRetentionInterface interface {
MakeWorker() model.Worker
MakeScheduler() model.Scheduler
}
var theDataRetentionInterface DataRetentionInterface
func RegisterDataRetentionInterface(newInterface DataRetentionInterface) {
theDataRetentionInterface = newInterface
}
func GetDataRetentionInterface() DataRetentionInterface {
return theDataRetentionInterface
}

View File

@ -0,0 +1,22 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package jobs
import (
"github.com/mattermost/platform/model"
)
type ElasticsearchIndexerInterface interface {
MakeWorker() model.Worker
}
var theElasticsearchIndexerInterface ElasticsearchIndexerInterface
func RegisterElasticsearchIndexerInterface(newInterface ElasticsearchIndexerInterface) {
theElasticsearchIndexerInterface = newInterface
}
func GetElasticsearchIndexerInterface() ElasticsearchIndexerInterface {
return theElasticsearchIndexerInterface
}

View File

@ -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 einterfaces

View File

@ -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 einterfaces
@ -33,6 +33,7 @@ type MetricsInterface interface {
IncrementMemCacheHitCounterSession()
IncrementWebsocketEvent(eventType string)
IncrementWebSocketBroadcast(eventType string)
AddMemCacheHitCounter(cacheName string, amount float64)
AddMemCacheMissCounter(cacheName string, amount float64)

View File

@ -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 einterfaces

View File

@ -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 einterfaces

View File

@ -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 einterfaces
@ -10,7 +10,7 @@ import (
type SamlInterface interface {
ConfigureSP() *model.AppError
BuildRequest(relayState string) (*model.SamlAuthRequest, *model.AppError)
DoLogin(encodedXML string, relayState map[string]string, siteURL string) (*model.User, *model.AppError)
DoLogin(encodedXML string, relayState map[string]string) (*model.User, *model.AppError)
GetMetadata() (string, *model.AppError)
}

View File

@ -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, "")
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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...,
),

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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
}

View File

@ -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"`
IpAddress string `json:"ipaddress"`
Hostname string `json:"hostname"`
LastSuccessfulPing int64 `json:"last_ping"`
IsAlive bool `json:"is_alive"`
}
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 ""

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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 {
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 {

View File

@ -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
}

View File

@ -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

View File

@ -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,

View File

@ -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_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 = ""
@ -80,6 +89,9 @@ const (
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 = ""
@ -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
ClusterName *string
OverrideHostname *string
UseIpAddress *bool
UseExperimentalGossip *bool
ReadOnlyConfig *bool
GossipPort *int
StreamingPort *int
}
type MetricsSettings struct {
@ -184,10 +218,12 @@ type SqlSettings struct {
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,6 +261,8 @@ type FileSettings struct {
AmazonS3Region string
AmazonS3Endpoint string
AmazonS3SSL *bool
AmazonS3SignV2 *bool
AmazonS3SSE *bool
}
type EmailSettings struct {
@ -239,19 +274,21 @@ type EmailSettings struct {
FeedbackName string
FeedbackEmail string
FeedbackOrganization *string
EnableSMTPAuth *bool
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
SkipServerCertificateVerification *bool
EmailNotificationContentsType *string
}
type RateLimitSettings struct {
@ -274,9 +311,20 @@ type SupportSettings struct {
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
@ -295,9 +343,11 @@ type TeamSettings struct {
RestrictPrivateChannelCreation *string
RestrictPublicChannelDeletion *string
RestrictPrivateChannelDeletion *string
RestrictPrivateChannelManageMembers *string
UserStatusAwayTimeout *int64
MaxChannelsPerTeam *int64
MaxNotificationsPerChannel *int64
TeammateNameDisplay *string
}
type LdapSettings struct {
@ -389,6 +439,30 @@ 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
@ -400,6 +474,7 @@ type Config struct {
RateLimitSettings RateLimitSettings
PrivacySettings PrivacySettings
SupportSettings SupportSettings
AnnouncementSettings AnnouncementSettings
GitLabSettings SSOSettings
GoogleSettings SSOSettings
Office365Settings SSOSettings
@ -412,6 +487,10 @@ type Config struct {
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,14 +712,22 @@ 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.
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.
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 {
o.TeamSettings.RestrictPublicChannelDeletion = new(string)
@ -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() {

View File

@ -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, "")
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
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"
)
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"`
}
var tasks = make(map[string]*ScheduledTask)
func addTask(task *ScheduledTask) {
tasks[task.Name] = 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)
}
func removeTaskByName(name string) {
delete(tasks, name)
if j.CreateAt == 0 {
return NewAppError("Job.IsValid", "model.job.is_valid.create_at.app_error", nil, "id="+j.Id, http.StatusBadRequest)
}
func GetTaskByName(name string) *ScheduledTask {
if task, ok := tasks[name]; ok {
return task
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)
}
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 nil
}
func GetAllTasks() *map[string]*ScheduledTask {
return &tasks
func (js *Job) ToJson() string {
if b, err := json.Marshal(js); err != nil {
return ""
} else {
return string(b)
}
}
func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask {
task := &ScheduledTask{
Name: name,
Interval: timeToExecution,
Recurring: false,
function: function,
func JobFromJson(data io.Reader) *Job {
var job Job
if err := json.NewDecoder(data).Decode(&job); err == nil {
return &job
} else {
return nil
}
}
taskRunner := func() {
go task.function()
removeTaskByName(task.Name)
func JobsToJson(jobs []*Job) string {
if b, err := json.Marshal(jobs); err != nil {
return ""
} else {
return string(b)
}
}
task.timer = time.AfterFunc(timeToExecution, taskRunner)
addTask(task)
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 CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask {
task := &ScheduledTask{
Name: name,
Interval: interval,
Recurring: true,
function: function,
func (js *Job) DataToJson() string {
if b, err := json.Marshal(js.Data); err != nil {
return ""
} else {
return string(b)
}
}
taskRecurer := func() {
go task.function()
task.timer.Reset(task.Interval)
type Worker interface {
Run()
Stop()
JobChannel() chan<- Job
}
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,
)
type Scheduler interface {
Run()
Stop()
}

View File

@ -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

View File

@ -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
@ -49,6 +49,10 @@ type Features struct {
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"`
}
@ -66,6 +70,8 @@ func (f *Features) ToMap() map[string]interface{} {
"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 {

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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

View File

@ -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_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 {

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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,
)
}

View File

@ -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,
})

View File

@ -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

View File

@ -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,10 +11,16 @@ import (
const (
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()
}
if me.Token == "" {
me.Token = NewId()
}
me.CreateAt = GetMillis()
me.LastActivityAt = me.CreateAt

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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 "[]"

View File

@ -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
}

View File

@ -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

40
vendor/github.com/mattermost/platform/model/token.go generated vendored Normal file
View File

@ -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
}

View File

@ -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 (
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_AUTH_SERVICE_USERNAME = "username"
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 {
@ -74,8 +83,8 @@ type UserPatch struct {
LastName *string `json:"last_name"`
Position *string `json:"position"`
Email *string `json:"email"`
Props *StringMap `json:"props,omitempty"`
NotifyProps *StringMap `json:"notify_props,omitempty"`
Props StringMap `json:"props,omitempty"`
NotifyProps StringMap `json:"notify_props,omitempty"`
Locale *string `json:"locale"`
}
@ -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 u.AuthService == USER_AUTH_SERVICE_LDAP
}
return false
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
}

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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+_~\-\.%=&amp;]*)?)?(?:#[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
}

View File

@ -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",

View File

@ -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
}
}

View File

@ -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)

View File

@ -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,15 +15,20 @@ const (
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"
@ -31,13 +36,14 @@ const (
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
}
@ -52,7 +58,7 @@ type WebSocketEvent struct {
Event string `json:"event"`
Data map[string]interface{} `json:"data"`
Broadcast *WebsocketBroadcast `json:"broadcast"`
PreComputeJson []byte `json:"-"`
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 {
@ -110,7 +103,6 @@ type WebSocketResponse struct {
SeqReply int64 `json:"seq_reply,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
Error *AppError `json:"error,omitempty"`
PreComputeJson []byte `json:"-"`
}
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

View File

@ -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

View File

@ -66,8 +66,9 @@ type Dialer struct {
// HandshakeTimeout specifies the duration for the handshake to complete.
HandshakeTimeout time.Duration
// Input and output buffer sizes. If the buffer size is zero, then a
// default value of 4096 is used.
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
// size is zero, then a useful default size is used. The I/O buffer sizes
// do not limit the size of the messages that can be sent or received.
ReadBufferSize, WriteBufferSize int
// Subprotocols specifies the client's requested subprotocols.
@ -368,7 +369,7 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
return nil, resp, ErrBadHandshake
}
for _, ext := range parseExtensions(req.Header) {
for _, ext := range parseExtensions(resp.Header) {
if ext[""] != "permessage-deflate" {
continue
}
@ -389,32 +390,3 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
netConn = nil // to avoid close in defer.
return conn, resp, nil
}
// cloneTLSConfig clones all public fields except the fields
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
// config in active use.
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
if cfg == nil {
return &tls.Config{}
}
return &tls.Config{
Rand: cfg.Rand,
Time: cfg.Time,
Certificates: cfg.Certificates,
NameToCertificate: cfg.NameToCertificate,
GetCertificate: cfg.GetCertificate,
RootCAs: cfg.RootCAs,
NextProtos: cfg.NextProtos,
ServerName: cfg.ServerName,
ClientAuth: cfg.ClientAuth,
ClientCAs: cfg.ClientCAs,
InsecureSkipVerify: cfg.InsecureSkipVerify,
CipherSuites: cfg.CipherSuites,
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
ClientSessionCache: cfg.ClientSessionCache,
MinVersion: cfg.MinVersion,
MaxVersion: cfg.MaxVersion,
CurvePreferences: cfg.CurvePreferences,
}
}

View File

@ -0,0 +1,16 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.8
package websocket
import "crypto/tls"
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
if cfg == nil {
return &tls.Config{}
}
return cfg.Clone()
}

View File

@ -0,0 +1,38 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.8
package websocket
import "crypto/tls"
// cloneTLSConfig clones all public fields except the fields
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
// config in active use.
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
if cfg == nil {
return &tls.Config{}
}
return &tls.Config{
Rand: cfg.Rand,
Time: cfg.Time,
Certificates: cfg.Certificates,
NameToCertificate: cfg.NameToCertificate,
GetCertificate: cfg.GetCertificate,
RootCAs: cfg.RootCAs,
NextProtos: cfg.NextProtos,
ServerName: cfg.ServerName,
ClientAuth: cfg.ClientAuth,
ClientCAs: cfg.ClientCAs,
InsecureSkipVerify: cfg.InsecureSkipVerify,
CipherSuites: cfg.CipherSuites,
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
ClientSessionCache: cfg.ClientSessionCache,
MinVersion: cfg.MinVersion,
MaxVersion: cfg.MaxVersion,
CurvePreferences: cfg.CurvePreferences,
}
}

View File

@ -12,31 +12,45 @@ import (
"sync"
)
var (
flateWriterPool = sync.Pool{}
const (
minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
maxCompressionLevel = flate.BestCompression
defaultCompressionLevel = 1
)
func decompressNoContextTakeover(r io.Reader) io.Reader {
var (
flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
flateReaderPool = sync.Pool{New: func() interface{} {
return flate.NewReader(nil)
}}
)
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
const tail =
// Add four bytes as specified in RFC
"\x00\x00\xff\xff" +
// Add final block to squelch unexpected EOF error from flate reader.
"\x01\x00\x00\xff\xff"
return flate.NewReader(io.MultiReader(r, strings.NewReader(tail)))
fr, _ := flateReaderPool.Get().(io.ReadCloser)
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
return &flateReadWrapper{fr}
}
func compressNoContextTakeover(w io.WriteCloser) (io.WriteCloser, error) {
func isValidCompressionLevel(level int) bool {
return minCompressionLevel <= level && level <= maxCompressionLevel
}
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
p := &flateWriterPools[level-minCompressionLevel]
tw := &truncWriter{w: w}
i := flateWriterPool.Get()
var fw *flate.Writer
var err error
if i == nil {
fw, err = flate.NewWriter(tw, 3)
fw, _ := p.Get().(*flate.Writer)
if fw == nil {
fw, _ = flate.NewWriter(tw, level)
} else {
fw = i.(*flate.Writer)
fw.Reset(tw)
}
return &flateWrapper{fw: fw, tw: tw}, err
return &flateWriteWrapper{fw: fw, tw: tw, p: p}
}
// truncWriter is an io.Writer that writes all but the last four bytes of the
@ -75,24 +89,25 @@ func (w *truncWriter) Write(p []byte) (int, error) {
return n + nn, err
}
type flateWrapper struct {
type flateWriteWrapper struct {
fw *flate.Writer
tw *truncWriter
p *sync.Pool
}
func (w *flateWrapper) Write(p []byte) (int, error) {
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
if w.fw == nil {
return 0, errWriteClosed
}
return w.fw.Write(p)
}
func (w *flateWrapper) Close() error {
func (w *flateWriteWrapper) Close() error {
if w.fw == nil {
return errWriteClosed
}
err1 := w.fw.Flush()
flateWriterPool.Put(w.fw)
w.p.Put(w.fw)
w.fw = nil
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
@ -103,3 +118,31 @@ func (w *flateWrapper) Close() error {
}
return err2
}
type flateReadWrapper struct {
fr io.ReadCloser
}
func (r *flateReadWrapper) Read(p []byte) (int, error) {
if r.fr == nil {
return 0, io.ErrClosedPipe
}
n, err := r.fr.Read(p)
if err == io.EOF {
// Preemptively place the reader back in the pool. This helps with
// scenarios where the application does not call NextReader() soon after
// this final read.
r.Close()
}
return n, err
}
func (r *flateReadWrapper) Close() error {
if r.fr == nil {
return io.ErrClosedPipe
}
err := r.fr.Close()
flateReaderPool.Put(r.fr)
r.fr = nil
return err
}

View File

@ -10,6 +10,7 @@ import (
"errors"
"io"
"io/ioutil"
"math/rand"
"net"
"strconv"
"sync"
@ -180,6 +181,11 @@ var (
errInvalidControlFrame = errors.New("websocket: invalid control frame")
)
func newMaskKey() [4]byte {
n := rand.Uint32()
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
}
func hideTempErr(err error) error {
if e, ok := err.(net.Error); ok && e.Temporary() {
err = &netError{msg: e.Error(), timeout: e.Timeout()}
@ -235,9 +241,11 @@ type Conn struct {
writeErr error
enableWriteCompression bool
newCompressionWriter func(io.WriteCloser) (io.WriteCloser, error)
compressionLevel int
newCompressionWriter func(io.WriteCloser, int) io.WriteCloser
// Read fields
reader io.ReadCloser // the current reader returned to the application
readErr error
br *bufio.Reader
readRemaining int64 // bytes remaining in current frame.
@ -253,31 +261,76 @@ type Conn struct {
messageReader *messageReader // the current low-level reader
readDecompress bool // whether last read frame had RSV1 set
newDecompressionReader func(io.Reader) io.Reader
newDecompressionReader func(io.Reader) io.ReadCloser
}
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil)
}
type writeHook struct {
p []byte
}
func (wh *writeHook) Write(p []byte) (int, error) {
wh.p = p
return len(p), nil
}
func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn {
mu := make(chan bool, 1)
mu <- true
var br *bufio.Reader
if readBufferSize == 0 && brw != nil && brw.Reader != nil {
// Reuse the supplied bufio.Reader if the buffer has a useful size.
// This code assumes that peek on a reader returns
// bufio.Reader.buf[:0].
brw.Reader.Reset(conn)
if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 {
br = brw.Reader
}
}
if br == nil {
if readBufferSize == 0 {
readBufferSize = defaultReadBufferSize
}
if readBufferSize < maxControlFramePayloadSize {
readBufferSize = maxControlFramePayloadSize
}
br = bufio.NewReaderSize(conn, readBufferSize)
}
var writeBuf []byte
if writeBufferSize == 0 && brw != nil && brw.Writer != nil {
// Use the bufio.Writer's buffer if the buffer has a useful size. This
// code assumes that bufio.Writer.buf[:1] is passed to the
// bufio.Writer's underlying writer.
var wh writeHook
brw.Writer.Reset(&wh)
brw.Writer.WriteByte(0)
brw.Flush()
if cap(wh.p) >= maxFrameHeaderSize+256 {
writeBuf = wh.p[:cap(wh.p)]
}
}
if writeBuf == nil {
if writeBufferSize == 0 {
writeBufferSize = defaultWriteBufferSize
}
writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize)
}
c := &Conn{
isServer: isServer,
br: bufio.NewReaderSize(conn, readBufferSize),
br: br,
conn: conn,
mu: mu,
readFinal: true,
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
writeBuf: writeBuf,
enableWriteCompression: true,
compressionLevel: defaultCompressionLevel,
}
c.SetCloseHandler(nil)
c.SetPingHandler(nil)
@ -443,11 +496,7 @@ func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
}
c.writer = mw
if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
w, err := c.newCompressionWriter(c.writer)
if err != nil {
c.writer = nil
return nil, err
}
w := c.newCompressionWriter(c.writer, c.compressionLevel)
mw.compress = true
c.writer = w
}
@ -654,12 +703,33 @@ func (w *messageWriter) Close() error {
return nil
}
// WritePreparedMessage writes prepared message into connection.
func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {
frameType, frameData, err := pm.frame(prepareKey{
isServer: c.isServer,
compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),
compressionLevel: c.compressionLevel,
})
if err != nil {
return err
}
if c.isWriting {
panic("concurrent write to websocket connection")
}
c.isWriting = true
err = c.write(frameType, c.writeDeadline, frameData, nil)
if !c.isWriting {
panic("concurrent write to websocket connection")
}
c.isWriting = false
return err
}
// WriteMessage is a helper method for getting a writer using NextWriter,
// writing the message and closing the writer.
func (c *Conn) WriteMessage(messageType int, data []byte) error {
if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
// Fast path with no allocations and single frame.
if err := c.prepWrite(messageType); err != nil {
@ -855,6 +925,11 @@ func (c *Conn) handleProtocolError(message string) error {
// permanent. Once this method returns a non-nil error, all subsequent calls to
// this method return the same error.
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
// Close previous reader, only relevant for decompression.
if c.reader != nil {
c.reader.Close()
c.reader = nil
}
c.messageReader = nil
c.readLength = 0
@ -867,11 +942,11 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
}
if frameType == TextMessage || frameType == BinaryMessage {
c.messageReader = &messageReader{c}
var r io.Reader = c.messageReader
c.reader = c.messageReader
if c.readDecompress {
r = c.newDecompressionReader(r)
c.reader = c.newDecompressionReader(c.reader)
}
return frameType, r, nil
return frameType, c.reader, nil
}
}
@ -933,6 +1008,10 @@ func (r *messageReader) Read(b []byte) (int, error) {
return 0, err
}
func (r *messageReader) Close() error {
return nil
}
// ReadMessage is a helper method for getting a reader using NextReader and
// reading from that reader to a buffer.
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
@ -969,6 +1048,15 @@ func (c *Conn) CloseHandler() func(code int, text string) error {
// The code argument to h is the received close code or CloseNoStatusReceived
// if the close message is empty. The default close handler sends a close frame
// back to the peer.
//
// The application must read the connection to process close messages as
// described in the section on Control Frames above.
//
// The connection read methods return a CloseError when a close frame is
// received. Most applications should handle close messages as part of their
// normal error handling. Applications should only set a close handler when the
// application must perform some action before sending a close frame back to
// the peer.
func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
if h == nil {
h = func(code int, text string) error {
@ -991,6 +1079,9 @@ func (c *Conn) PingHandler() func(appData string) error {
// SetPingHandler sets the handler for ping messages received from the peer.
// The appData argument to h is the PING frame application data. The default
// ping handler sends a pong to the peer.
//
// The application must read the connection to process ping messages as
// described in the section on Control Frames above.
func (c *Conn) SetPingHandler(h func(appData string) error) {
if h == nil {
h = func(message string) error {
@ -1014,6 +1105,9 @@ func (c *Conn) PongHandler() func(appData string) error {
// SetPongHandler sets the handler for pong messages received from the peer.
// The appData argument to h is the PONG frame application data. The default
// pong handler does nothing.
//
// The application must read the connection to process ping messages as
// described in the section on Control Frames above.
func (c *Conn) SetPongHandler(h func(appData string) error) {
if h == nil {
h = func(string) error { return nil }
@ -1034,6 +1128,18 @@ func (c *Conn) EnableWriteCompression(enable bool) {
c.enableWriteCompression = enable
}
// SetCompressionLevel sets the flate compression level for subsequent text and
// binary messages. This function is a noop if compression was not negotiated
// with the peer. See the compress/flate package for a description of
// compression levels.
func (c *Conn) SetCompressionLevel(level int) error {
if !isValidCompressionLevel(level) {
return errors.New("websocket: invalid compression level")
}
c.compressionLevel = level
return nil
}
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
func FormatCloseMessage(closeCode int, text string) []byte {
buf := make([]byte, 2+len(text))

View File

@ -118,9 +118,10 @@
//
// Applications are responsible for ensuring that no more than one goroutine
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
// WriteJSON) concurrently and that no more than one goroutine calls the read
// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler,
// SetPingHandler) concurrently.
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
// that no more than one goroutine calls the read methods (NextReader,
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
// concurrently.
//
// The Close and WriteControl methods can be called concurrently with all other
// methods.
@ -150,19 +151,25 @@
// application's responsibility to check the Origin header before calling
// Upgrade.
//
// Compression [Experimental]
// Compression EXPERIMENTAL
//
// Per message compression extensions (RFC 7692) are experimentally supported
// by this package in a limited capacity. Setting the EnableCompression option
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
// support. If compression was successfully negotiated with the connection's
// peer, any message received in compressed form will be automatically
// decompressed. All Read methods will return uncompressed bytes.
// support.
//
// var upgrader = websocket.Upgrader{
// EnableCompression: true,
// }
//
// If compression was successfully negotiated with the connection's peer, any
// message received in compressed form will be automatically decompressed.
// All Read methods will return uncompressed bytes.
//
// Per message compression of messages written to a connection can be enabled
// or disabled by calling the corresponding Conn method:
//
// conn.EnableWriteCompression(true)
// conn.EnableWriteCompression(false)
//
// Currently this package does not support compression with "context takeover".
// This means that messages must be compressed and decompressed in isolation,

View File

@ -85,7 +85,7 @@ func echoCopyFull(w http.ResponseWriter, r *http.Request) {
// echoReadAll echoes messages from the client by reading the entire message
// with ioutil.ReadAll.
func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage, writePrepared bool) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Upgrade:", err)
@ -109,10 +109,22 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
}
}
if writeMessage {
if !writePrepared {
err = conn.WriteMessage(mt, b)
if err != nil {
log.Println("WriteMessage:", err)
}
} else {
pm, err := websocket.NewPreparedMessage(mt, b)
if err != nil {
log.Println("NewPreparedMessage:", err)
return
}
err = conn.WritePreparedMessage(pm)
if err != nil {
log.Println("WritePreparedMessage:", err)
}
}
} else {
w, err := conn.NextWriter(mt)
if err != nil {
@ -132,11 +144,15 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
}
func echoReadAllWriter(w http.ResponseWriter, r *http.Request) {
echoReadAll(w, r, false)
echoReadAll(w, r, false, false)
}
func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) {
echoReadAll(w, r, true)
echoReadAll(w, r, true, false)
}
func echoReadAllWritePreparedMessage(w http.ResponseWriter, r *http.Request) {
echoReadAll(w, r, true, true)
}
func serveHome(w http.ResponseWriter, r *http.Request) {
@ -161,6 +177,7 @@ func main() {
http.HandleFunc("/f", echoCopyFull)
http.HandleFunc("/r", echoReadAllWriter)
http.HandleFunc("/m", echoReadAllWriteMessage)
http.HandleFunc("/p", echoReadAllWritePreparedMessage)
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)

View File

@ -129,6 +129,9 @@ func serveWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
}
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
client.hub.register <- client
// Allow collection of memory referenced by the caller by doing all work in
// new goroutines.
go client.writePump()
client.readPump()
go client.readPump()
}

View File

@ -8,11 +8,9 @@ import (
"flag"
"log"
"net/http"
"text/template"
)
var addr = flag.String("addr", ":8080", "http service address")
var homeTemplate = template.Must(template.ParseFiles("home.html"))
func serveHome(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL)
@ -24,8 +22,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Method not allowed", 405)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
homeTemplate.Execute(w, r.Host)
http.ServeFile(w, r, "home.html")
}
func main() {

View File

@ -12,7 +12,6 @@ import (
"net/http"
"os"
"os/exec"
"text/template"
"time"
"github.com/gorilla/websocket"
@ -21,7 +20,6 @@ import (
var (
addr = flag.String("addr", "127.0.0.1:8080", "http service address")
cmdPath string
homeTempl = template.Must(template.ParseFiles("home.html"))
)
const (
@ -176,8 +174,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Method not allowed", 405)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
homeTempl.Execute(w, r.Host)
http.ServeFile(w, r, "home.html")
}
func main() {

View File

@ -6,12 +6,12 @@ package main
import (
"flag"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"text/template"
"time"
"github.com/gorilla/websocket"

View File

@ -2,20 +2,14 @@
// this source code is governed by a BSD-style license that can be found in the
// LICENSE file.
// +build !appengine
package websocket
import (
"math/rand"
"unsafe"
)
import "unsafe"
const wordSize = int(unsafe.Sizeof(uintptr(0)))
func newMaskKey() [4]byte {
n := rand.Uint32()
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
}
func maskBytes(key [4]byte, pos int, b []byte) int {
// Mask one byte at a time for small buffers.

View File

@ -0,0 +1,15 @@
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
// this source code is governed by a BSD-style license that can be found in the
// LICENSE file.
// +build appengine
package websocket
func maskBytes(key [4]byte, pos int, b []byte) int {
for i := range b {
b[i] ^= key[pos&3]
pos++
}
return pos & 3
}

View File

@ -0,0 +1,103 @@
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bytes"
"net"
"sync"
"time"
)
// PreparedMessage caches on the wire representations of a message payload.
// Use PreparedMessage to efficiently send a message payload to multiple
// connections. PreparedMessage is especially useful when compression is used
// because the CPU and memory expensive compression operation can be executed
// once for a given set of compression options.
type PreparedMessage struct {
messageType int
data []byte
err error
mu sync.Mutex
frames map[prepareKey]*preparedFrame
}
// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
type prepareKey struct {
isServer bool
compress bool
compressionLevel int
}
// preparedFrame contains data in wire representation.
type preparedFrame struct {
once sync.Once
data []byte
}
// NewPreparedMessage returns an initialized PreparedMessage. You can then send
// it to connection using WritePreparedMessage method. Valid wire
// representation will be calculated lazily only once for a set of current
// connection options.
func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
pm := &PreparedMessage{
messageType: messageType,
frames: make(map[prepareKey]*preparedFrame),
data: data,
}
// Prepare a plain server frame.
_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
if err != nil {
return nil, err
}
// To protect against caller modifying the data argument, remember the data
// copied to the plain server frame.
pm.data = frameData[len(frameData)-len(data):]
return pm, nil
}
func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
pm.mu.Lock()
frame, ok := pm.frames[key]
if !ok {
frame = &preparedFrame{}
pm.frames[key] = frame
}
pm.mu.Unlock()
var err error
frame.once.Do(func() {
// Prepare a frame using a 'fake' connection.
// TODO: Refactor code in conn.go to allow more direct construction of
// the frame.
mu := make(chan bool, 1)
mu <- true
var nc prepareConn
c := &Conn{
conn: &nc,
mu: mu,
isServer: key.isServer,
compressionLevel: key.compressionLevel,
enableWriteCompression: true,
writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
}
if key.compress {
c.newCompressionWriter = compressNoContextTakeover
}
err = c.WriteMessage(pm.messageType, pm.data)
frame.data = nc.buf.Bytes()
})
return pm.messageType, frame.data, err
}
type prepareConn struct {
buf bytes.Buffer
net.Conn
}
func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }

View File

@ -28,8 +28,9 @@ type Upgrader struct {
HandshakeTimeout time.Duration
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
// size is zero, then a default value of 4096 is used. The I/O buffer sizes
// do not limit the size of the messages that can be sent or received.
// size is zero, then buffers allocated by the HTTP server are used. The
// I/O buffer sizes do not limit the size of the messages that can be sent
// or received.
ReadBufferSize, WriteBufferSize int
// Subprotocols specifies the server's supported protocols in order of
@ -104,23 +105,23 @@ func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header
// response.
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
if r.Method != "GET" {
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET")
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET")
}
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific Sec-Websocket-Extensions headers are unsupported")
}
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13")
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported")
}
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'")
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header")
}
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'")
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header")
}
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
}
checkOrigin := u.CheckOrigin
@ -128,12 +129,12 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
checkOrigin = checkSameOrigin
}
if !checkOrigin(r) {
return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed")
return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed")
}
challengeKey := r.Header.Get("Sec-Websocket-Key")
if challengeKey == "" {
return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank")
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank")
}
subprotocol := u.selectSubprotocol(r, responseHeader)
@ -152,7 +153,6 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
var (
netConn net.Conn
br *bufio.Reader
err error
)
@ -160,19 +160,18 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
if !ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
}
var rw *bufio.ReadWriter
netConn, rw, err = h.Hijack()
var brw *bufio.ReadWriter
netConn, brw, err = h.Hijack()
if err != nil {
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
}
br = rw.Reader
if br.Buffered() > 0 {
if brw.Reader.Buffered() > 0 {
netConn.Close()
return nil, errors.New("websocket: client sent data before handshake is complete")
}
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize)
c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw)
c.subprotocol = subprotocol
if compress {

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