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. // See License.txt for license information.
package einterfaces package einterfaces
@ -6,7 +6,7 @@ package einterfaces
import "github.com/mattermost/platform/model" import "github.com/mattermost/platform/model"
type AccountMigrationInterface interface { type AccountMigrationInterface interface {
MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string) *model.AppError MigrateToLdap(fromAuthService string, forignUserFieldNameToMatch string, force bool) *model.AppError
} }
var theAccountMigrationInterface AccountMigrationInterface 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. // See License.txt for license information.
package einterfaces 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. // See License.txt for license information.
package einterfaces package einterfaces
@ -7,26 +7,19 @@ import (
"github.com/mattermost/platform/model" "github.com/mattermost/platform/model"
) )
type ClusterMessageHandler func(msg *model.ClusterMessage)
type ClusterInterface interface { type ClusterInterface interface {
StartInterNodeCommunication() StartInterNodeCommunication()
StopInterNodeCommunication() StopInterNodeCommunication()
GetClusterInfos() []*model.ClusterInfo RegisterClusterMessageHandler(event string, crm ClusterMessageHandler)
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)
GetClusterId() string 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 ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError
InvalidateAllCaches() *model.AppError
} }
var theClusterInterface ClusterInterface 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. // See License.txt for license information.
package einterfaces 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. // See License.txt for license information.
package einterfaces 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. // See License.txt for license information.
package einterfaces 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. // See License.txt for license information.
package einterfaces package einterfaces
@ -33,6 +33,7 @@ type MetricsInterface interface {
IncrementMemCacheHitCounterSession() IncrementMemCacheHitCounterSession()
IncrementWebsocketEvent(eventType string) IncrementWebsocketEvent(eventType string)
IncrementWebSocketBroadcast(eventType string)
AddMemCacheHitCounter(cacheName string, amount float64) AddMemCacheHitCounter(cacheName string, amount float64)
AddMemCacheMissCounter(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. // See License.txt for license information.
package einterfaces 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. // See License.txt for license information.
package einterfaces 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. // See License.txt for license information.
package einterfaces package einterfaces
@ -10,7 +10,7 @@ import (
type SamlInterface interface { type SamlInterface interface {
ConfigureSP() *model.AppError ConfigureSP() *model.AppError
BuildRequest(relayState string) (*model.SamlAuthRequest, *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) 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. // See License.txt for license information.
package model package model
@ -21,6 +21,7 @@ type AccessData struct {
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
RedirectUri string `json:"redirect_uri"` RedirectUri string `json:"redirect_uri"`
ExpiresAt int64 `json:"expires_at"` ExpiresAt int64 `json:"expires_at"`
Scope string `json:"scope"`
} }
type AccessResponse struct { 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, "") 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, "") 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -39,6 +39,7 @@ var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission
var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission
var PERMISSION_EDIT_OTHER_USERS *Permission var PERMISSION_EDIT_OTHER_USERS *Permission
var PERMISSION_READ_CHANNEL *Permission var PERMISSION_READ_CHANNEL *Permission
var PERMISSION_READ_PUBLIC_CHANNEL *Permission
var PERMISSION_PERMANENT_DELETE_USER *Permission var PERMISSION_PERMANENT_DELETE_USER *Permission
var PERMISSION_UPLOAD_FILE *Permission var PERMISSION_UPLOAD_FILE *Permission
var PERMISSION_GET_PUBLIC_LINK *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_OAUTH *Permission
var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission
var PERMISSION_CREATE_POST *Permission var PERMISSION_CREATE_POST *Permission
var PERMISSION_CREATE_POST_PUBLIC *Permission
var PERMISSION_EDIT_POST *Permission var PERMISSION_EDIT_POST *Permission
var PERMISSION_EDIT_OTHERS_POSTS *Permission var PERMISSION_EDIT_OTHERS_POSTS *Permission
var PERMISSION_DELETE_POST *Permission var PERMISSION_DELETE_POST *Permission
@ -56,6 +58,11 @@ var PERMISSION_CREATE_TEAM *Permission
var PERMISSION_MANAGE_TEAM *Permission var PERMISSION_MANAGE_TEAM *Permission
var PERMISSION_IMPORT_TEAM *Permission var PERMISSION_IMPORT_TEAM *Permission
var PERMISSION_VIEW_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 // General permission that encompases all system admin functions
// in the future this could be broken up to allow access to some // 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_USER *Role
var ROLE_SYSTEM_ADMIN *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_USER *Role
var ROLE_TEAM_ADMIN *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_USER *Role
var ROLE_CHANNEL_ADMIN *Role var ROLE_CHANNEL_ADMIN *Role
@ -195,6 +207,11 @@ func InitalizePermissions() {
"authentication.permissions.read_channel.name", "authentication.permissions.read_channel.name",
"authentication.permissions.read_channel.description", "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{ PERMISSION_PERMANENT_DELETE_USER = &Permission{
"permanent_delete_user", "permanent_delete_user",
"authentication.permissions.permanent_delete_user.name", "authentication.permissions.permanent_delete_user.name",
@ -235,6 +252,11 @@ func InitalizePermissions() {
"authentication.permissions.create_post.name", "authentication.permissions.create_post.name",
"authentication.permissions.create_post.description", "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{ PERMISSION_EDIT_POST = &Permission{
"edit_post", "edit_post",
"authentication.permissions.edit_post.name", "authentication.permissions.edit_post.name",
@ -280,6 +302,31 @@ func InitalizePermissions() {
"authentication.permissions.view_team.name", "authentication.permissions.view_team.name",
"authentication.permissions.view_team.description", "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() { func InitalizeRoles() {
@ -293,7 +340,6 @@ func InitalizeRoles() {
[]string{ []string{
PERMISSION_READ_CHANNEL.Id, PERMISSION_READ_CHANNEL.Id,
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id, PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
PERMISSION_UPLOAD_FILE.Id, PERMISSION_UPLOAD_FILE.Id,
PERMISSION_GET_PUBLIC_LINK.Id, PERMISSION_GET_PUBLIC_LINK.Id,
PERMISSION_CREATE_POST.Id, PERMISSION_CREATE_POST.Id,
@ -326,17 +372,38 @@ func InitalizeRoles() {
[]string{ []string{
PERMISSION_LIST_TEAM_CHANNELS.Id, PERMISSION_LIST_TEAM_CHANNELS.Id,
PERMISSION_JOIN_PUBLIC_CHANNELS.Id, PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
PERMISSION_READ_PUBLIC_CHANNEL.Id,
PERMISSION_VIEW_TEAM.Id, PERMISSION_VIEW_TEAM.Id,
}, },
} }
BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER 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{ ROLE_TEAM_ADMIN = &Role{
"team_admin", "team_admin",
"authentication.roles.team_admin.name", "authentication.roles.team_admin.name",
"authentication.roles.team_admin.description", "authentication.roles.team_admin.description",
[]string{ []string{
PERMISSION_EDIT_OTHERS_POSTS.Id, PERMISSION_EDIT_OTHERS_POSTS.Id,
PERMISSION_ADD_USER_TO_TEAM.Id,
PERMISSION_REMOVE_USER_FROM_TEAM.Id, PERMISSION_REMOVE_USER_FROM_TEAM.Id,
PERMISSION_MANAGE_TEAM.Id, PERMISSION_MANAGE_TEAM.Id,
PERMISSION_IMPORT_TEAM.Id, PERMISSION_IMPORT_TEAM.Id,
@ -358,10 +425,42 @@ func InitalizeRoles() {
PERMISSION_CREATE_DIRECT_CHANNEL.Id, PERMISSION_CREATE_DIRECT_CHANNEL.Id,
PERMISSION_CREATE_GROUP_CHANNEL.Id, PERMISSION_CREATE_GROUP_CHANNEL.Id,
PERMISSION_PERMANENT_DELETE_USER.Id, PERMISSION_PERMANENT_DELETE_USER.Id,
PERMISSION_MANAGE_OAUTH.Id,
}, },
} }
BuiltInRoles[ROLE_SYSTEM_USER.Id] = ROLE_SYSTEM_USER 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{ ROLE_SYSTEM_ADMIN = &Role{
"system_admin", "system_admin",
"authentication.roles.global_admin.name", "authentication.roles.global_admin.name",
@ -378,6 +477,8 @@ func InitalizeRoles() {
PERMISSION_MANAGE_SYSTEM.Id, PERMISSION_MANAGE_SYSTEM.Id,
PERMISSION_MANAGE_ROLES.Id, PERMISSION_MANAGE_ROLES.Id,
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.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_DELETE_PUBLIC_CHANNEL.Id,
PERMISSION_CREATE_PUBLIC_CHANNEL.Id, PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id, PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
@ -391,6 +492,13 @@ func InitalizeRoles() {
PERMISSION_DELETE_POST.Id, PERMISSION_DELETE_POST.Id,
PERMISSION_DELETE_OTHERS_POSTS.Id, PERMISSION_DELETE_OTHERS_POSTS.Id,
PERMISSION_CREATE_TEAM.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..., 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. // See License.txt for license information.
package model package model
@ -6,6 +6,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"net/http"
) )
const ( const (
@ -25,6 +26,14 @@ type AuthData struct {
Scope string `json:"scope"` 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 // IsValid validates the AuthData and returns an error if it isn't configured
// correctly. // correctly.
func (ad *AuthData) IsValid() *AppError { 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) 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) 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 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() { func (ad *AuthData) PreSave() {
if ad.ExpiresIn == 0 { if ad.ExpiresIn == 0 {
ad.ExpiresIn = AUTHCODE_EXPIRE_TIME 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 { func (ad *AuthData) IsExpired() bool {
if GetMillis() > ad.CreateAt+int64(ad.ExpiresIn*1000) { 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. // See License.txt for license information.
package model package model
@ -46,6 +46,13 @@ type Channel struct {
CreatorId string `json:"creator_id"` 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 { func (o *Channel) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { 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 { func ChannelFromJson(data io.Reader) *Channel {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)
var o Channel 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 { func (o *Channel) Etag() string {
return Etag(o.Id, o.UpdateAt) return Etag(o.Id, o.UpdateAt)
} }
@ -137,6 +164,24 @@ func (o *Channel) IsGroupOrDirect() bool {
return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP 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 { func GetDMNameFromIds(userId1, userId2 string) string {
if userId1 > userId2 { if userId1 > userId2 {
return userId2 + "__" + userId1 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -50,3 +50,14 @@ func ChannelListFromJson(data io.Reader) *ChannelList {
return nil 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. // See License.txt for license information.
package model package model
@ -19,11 +19,11 @@ const (
) )
type ChannelUnread struct { type ChannelUnread struct {
TeamId string TeamId string `json:"team_id"`
TotalMsgCount int64 ChannelId string `json:"channel_id"`
MsgCount int64 MsgCount int64 `json:"msg_count"`
MentionCount int64 MentionCount int64 `json:"mention_count"`
NotifyProps StringMap NotifyProps StringMap `json:"-"`
} }
type ChannelMember struct { 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 { func ChannelMembersFromJson(data io.Reader) *ChannelMembers {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)
var o ChannelMembers 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 { func (o *ChannelMember) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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 // GetMoreChannelsPage will return a page of open channels the user is not in based on
// the provided offset and limit. Must be authenticated. // the provided offset and limit. Must be authenticated.
func (c *Client) GetMoreChannelsPage(offset int, limit int) (*Result, *AppError) { 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 // 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. // 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. // 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) { func (c *Client) UploadProfileFile(data []byte, contentType string) (*Result, *AppError) {
return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType) 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) { func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetTeamRoute()+"/me", "", etag); err != nil { if r, err := c.DoApiGet(c.GetTeamRoute()+"/me", "", etag); err != nil {
return nil, err 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 // GetTeamByName will return a team object based on the team name provided. Must be authenticated.
// based on the team id provided. Must be authenticated.
func (c *Client) GetTeamByName(teamName string) (*Result, *AppError) { func (c *Client) GetTeamByName(teamName string) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf("/teams/name/%v", teamName), "", ""); err != nil { if r, err := c.DoApiGet(fmt.Sprintf("/teams/name/%v", teamName), "", ""); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID), 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. // See License.txt for license information.
package model package model
@ -6,16 +6,14 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"strings"
) )
type ClusterInfo struct { type ClusterInfo struct {
Id string `json:"id"` Version string `json:"version"`
Version string `json:"version"` ConfigHash string `json:"config_hash"`
ConfigHash string `json:"config_hash"` IpAddress string `json:"ipaddress"`
InterNodeUrl string `json:"internode_url"` Hostname string `json:"hostname"`
Hostname string `json:"hostname"`
LastSuccessfulPing int64 `json:"last_ping"`
IsAlive bool `json:"is_alive"`
} }
func (me *ClusterInfo) ToJson() string { 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 { func ClusterInfoFromJson(data io.Reader) *ClusterInfo {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)
var me ClusterInfo 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 { func ClusterInfosToJson(objmap []*ClusterInfo) string {
if b, err := json.Marshal(objmap); err != nil { if b, err := json.Marshal(objmap); err != nil {
return "" 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -6,13 +6,20 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
goi18n "github.com/nicksnyder/go-i18n/i18n"
) )
type CommandArgs struct { type CommandArgs struct {
ChannelId string `json:"channel_id"` UserId string `json:"user_id"`
RootId string `json:"root_id"` ChannelId string `json:"channel_id"`
ParentId string `json:"parent_id"` TeamId string `json:"team_id"`
Command string `json:"command"` 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 { 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. // See License.txt for license information.
package model package model
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io" "io"
) )
@ -40,14 +39,8 @@ func CommandResponseFromJson(data io.Reader) *CommandResponse {
return nil return nil
} }
// Ensure attachment fields are stored as strings o.Text = ExpandAnnouncement(o.Text)
for _, attachment := range o.Attachments { o.Attachments = ProcessSlackAttachments(o.Attachments)
for _, field := range attachment.Fields {
if field.Value != nil {
field.Value = fmt.Sprintf("%v", field.Value)
}
}
}
return &o 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
import ( import (
"regexp"
"time" "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 { func (me *CompliancePost) Row() []string {
postDeleteAt := "" postDeleteAt := ""
@ -77,15 +87,15 @@ func (me *CompliancePost) Row() []string {
} }
return []string{ return []string{
me.TeamName, cleanComplianceStrings(me.TeamName),
me.TeamDisplayName, cleanComplianceStrings(me.TeamDisplayName),
me.ChannelName, cleanComplianceStrings(me.ChannelName),
me.ChannelDisplayName, cleanComplianceStrings(me.ChannelDisplayName),
me.UserUsername, cleanComplianceStrings(me.UserUsername),
me.UserEmail, cleanComplianceStrings(me.UserEmail),
me.UserNickname, cleanComplianceStrings(me.UserNickname),
me.PostId, me.PostId,
time.Unix(0, me.PostCreateAt*int64(1000*1000)).Format(time.RFC3339), time.Unix(0, me.PostCreateAt*int64(1000*1000)).Format(time.RFC3339),
@ -95,7 +105,7 @@ func (me *CompliancePost) Row() []string {
me.PostRootId, me.PostRootId,
me.PostParentId, me.PostParentId,
me.PostOriginalId, me.PostOriginalId,
me.PostMessage, cleanComplianceStrings(me.PostMessage),
me.PostType, me.PostType,
me.PostProps, me.PostProps,
me.PostHashtags, 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. // See License.txt for license information.
package model package model
@ -6,6 +6,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"net/http"
"net/url" "net/url"
) )
@ -32,12 +33,17 @@ const (
WEBSERVER_MODE_GZIP = "gzip" WEBSERVER_MODE_GZIP = "gzip"
WEBSERVER_MODE_DISABLED = "disabled" WEBSERVER_MODE_DISABLED = "disabled"
GENERIC_NOTIFICATION = "generic" GENERIC_NO_CHANNEL_NOTIFICATION = "generic_no_channel"
FULL_NOTIFICATION = "full" GENERIC_NOTIFICATION = "generic"
FULL_NOTIFICATION = "full"
DIRECT_MESSAGE_ANY = "any" DIRECT_MESSAGE_ANY = "any"
DIRECT_MESSAGE_TEAM = "team" DIRECT_MESSAGE_TEAM = "team"
SHOW_USERNAME = "username"
SHOW_NICKNAME_FULLNAME = "nickname_full_name"
SHOW_FULLNAME = "full_name"
PERMISSIONS_ALL = "all" PERMISSIONS_ALL = "all"
PERMISSIONS_CHANNEL_ADMIN = "channel_admin" PERMISSIONS_CHANNEL_ADMIN = "channel_admin"
PERMISSIONS_TEAM_ADMIN = "team_admin" PERMISSIONS_TEAM_ADMIN = "team_admin"
@ -60,6 +66,9 @@ const (
EMAIL_BATCHING_BUFFER_SIZE = 256 EMAIL_BATCHING_BUFFER_SIZE = 256
EMAIL_BATCHING_INTERVAL = 30 EMAIL_BATCHING_INTERVAL = 30
EMAIL_NOTIFICATION_CONTENTS_FULL = "full"
EMAIL_NOTIFICATION_CONTENTS_GENERIC = "generic"
SITENAME_MAX_LENGTH = 30 SITENAME_MAX_LENGTH = 30
SERVICE_SETTINGS_DEFAULT_SITE_URL = "" SERVICE_SETTINGS_DEFAULT_SITE_URL = ""
@ -75,12 +84,15 @@ const (
EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = "" EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = ""
SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/" SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/"
SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/" SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/"
SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/" 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_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_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/"
SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com" SUPPORT_SETTINGS_DEFAULT_ADMINISTRATORS_GUIDE_LINK = "https://about.mattermost.com/administrators-guide/"
SUPPORT_SETTINGS_DEFAULT_TROUBLESHOOTING_FORUM_LINK = "https://about.mattermost.com/troubleshooting-forum/"
SUPPORT_SETTINGS_DEFAULT_COMMERCIAL_SUPPORT_LINK = "https://about.mattermost.com/commercial-support/"
SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = "" LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = "" LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
@ -107,10 +119,20 @@ const (
WEBRTC_SETTINGS_DEFAULT_TURN_URI = "" WEBRTC_SETTINGS_DEFAULT_TURN_URI = ""
ANALYTICS_SETTINGS_DEFAULT_MAX_USERS_FOR_STATISTICS = 2500 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 { type ServiceSettings struct {
SiteURL *string SiteURL *string
LicenseFileLocation *string
ListenAddress string ListenAddress string
ConnectionSecurity *string ConnectionSecurity *string
TLSCertFile *string TLSCertFile *string
@ -121,6 +143,7 @@ type ServiceSettings struct {
ReadTimeout *int ReadTimeout *int
WriteTimeout *int WriteTimeout *int
MaximumLoginAttempts int MaximumLoginAttempts int
GoroutineHealthThreshold *int
GoogleDeveloperKey string GoogleDeveloperKey string
EnableOAuthServiceProvider bool EnableOAuthServiceProvider bool
EnableIncomingWebhooks bool EnableIncomingWebhooks bool
@ -129,6 +152,7 @@ type ServiceSettings struct {
EnableOnlyAdminIntegrations *bool EnableOnlyAdminIntegrations *bool
EnablePostUsernameOverride bool EnablePostUsernameOverride bool
EnablePostIconOverride bool EnablePostIconOverride bool
EnableAPIv3 *bool
EnableLinkPreviews *bool EnableLinkPreviews *bool
EnableTesting bool EnableTesting bool
EnableDeveloper *bool EnableDeveloper *bool
@ -136,6 +160,7 @@ type ServiceSettings struct {
EnableInsecureOutgoingConnections *bool EnableInsecureOutgoingConnections *bool
EnableMultifactorAuthentication *bool EnableMultifactorAuthentication *bool
EnforceMultifactorAuthentication *bool EnforceMultifactorAuthentication *bool
EnableUserAccessTokens *bool
AllowCorsFrom *string AllowCorsFrom *string
SessionLengthWebInDays *int SessionLengthWebInDays *int
SessionLengthMobileInDays *int SessionLengthMobileInDays *int
@ -145,19 +170,28 @@ type ServiceSettings struct {
WebsocketPort *int WebsocketPort *int
WebserverMode *string WebserverMode *string
EnableCustomEmoji *bool EnableCustomEmoji *bool
EnableEmojiPicker *bool
RestrictCustomEmojiCreation *string RestrictCustomEmojiCreation *string
RestrictPostDelete *string RestrictPostDelete *string
AllowEditPost *string AllowEditPost *string
PostEditTimeLimit *int PostEditTimeLimit *int
TimeBetweenUserTypingUpdatesMilliseconds *int64 TimeBetweenUserTypingUpdatesMilliseconds *int64
EnablePostSearch *bool
EnableUserTypingMessages *bool EnableUserTypingMessages *bool
EnableChannelViewedMessages *bool
EnableUserStatuses *bool
ClusterLogTimeoutMilliseconds *int ClusterLogTimeoutMilliseconds *int
} }
type ClusterSettings struct { type ClusterSettings struct {
Enable *bool Enable *bool
InterNodeListenAddress *string ClusterName *string
InterNodeUrls []string OverrideHostname *string
UseIpAddress *bool
UseExperimentalGossip *bool
ReadOnlyConfig *bool
GossipPort *int
StreamingPort *int
} }
type MetricsSettings struct { type MetricsSettings struct {
@ -181,13 +215,15 @@ type SSOSettings struct {
} }
type SqlSettings struct { type SqlSettings struct {
DriverName string DriverName string
DataSource string DataSource string
DataSourceReplicas []string DataSourceReplicas []string
MaxIdleConns int DataSourceSearchReplicas []string
MaxOpenConns int MaxIdleConns int
Trace bool MaxOpenConns int
AtRestEncryptKey string Trace bool
AtRestEncryptKey string
QueryTimeout *int
} }
type LogSettings struct { type LogSettings struct {
@ -210,17 +246,14 @@ type PasswordSettings struct {
} }
type FileSettings struct { type FileSettings struct {
EnableFileAttachments *bool
EnableMobileUpload *bool
EnableMobileDownload *bool
MaxFileSize *int64 MaxFileSize *int64
DriverName string DriverName string
Directory string Directory string
EnablePublicLink bool EnablePublicLink bool
PublicLinkSalt *string PublicLinkSalt *string
ThumbnailWidth int
ThumbnailHeight int
PreviewWidth int
PreviewHeight int
ProfileWidth int
ProfileHeight int
InitialFont string InitialFont string
AmazonS3AccessKeyId string AmazonS3AccessKeyId string
AmazonS3SecretAccessKey string AmazonS3SecretAccessKey string
@ -228,30 +261,34 @@ type FileSettings struct {
AmazonS3Region string AmazonS3Region string
AmazonS3Endpoint string AmazonS3Endpoint string
AmazonS3SSL *bool AmazonS3SSL *bool
AmazonS3SignV2 *bool
AmazonS3SSE *bool
} }
type EmailSettings struct { type EmailSettings struct {
EnableSignUpWithEmail bool EnableSignUpWithEmail bool
EnableSignInWithEmail *bool EnableSignInWithEmail *bool
EnableSignInWithUsername *bool EnableSignInWithUsername *bool
SendEmailNotifications bool SendEmailNotifications bool
RequireEmailVerification bool RequireEmailVerification bool
FeedbackName string FeedbackName string
FeedbackEmail string FeedbackEmail string
FeedbackOrganization *string FeedbackOrganization *string
SMTPUsername string EnableSMTPAuth *bool
SMTPPassword string SMTPUsername string
SMTPServer string SMTPPassword string
SMTPPort string SMTPServer string
ConnectionSecurity string SMTPPort string
InviteSalt string ConnectionSecurity string
PasswordResetSalt string InviteSalt string
SendPushNotifications *bool SendPushNotifications *bool
PushNotificationServer *string PushNotificationServer *string
PushNotificationContents *string PushNotificationContents *string
EnableEmailBatching *bool EnableEmailBatching *bool
EmailBatchingBufferSize *int EmailBatchingBufferSize *int
EmailBatchingInterval *int EmailBatchingInterval *int
SkipServerCertificateVerification *bool
EmailNotificationContentsType *string
} }
type RateLimitSettings struct { type RateLimitSettings struct {
@ -269,35 +306,48 @@ type PrivacySettings struct {
} }
type SupportSettings struct { type SupportSettings struct {
TermsOfServiceLink *string TermsOfServiceLink *string
PrivacyPolicyLink *string PrivacyPolicyLink *string
AboutLink *string AboutLink *string
HelpLink *string HelpLink *string
ReportAProblemLink *string ReportAProblemLink *string
SupportEmail *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 { type TeamSettings struct {
SiteName string SiteName string
MaxUsersPerTeam int MaxUsersPerTeam int
EnableTeamCreation bool EnableTeamCreation bool
EnableUserCreation bool EnableUserCreation bool
EnableOpenServer *bool EnableOpenServer *bool
RestrictCreationToDomains string RestrictCreationToDomains string
EnableCustomBrand *bool EnableCustomBrand *bool
CustomBrandText *string CustomBrandText *string
CustomDescriptionText *string CustomDescriptionText *string
RestrictDirectMessage *string RestrictDirectMessage *string
RestrictTeamInvite *string RestrictTeamInvite *string
RestrictPublicChannelManagement *string RestrictPublicChannelManagement *string
RestrictPrivateChannelManagement *string RestrictPrivateChannelManagement *string
RestrictPublicChannelCreation *string RestrictPublicChannelCreation *string
RestrictPrivateChannelCreation *string RestrictPrivateChannelCreation *string
RestrictPublicChannelDeletion *string RestrictPublicChannelDeletion *string
RestrictPrivateChannelDeletion *string RestrictPrivateChannelDeletion *string
UserStatusAwayTimeout *int64 RestrictPrivateChannelManageMembers *string
MaxChannelsPerTeam *int64 UserStatusAwayTimeout *int64
MaxNotificationsPerChannel *int64 MaxChannelsPerTeam *int64
MaxNotificationsPerChannel *int64
TeammateNameDisplay *string
} }
type LdapSettings struct { type LdapSettings struct {
@ -389,29 +439,58 @@ type WebrtcSettings struct {
TurnSharedKey *string 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 { type Config struct {
ServiceSettings ServiceSettings ServiceSettings ServiceSettings
TeamSettings TeamSettings TeamSettings TeamSettings
SqlSettings SqlSettings SqlSettings SqlSettings
LogSettings LogSettings LogSettings LogSettings
PasswordSettings PasswordSettings PasswordSettings PasswordSettings
FileSettings FileSettings FileSettings FileSettings
EmailSettings EmailSettings EmailSettings EmailSettings
RateLimitSettings RateLimitSettings RateLimitSettings RateLimitSettings
PrivacySettings PrivacySettings PrivacySettings PrivacySettings
SupportSettings SupportSettings SupportSettings SupportSettings
GitLabSettings SSOSettings AnnouncementSettings AnnouncementSettings
GoogleSettings SSOSettings GitLabSettings SSOSettings
Office365Settings SSOSettings GoogleSettings SSOSettings
LdapSettings LdapSettings Office365Settings SSOSettings
ComplianceSettings ComplianceSettings LdapSettings LdapSettings
LocalizationSettings LocalizationSettings ComplianceSettings ComplianceSettings
SamlSettings SamlSettings LocalizationSettings LocalizationSettings
NativeAppSettings NativeAppSettings SamlSettings SamlSettings
ClusterSettings ClusterSettings NativeAppSettings NativeAppSettings
MetricsSettings MetricsSettings ClusterSettings ClusterSettings
AnalyticsSettings AnalyticsSettings MetricsSettings MetricsSettings
WebrtcSettings WebrtcSettings AnalyticsSettings AnalyticsSettings
WebrtcSettings WebrtcSettings
ElasticsearchSettings ElasticsearchSettings
DataRetentionSettings DataRetentionSettings
JobSettings JobSettings
PluginSettings PluginSettings
} }
func (o *Config) ToJson() string { func (o *Config) ToJson() string {
@ -453,27 +532,52 @@ func (o *Config) SetDefaults() {
o.SqlSettings.AtRestEncryptKey = NewRandomString(32) o.SqlSettings.AtRestEncryptKey = NewRandomString(32)
} }
if o.SqlSettings.QueryTimeout == nil {
o.SqlSettings.QueryTimeout = new(int)
*o.SqlSettings.QueryTimeout = 30
}
if o.FileSettings.AmazonS3Endpoint == "" { if o.FileSettings.AmazonS3Endpoint == "" {
// Defaults to "s3.amazonaws.com" // Defaults to "s3.amazonaws.com"
o.FileSettings.AmazonS3Endpoint = "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 { if o.FileSettings.AmazonS3SSL == nil {
o.FileSettings.AmazonS3SSL = new(bool) o.FileSettings.AmazonS3SSL = new(bool)
*o.FileSettings.AmazonS3SSL = true // Secure by default. *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 { if o.FileSettings.MaxFileSize == nil {
o.FileSettings.MaxFileSize = new(int64) o.FileSettings.MaxFileSize = new(int64)
*o.FileSettings.MaxFileSize = 52428800 // 50 MB *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 = new(string)
*o.FileSettings.PublicLinkSalt = NewRandomString(32) *o.FileSettings.PublicLinkSalt = NewRandomString(32)
} }
@ -483,12 +587,12 @@ func (o *Config) SetDefaults() {
o.FileSettings.InitialFont = "luximbi.ttf" o.FileSettings.InitialFont = "luximbi.ttf"
} }
if len(o.EmailSettings.InviteSalt) == 0 { if o.FileSettings.Directory == "" {
o.EmailSettings.InviteSalt = NewRandomString(32) o.FileSettings.Directory = "./data/"
} }
if len(o.EmailSettings.PasswordResetSalt) == 0 { if len(o.EmailSettings.InviteSalt) == 0 {
o.EmailSettings.PasswordResetSalt = NewRandomString(32) o.EmailSettings.InviteSalt = NewRandomString(32)
} }
if o.ServiceSettings.SiteURL == nil { if o.ServiceSettings.SiteURL == nil {
@ -496,6 +600,15 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.SiteURL = SERVICE_SETTINGS_DEFAULT_SITE_URL *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 { if o.ServiceSettings.EnableLinkPreviews == nil {
o.ServiceSettings.EnableLinkPreviews = new(bool) o.ServiceSettings.EnableLinkPreviews = new(bool)
*o.ServiceSettings.EnableLinkPreviews = false *o.ServiceSettings.EnableLinkPreviews = false
@ -526,6 +639,11 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.EnforceMultifactorAuthentication = false *o.ServiceSettings.EnforceMultifactorAuthentication = false
} }
if o.ServiceSettings.EnableUserAccessTokens == nil {
o.ServiceSettings.EnableUserAccessTokens = new(bool)
*o.ServiceSettings.EnableUserAccessTokens = false
}
if o.PasswordSettings.MinimumLength == nil { if o.PasswordSettings.MinimumLength == nil {
o.PasswordSettings.MinimumLength = new(int) o.PasswordSettings.MinimumLength = new(int)
*o.PasswordSettings.MinimumLength = PASSWORD_MINIMUM_LENGTH *o.PasswordSettings.MinimumLength = PASSWORD_MINIMUM_LENGTH
@ -594,13 +712,21 @@ func (o *Config) SetDefaults() {
if o.TeamSettings.RestrictPublicChannelCreation == nil { if o.TeamSettings.RestrictPublicChannelCreation == nil {
o.TeamSettings.RestrictPublicChannelCreation = new(string) o.TeamSettings.RestrictPublicChannelCreation = new(string)
// If this setting does not exist, assume migration from <3.6, so use management setting as default. // If this setting does not exist, assume migration from <3.6, so use management setting as default.
*o.TeamSettings.RestrictPublicChannelCreation = *o.TeamSettings.RestrictPublicChannelManagement if *o.TeamSettings.RestrictPublicChannelManagement == PERMISSIONS_CHANNEL_ADMIN {
*o.TeamSettings.RestrictPublicChannelCreation = PERMISSIONS_TEAM_ADMIN
} else {
*o.TeamSettings.RestrictPublicChannelCreation = *o.TeamSettings.RestrictPublicChannelManagement
}
} }
if o.TeamSettings.RestrictPrivateChannelCreation == nil { if o.TeamSettings.RestrictPrivateChannelCreation == nil {
o.TeamSettings.RestrictPrivateChannelCreation = new(string) o.TeamSettings.RestrictPrivateChannelCreation = new(string)
// If this setting does not exist, assume migration from <3.6, so use management setting as default. // If this setting does not exist, assume migration from <3.6, so use management setting as default.
*o.TeamSettings.RestrictPrivateChannelCreation = *o.TeamSettings.RestrictPrivateChannelManagement if *o.TeamSettings.RestrictPrivateChannelManagement == PERMISSIONS_CHANNEL_ADMIN {
*o.TeamSettings.RestrictPrivateChannelCreation = PERMISSIONS_TEAM_ADMIN
} else {
*o.TeamSettings.RestrictPrivateChannelCreation = *o.TeamSettings.RestrictPrivateChannelManagement
}
} }
if o.TeamSettings.RestrictPublicChannelDeletion == nil { if o.TeamSettings.RestrictPublicChannelDeletion == nil {
@ -615,6 +741,11 @@ func (o *Config) SetDefaults() {
*o.TeamSettings.RestrictPrivateChannelDeletion = *o.TeamSettings.RestrictPrivateChannelManagement *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 { if o.TeamSettings.UserStatusAwayTimeout == nil {
o.TeamSettings.UserStatusAwayTimeout = new(int64) o.TeamSettings.UserStatusAwayTimeout = new(int64)
*o.TeamSettings.UserStatusAwayTimeout = TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT *o.TeamSettings.UserStatusAwayTimeout = TEAM_SETTINGS_DEFAULT_USER_STATUS_AWAY_TIMEOUT
@ -680,8 +811,31 @@ func (o *Config) SetDefaults() {
*o.EmailSettings.EmailBatchingInterval = EMAIL_BATCHING_INTERVAL *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) { if !IsSafeLink(o.SupportSettings.TermsOfServiceLink) {
o.SupportSettings.TermsOfServiceLink = nil *o.SupportSettings.TermsOfServiceLink = SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK
} }
if o.SupportSettings.TermsOfServiceLink == nil { if o.SupportSettings.TermsOfServiceLink == nil {
@ -690,7 +844,7 @@ func (o *Config) SetDefaults() {
} }
if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) { if !IsSafeLink(o.SupportSettings.PrivacyPolicyLink) {
o.SupportSettings.PrivacyPolicyLink = nil *o.SupportSettings.PrivacyPolicyLink = ""
} }
if o.SupportSettings.PrivacyPolicyLink == nil { if o.SupportSettings.PrivacyPolicyLink == nil {
@ -699,7 +853,7 @@ func (o *Config) SetDefaults() {
} }
if !IsSafeLink(o.SupportSettings.AboutLink) { if !IsSafeLink(o.SupportSettings.AboutLink) {
o.SupportSettings.AboutLink = nil *o.SupportSettings.AboutLink = ""
} }
if o.SupportSettings.AboutLink == nil { if o.SupportSettings.AboutLink == nil {
@ -708,7 +862,7 @@ func (o *Config) SetDefaults() {
} }
if !IsSafeLink(o.SupportSettings.HelpLink) { if !IsSafeLink(o.SupportSettings.HelpLink) {
o.SupportSettings.HelpLink = nil *o.SupportSettings.HelpLink = ""
} }
if o.SupportSettings.HelpLink == nil { if o.SupportSettings.HelpLink == nil {
@ -717,7 +871,7 @@ func (o *Config) SetDefaults() {
} }
if !IsSafeLink(o.SupportSettings.ReportAProblemLink) { if !IsSafeLink(o.SupportSettings.ReportAProblemLink) {
o.SupportSettings.ReportAProblemLink = nil *o.SupportSettings.ReportAProblemLink = ""
} }
if o.SupportSettings.ReportAProblemLink == nil { if o.SupportSettings.ReportAProblemLink == nil {
@ -725,11 +879,63 @@ func (o *Config) SetDefaults() {
*o.SupportSettings.ReportAProblemLink = SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK *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 { if o.SupportSettings.SupportEmail == nil {
o.SupportSettings.SupportEmail = new(string) o.SupportSettings.SupportEmail = new(string)
*o.SupportSettings.SupportEmail = SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL *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 { if o.LdapSettings.Enable == nil {
o.LdapSettings.Enable = new(bool) o.LdapSettings.Enable = new(bool)
*o.LdapSettings.Enable = false *o.LdapSettings.Enable = false
@ -884,7 +1090,12 @@ func (o *Config) SetDefaults() {
if o.ServiceSettings.EnableCustomEmoji == nil { if o.ServiceSettings.EnableCustomEmoji == nil {
o.ServiceSettings.EnableCustomEmoji = new(bool) 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 { if o.ServiceSettings.RestrictCustomEmojiCreation == nil {
@ -907,18 +1118,44 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.PostEditTimeLimit = 300 *o.ServiceSettings.PostEditTimeLimit = 300
} }
if o.ClusterSettings.InterNodeListenAddress == nil {
o.ClusterSettings.InterNodeListenAddress = new(string)
*o.ClusterSettings.InterNodeListenAddress = ":8075"
}
if o.ClusterSettings.Enable == nil { if o.ClusterSettings.Enable == nil {
o.ClusterSettings.Enable = new(bool) o.ClusterSettings.Enable = new(bool)
*o.ClusterSettings.Enable = false *o.ClusterSettings.Enable = false
} }
if o.ClusterSettings.InterNodeUrls == nil { if o.ClusterSettings.ClusterName == nil {
o.ClusterSettings.InterNodeUrls = []string{} 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 { if o.MetricsSettings.ListenAddress == nil {
@ -978,12 +1215,12 @@ func (o *Config) SetDefaults() {
if o.SamlSettings.Verify == nil { if o.SamlSettings.Verify == nil {
o.SamlSettings.Verify = new(bool) o.SamlSettings.Verify = new(bool)
*o.SamlSettings.Verify = false *o.SamlSettings.Verify = true
} }
if o.SamlSettings.Encrypt == nil { if o.SamlSettings.Encrypt == nil {
o.SamlSettings.Encrypt = new(bool) o.SamlSettings.Encrypt = new(bool)
*o.SamlSettings.Encrypt = false *o.SamlSettings.Encrypt = true
} }
if o.SamlSettings.IdpUrl == nil { if o.SamlSettings.IdpUrl == nil {
@ -1056,6 +1293,15 @@ func (o *Config) SetDefaults() {
*o.SamlSettings.LocaleAttribute = SAML_SETTINGS_DEFAULT_LOCALE_ATTRIBUTE *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 { if o.NativeAppSettings.AppDownloadLink == nil {
o.NativeAppSettings.AppDownloadLink = new(string) o.NativeAppSettings.AppDownloadLink = new(string)
*o.NativeAppSettings.AppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK *o.NativeAppSettings.AppDownloadLink = NATIVEAPP_SETTINGS_DEFAULT_APP_DOWNLOAD_LINK
@ -1076,6 +1322,11 @@ func (o *Config) SetDefaults() {
*o.RateLimitSettings.Enable = false *o.RateLimitSettings.Enable = false
} }
if o.ServiceSettings.GoroutineHealthThreshold == nil {
o.ServiceSettings.GoroutineHealthThreshold = new(int)
*o.ServiceSettings.GoroutineHealthThreshold = -1
}
if o.RateLimitSettings.MaxBurst == nil { if o.RateLimitSettings.MaxBurst == nil {
o.RateLimitSettings.MaxBurst = new(int) o.RateLimitSettings.MaxBurst = new(int)
*o.RateLimitSettings.MaxBurst = 100 *o.RateLimitSettings.MaxBurst = 100
@ -1131,16 +1382,90 @@ func (o *Config) SetDefaults() {
*o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = 5000 *o.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds = 5000
} }
if o.ServiceSettings.EnablePostSearch == nil {
o.ServiceSettings.EnablePostSearch = new(bool)
*o.ServiceSettings.EnablePostSearch = true
}
if o.ServiceSettings.EnableUserTypingMessages == nil { if o.ServiceSettings.EnableUserTypingMessages == nil {
o.ServiceSettings.EnableUserTypingMessages = new(bool) o.ServiceSettings.EnableUserTypingMessages = new(bool)
*o.ServiceSettings.EnableUserTypingMessages = true *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 { if o.ServiceSettings.ClusterLogTimeoutMilliseconds == nil {
o.ServiceSettings.ClusterLogTimeoutMilliseconds = new(int) o.ServiceSettings.ClusterLogTimeoutMilliseconds = new(int)
*o.ServiceSettings.ClusterLogTimeoutMilliseconds = 2000 *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() 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, "") 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 { if len(o.SqlSettings.AtRestEncryptKey) < 32 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.encrypt_sql.app_error", nil, "") 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, "") 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 { if len(o.SqlSettings.DataSource) == 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_data_src.app_error", nil, "") 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, "") 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 { if len(*o.FileSettings.PublicLinkSalt) < 32 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.file_salt.app_error", nil, "") 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, "") 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 { if *o.EmailSettings.EmailBatchingBufferSize <= 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.email_batching_buffer_size.app_error", nil, "") 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, "") 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 { if o.RateLimitSettings.MemoryStoreSize <= 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.rate_mem.app_error", nil, "") 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, "") 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 return nil
} }
@ -1398,7 +1717,6 @@ func (o *Config) Sanitize() {
} }
o.EmailSettings.InviteSalt = FAKE_SETTING o.EmailSettings.InviteSalt = FAKE_SETTING
o.EmailSettings.PasswordResetSalt = FAKE_SETTING
if len(o.EmailSettings.SMTPPassword) > 0 { if len(o.EmailSettings.SMTPPassword) > 0 {
o.EmailSettings.SMTPPassword = FAKE_SETTING o.EmailSettings.SMTPPassword = FAKE_SETTING
} }
@ -1413,6 +1731,12 @@ func (o *Config) Sanitize() {
for i := range o.SqlSettings.DataSourceReplicas { for i := range o.SqlSettings.DataSourceReplicas {
o.SqlSettings.DataSourceReplicas[i] = FAKE_SETTING 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() { 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. // See License.txt for license information.
package model package model
@ -34,7 +34,7 @@ func (emoji *Emoji) IsValid() *AppError {
return NewLocAppError("Emoji.IsValid", "model.emoji.user_id.app_error", nil, "") 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, "") 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package oauthgitlab 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. // See License.txt for license information.
package model package model
@ -6,10 +6,9 @@ package model
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"net/http"
"regexp" "regexp"
"strings"
) )
const ( const (
@ -81,35 +80,36 @@ func IncomingWebhookListFromJson(data io.Reader) []*IncomingWebhook {
func (o *IncomingWebhook) IsValid() *AppError { func (o *IncomingWebhook) IsValid() *AppError {
if len(o.Id) != 26 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 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 { func IncomingWebhookRequestFromJson(data io.Reader) *IncomingWebhookRequest {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.ReadFrom(data) 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 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
import ( import (
"fmt" "encoding/json"
"time" "io"
"net/http"
) )
type TaskFunc func() const (
JOB_TYPE_DATA_RETENTION = "data_retention"
JOB_TYPE_ELASTICSEARCH_POST_INDEXING = "elasticsearch_post_indexing"
type ScheduledTask struct { JOB_STATUS_PENDING = "pending"
Name string `json:"name"` JOB_STATUS_IN_PROGRESS = "in_progress"
Interval time.Duration `json:"interval"` JOB_STATUS_SUCCESS = "success"
Recurring bool `json:"recurring"` JOB_STATUS_ERROR = "error"
function TaskFunc JOB_STATUS_CANCEL_REQUESTED = "cancel_requested"
timer *time.Timer 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 (j *Job) IsValid() *AppError {
if len(j.Id) != 26 {
func addTask(task *ScheduledTask) { return NewAppError("Job.IsValid", "model.job.is_valid.id.app_error", nil, "id="+j.Id, http.StatusBadRequest)
tasks[task.Name] = task
}
func removeTaskByName(name string) {
delete(tasks, name)
}
func GetTaskByName(name string) *ScheduledTask {
if task, ok := tasks[name]; ok {
return task
} }
if j.CreateAt == 0 {
return NewAppError("Job.IsValid", "model.job.is_valid.create_at.app_error", nil, "id="+j.Id, http.StatusBadRequest)
}
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 return nil
} }
func GetAllTasks() *map[string]*ScheduledTask { func (js *Job) ToJson() string {
return &tasks if b, err := json.Marshal(js); err != nil {
} return ""
} else {
func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask { return string(b)
task := &ScheduledTask{
Name: name,
Interval: timeToExecution,
Recurring: false,
function: function,
} }
}
taskRunner := func() { func JobFromJson(data io.Reader) *Job {
go task.function() var job Job
removeTaskByName(task.Name) if err := json.NewDecoder(data).Decode(&job); err == nil {
return &job
} else {
return nil
} }
task.timer = time.AfterFunc(timeToExecution, taskRunner)
addTask(task)
return task
} }
func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask { func JobsToJson(jobs []*Job) string {
task := &ScheduledTask{ if b, err := json.Marshal(jobs); err != nil {
Name: name, return ""
Interval: interval, } else {
Recurring: true, return string(b)
function: function,
} }
}
taskRecurer := func() { func JobsFromJson(data io.Reader) []*Job {
go task.function() var jobs []*Job
task.timer.Reset(task.Interval) if err := json.NewDecoder(data).Decode(&jobs); err == nil {
return jobs
} else {
return nil
} }
task.timer = time.AfterFunc(interval, taskRecurer)
addTask(task)
return task
} }
func (task *ScheduledTask) Cancel() { func (js *Job) DataToJson() string {
task.timer.Stop() if b, err := json.Marshal(js.Data); err != nil {
removeTaskByName(task.Name) return ""
} else {
return string(b)
}
} }
// Executes the task immediatly. A recurring task will be run regularally after interval. type Worker interface {
func (task *ScheduledTask) Execute() { Run()
task.function() Stop()
task.timer.Reset(task.Interval) JobChannel() chan<- Job
} }
func (task *ScheduledTask) String() string { type Scheduler interface {
return fmt.Sprintf( Run()
"%s\nInterval: %s\nRecurring: %t\n", Stop()
task.Name,
task.Interval.String(),
task.Recurring,
)
} }

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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -37,36 +37,42 @@ type Customer struct {
} }
type Features struct { type Features struct {
Users *int `json:"users"` Users *int `json:"users"`
LDAP *bool `json:"ldap"` LDAP *bool `json:"ldap"`
MFA *bool `json:"mfa"` MFA *bool `json:"mfa"`
GoogleOAuth *bool `json:"google_oauth"` GoogleOAuth *bool `json:"google_oauth"`
Office365OAuth *bool `json:"office365_oauth"` Office365OAuth *bool `json:"office365_oauth"`
Compliance *bool `json:"compliance"` Compliance *bool `json:"compliance"`
Cluster *bool `json:"cluster"` Cluster *bool `json:"cluster"`
Metrics *bool `json:"metrics"` Metrics *bool `json:"metrics"`
CustomBrand *bool `json:"custom_brand"` CustomBrand *bool `json:"custom_brand"`
MHPNS *bool `json:"mhpns"` MHPNS *bool `json:"mhpns"`
SAML *bool `json:"saml"` SAML *bool `json:"saml"`
PasswordRequirements *bool `json:"password_requirements"` 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 // after we enabled more features for webrtc we'll need to control them with this
FutureFeatures *bool `json:"future_features"` FutureFeatures *bool `json:"future_features"`
} }
func (f *Features) ToMap() map[string]interface{} { func (f *Features) ToMap() map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
"ldap": *f.LDAP, "ldap": *f.LDAP,
"mfa": *f.MFA, "mfa": *f.MFA,
"google": *f.GoogleOAuth, "google": *f.GoogleOAuth,
"office365": *f.Office365OAuth, "office365": *f.Office365OAuth,
"compliance": *f.Compliance, "compliance": *f.Compliance,
"cluster": *f.Cluster, "cluster": *f.Cluster,
"metrics": *f.Metrics, "metrics": *f.Metrics,
"custom_brand": *f.CustomBrand, "custom_brand": *f.CustomBrand,
"mhpns": *f.MHPNS, "mhpns": *f.MHPNS,
"saml": *f.SAML, "saml": *f.SAML,
"password": *f.PasswordRequirements, "password": *f.PasswordRequirements,
"future": *f.FutureFeatures, "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 = new(bool)
*f.PasswordRequirements = *f.FutureFeatures *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 { 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. // See License.txt for license information.
package model package model
@ -7,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net/http"
"unicode/utf8" "unicode/utf8"
) )
@ -15,6 +16,7 @@ const (
OAUTH_ACTION_LOGIN = "login" OAUTH_ACTION_LOGIN = "login"
OAUTH_ACTION_EMAIL_TO_SSO = "email_to_sso" OAUTH_ACTION_EMAIL_TO_SSO = "email_to_sso"
OAUTH_ACTION_SSO_TO_EMAIL = "sso_to_email" OAUTH_ACTION_SSO_TO_EMAIL = "sso_to_email"
OAUTH_ACTION_MOBILE = "mobile"
) )
type OAuthApp struct { type OAuthApp struct {
@ -36,50 +38,50 @@ type OAuthApp struct {
func (a *OAuthApp) IsValid() *AppError { func (a *OAuthApp) IsValid() *AppError {
if len(a.Id) != 26 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { for _, callback := range a.CallbackUrls {
if !IsValidHttpUrl(callback) { 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) { 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 { 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) > 0 {
if len(a.IconURL) > 512 || !IsValidHttpUrl(a.IconURL) { 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. // See License.txt for license information.
package model package model
@ -7,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -41,6 +42,7 @@ type OutgoingWebhookPayload struct {
PostId string `json:"post_id"` PostId string `json:"post_id"`
Text string `json:"text"` Text string `json:"text"`
TriggerWord string `json:"trigger_word"` TriggerWord string `json:"trigger_word"`
FileIds string `json:"file_ids"`
} }
func (o *OutgoingWebhookPayload) ToJSON() string { func (o *OutgoingWebhookPayload) ToJSON() string {
@ -65,6 +67,7 @@ func (o *OutgoingWebhookPayload) ToFormValues() string {
v.Set("post_id", o.PostId) v.Set("post_id", o.PostId)
v.Set("text", o.Text) v.Set("text", o.Text)
v.Set("trigger_word", o.TriggerWord) v.Set("trigger_word", o.TriggerWord)
v.Set("file_ids", o.FileIds)
return v.Encode() return v.Encode()
} }
@ -112,69 +115,69 @@ func OutgoingWebhookListFromJson(data io.Reader) []*OutgoingWebhook {
func (o *OutgoingWebhook) IsValid() *AppError { func (o *OutgoingWebhook) IsValid() *AppError {
if len(o.Id) != 26 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { if len(o.TriggerWords) != 0 {
for _, triggerWord := range o.TriggerWords { for _, triggerWord := range o.TriggerWords {
if len(triggerWord) == 0 { 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 { 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 { for _, callback := range o.CallbackURLs {
if !IsValidHttpUrl(callback) { 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 { 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 { 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 { 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 { 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 return nil
@ -197,8 +200,8 @@ func (o *OutgoingWebhook) PreUpdate() {
o.UpdateAt = GetMillis() o.UpdateAt = GetMillis()
} }
func (o *OutgoingWebhook) HasTriggerWord(word string) bool { func (o *OutgoingWebhook) TriggerWordExactMatch(word string) bool {
if len(o.TriggerWords) == 0 || len(word) == 0 { if len(word) == 0 {
return false return false
} }
@ -212,7 +215,7 @@ func (o *OutgoingWebhook) HasTriggerWord(word string) bool {
} }
func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool { func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool {
if len(o.TriggerWords) == 0 || len(word) == 0 { if len(word) == 0 {
return false return false
} }
@ -224,3 +227,27 @@ func (o *OutgoingWebhook) TriggerWordStartsWith(word string) bool {
return false 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. // See License.txt for license information.
package model package model
@ -38,6 +38,7 @@ type Post struct {
UpdateAt int64 `json:"update_at"` UpdateAt int64 `json:"update_at"`
EditAt int64 `json:"edit_at"` EditAt int64 `json:"edit_at"`
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
IsPinned bool `json:"is_pinned"`
UserId string `json:"user_id"` UserId string `json:"user_id"`
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
RootId string `json:"root_id"` RootId string `json:"root_id"`
@ -53,6 +54,20 @@ type Post struct {
HasReactions bool `json:"has_reactions,omitempty"` 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 { func (o *Post) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {
@ -189,3 +204,45 @@ func (o *Post) AddProp(key string, value interface{}) {
func (o *Post) IsSystemMessage() bool { 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 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -16,14 +16,10 @@ const (
PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step" PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step"
PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings" PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings"
PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post" PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post"
PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel"
PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings" PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings"
PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews" 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" PREFERENCE_CATEGORY_THEME = "theme"
// the name for theme props is the team id // the name for theme props is the team id
@ -37,7 +33,9 @@ const (
PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications" PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval" 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 { 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -36,6 +36,10 @@ type PushNotification struct {
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
ChannelName string `json:"channel_name"` ChannelName string `json:"channel_name"`
Type string `json:"type"` 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 { 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -6,6 +6,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"regexp"
) )
type Reaction struct { 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) 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) 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. // See License.txt for license information.
package model package model
import (
"encoding/json"
"io"
)
const ( const (
USER_AUTH_SERVICE_SAML = "saml" USER_AUTH_SERVICE_SAML = "saml"
USER_AUTH_SERVICE_SAML_TEXT = "With SAML" USER_AUTH_SERVICE_SAML_TEXT = "With SAML"
@ -16,3 +21,29 @@ type SamlAuthRequest struct {
URL string URL string
RelayState 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. // See License.txt for license information.
package model package model
import ( import (
"encoding/json"
"regexp" "regexp"
"strings" "strings"
) )
@ -19,6 +20,15 @@ type SearchParams struct {
OrTerms bool 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"} var searchFlags = [...]string{"from", "channel", "in"}
func splitWordsNoQuotes(text string) []string { 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) { if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0) {
paramsList = append(paramsList, &SearchParams{ paramsList = append(paramsList, &SearchParams{
Terms: "", Terms: "",
IsHashtag: true, IsHashtag: false,
InChannels: inChannels, InChannels: inChannels,
FromUsers: fromUsers, 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -10,11 +10,17 @@ import (
) )
const ( const (
SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" SESSION_COOKIE_TOKEN = "MMAUTHTOKEN"
SESSION_CACHE_SIZE = 35000 SESSION_COOKIE_USER = "MMUSERID"
SESSION_PROP_PLATFORM = "platform" SESSION_CACHE_SIZE = 35000
SESSION_PROP_OS = "os" SESSION_PROP_PLATFORM = "platform"
SESSION_PROP_BROWSER = "browser" 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 { type Session struct {
@ -56,7 +62,9 @@ func (me *Session) PreSave() {
me.Id = NewId() me.Id = NewId()
} }
me.Token = NewId() if me.Token == "" {
me.Token = NewId()
}
me.CreateAt = GetMillis() me.CreateAt = GetMillis()
me.LastActivityAt = me.CreateAt 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. // See License.txt for license information.
package model package model
import (
"fmt"
"strings"
)
type SlackAttachment struct { type SlackAttachment struct {
Id int64 `json:"id"` Id int64 `json:"id"`
Fallback string `json:"fallback"` Fallback string `json:"fallback"`
@ -27,3 +32,48 @@ type SlackAttachmentField struct {
Value interface{} `json:"value"` Value interface{} `json:"value"`
Short bool `json:"short"` 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. // See License.txt for license information.
package model package model
@ -22,7 +22,7 @@ type Status struct {
Status string `json:"status"` Status string `json:"status"`
Manual bool `json:"manual"` Manual bool `json:"manual"`
LastActivityAt int64 `json:"last_activity_at"` LastActivityAt int64 `json:"last_activity_at"`
ActiveChannel string `json:"active_channel" db:"-"` ActiveChannel string `json:"-" db:"-"`
} }
func (o *Status) ToJson() string { 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{} { func StatusMapToInterfaceMap(statusMap map[string]*Status) map[string]interface{} {
interfaceMap := map[string]interface{}{} interfaceMap := map[string]interface{}{}
for _, s := range statusMap { 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -41,6 +41,14 @@ type Team struct {
AllowOpenInvite bool `json:"allow_open_invite"` 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 { type Invites struct {
Invites []map[string]string `json:"invites"` Invites []map[string]string `json:"invites"`
} }
@ -225,7 +233,7 @@ func IsReservedTeamName(s string) bool {
func IsValidTeamName(s string) bool { func IsValidTeamName(s string) bool {
if !IsValidAlphaNum(s, false) { if !IsValidAlphaNum(s) {
return false return false
} }
@ -278,3 +286,45 @@ func (o *Team) SanitizeForNotLoggedIn() {
o.InviteId = "" 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. // See License.txt for license information.
package model 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 { func TeamMemberFromJson(data io.Reader) *TeamMember {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)
var o TeamMember 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 { func TeamMembersToJson(o []*TeamMember) string {
if b, err := json.Marshal(o); err != nil { if b, err := json.Marshal(o); err != nil {
return "[]" 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. // See License.txt for license information.
package model 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. // See License.txt for license information.
package model package model
@ -10,33 +10,42 @@ import (
"net/http" "net/http"
"regexp" "regexp"
"strings" "strings"
"unicode"
"unicode/utf8" "unicode/utf8"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
const ( const (
USER_NOTIFY_ALL = "all" ME = "me"
USER_NOTIFY_MENTION = "mention" USER_NOTIFY_ALL = "all"
USER_NOTIFY_NONE = "none" USER_NOTIFY_MENTION = "mention"
DESKTOP_NOTIFY_PROP = "desktop" USER_NOTIFY_NONE = "none"
MARK_UNREAD_NOTIFY_PROP = "mark_unread" DESKTOP_NOTIFY_PROP = "desktop"
PUSH_NOTIFY_PROP = "push" DESKTOP_SOUND_NOTIFY_PROP = "desktop_sound"
EMAIL_NOTIFY_PROP = "email" 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" DEFAULT_LOCALE = "en"
USER_AUTH_SERVICE_EMAIL = "email" USER_AUTH_SERVICE_EMAIL = "email"
USER_AUTH_SERVICE_USERNAME = "username"
USER_EMAIL_MAX_LENGTH = 128 USER_EMAIL_MAX_LENGTH = 128
USER_NICKNAME_MAX_RUNES = 64 USER_NICKNAME_MAX_RUNES = 64
USER_POSITION_MAX_RUNES = 35 USER_POSITION_MAX_RUNES = 64
USER_FIRST_NAME_MAX_RUNES = 64 USER_FIRST_NAME_MAX_RUNES = 64
USER_LAST_NAME_MAX_RUNES = 64 USER_LAST_NAME_MAX_RUNES = 64
USER_AUTH_DATA_MAX_LENGTH = 128 USER_AUTH_DATA_MAX_LENGTH = 128
USER_NAME_MAX_LENGTH = 64 USER_NAME_MAX_LENGTH = 64
USER_NAME_MIN_LENGTH = 1 USER_NAME_MIN_LENGTH = 1
USER_PASSWORD_MAX_LENGTH = 72
) )
type User struct { type User struct {
@ -68,15 +77,15 @@ type User struct {
} }
type UserPatch struct { type UserPatch struct {
Username *string `json:"username"` Username *string `json:"username"`
Nickname *string `json:"nickname"` Nickname *string `json:"nickname"`
FirstName *string `json:"first_name"` FirstName *string `json:"first_name"`
LastName *string `json:"last_name"` LastName *string `json:"last_name"`
Position *string `json:"position"` Position *string `json:"position"`
Email *string `json:"email"` Email *string `json:"email"`
Props *StringMap `json:"props,omitempty"` Props StringMap `json:"props,omitempty"`
NotifyProps *StringMap `json:"notify_props,omitempty"` NotifyProps StringMap `json:"notify_props,omitempty"`
Locale *string `json:"locale"` Locale *string `json:"locale"`
} }
// IsValid validates the user and returns an error if it isn't configured // IsValid validates the user and returns an error if it isn't configured
@ -84,56 +93,69 @@ type UserPatch struct {
func (u *User) IsValid() *AppError { func (u *User) IsValid() *AppError {
if len(u.Id) != 26 { 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 { 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 { 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) { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 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 // 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 // in the CreateAt, UpdateAt times. It will also hash the password. It should
// be run before saving the user to the db. // be run before saving the user to the db.
@ -143,7 +165,7 @@ func (u *User) PreSave() {
} }
if u.Username == "" { if u.Username == "" {
u.Username = "n" + NewId() u.Username = NewId()
} }
if u.AuthData != nil && *u.AuthData == "" { if u.AuthData != nil && *u.AuthData == "" {
@ -259,11 +281,11 @@ func (u *User) Patch(patch *UserPatch) {
} }
if patch.Props != nil { if patch.Props != nil {
u.Props = *patch.Props u.Props = patch.Props
} }
if patch.NotifyProps != nil { if patch.NotifyProps != nil {
u.NotifyProps = *patch.NotifyProps u.NotifyProps = patch.NotifyProps
} }
if patch.Locale != nil { if patch.Locale != nil {
@ -327,7 +349,6 @@ func (u *User) ClearNonProfileFields() {
u.Props = StringMap{} u.Props = StringMap{}
u.NotifyProps = StringMap{} u.NotifyProps = StringMap{}
u.LastPasswordUpdate = 0 u.LastPasswordUpdate = 0
u.LastPictureUpdate = 0
u.FailedAttempts = 0 u.FailedAttempts = 0
} }
@ -371,26 +392,16 @@ func (u *User) GetFullName() string {
} }
} }
func (u *User) GetDisplayName() string { func (u *User) GetDisplayName(nameFormat string) 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 {
displayName := u.Username displayName := u.Username
if nameFormat == PREFERENCE_VALUE_DISPLAY_NAME_NICKNAME { if nameFormat == SHOW_NICKNAME_FULLNAME {
if u.Nickname != "" { if u.Nickname != "" {
displayName = u.Nickname displayName = u.Nickname
} else if fullName := u.GetFullName(); fullName != "" { } else if fullName := u.GetFullName(); fullName != "" {
displayName = fullName displayName = fullName
} }
} else if nameFormat == PREFERENCE_VALUE_DISPLAY_NAME_FULL { } else if nameFormat == SHOW_FULLNAME {
if fullName := u.GetFullName(); fullName != "" { if fullName := u.GetFullName(); fullName != "" {
displayName = fullName displayName = fullName
} }
@ -445,31 +456,25 @@ func IsInRole(userRoles string, inRole string) bool {
if r == inRole { if r == inRole {
return true return true
} }
} }
return false return false
} }
func (u *User) IsSSOUser() bool { func (u *User) IsSSOUser() bool {
if u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL { return u.AuthService != "" && u.AuthService != USER_AUTH_SERVICE_EMAIL
return true
}
return false
} }
func (u *User) IsOAuthUser() bool { func (u *User) IsOAuthUser() bool {
if u.AuthService == USER_AUTH_SERVICE_GITLAB { return u.AuthService == USER_AUTH_SERVICE_GITLAB
return true
}
return false
} }
func (u *User) IsLDAPUser() bool { func (u *User) IsLDAPUser() bool {
if u.AuthService == USER_AUTH_SERVICE_LDAP { return u.AuthService == USER_AUTH_SERVICE_LDAP
return true }
}
return false func (u *User) IsSAMLUser() bool {
return u.AuthService == USER_AUTH_SERVICE_SAML
} }
// UserFromJson will decode the input and return a User // UserFromJson will decode the input and return a User
@ -573,10 +578,6 @@ func IsValidUsername(s string) bool {
return false return false
} }
if !unicode.IsLetter(rune(s[0])) {
return false
}
for _, restrictedUsername := range restrictedUsernames { for _, restrictedUsername := range restrictedUsernames {
if s == restrictedUsername { if s == restrictedUsername {
return false return false
@ -612,3 +613,21 @@ func CleanUsername(s string) string {
return s 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. // See License.txt for license information.
package model package model
@ -17,6 +17,31 @@ type UserAutocompleteInTeam struct {
InTeam []*User `json:"in_team"` 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 { func (o *UserAutocompleteInChannel) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { 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. // See License.txt for license information.
package model package model
@ -11,9 +11,11 @@ import (
type UserSearch struct { type UserSearch struct {
Term string `json:"term"` Term string `json:"term"`
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
NotInTeamId string `json:"not_in_team_id"`
InChannelId string `json:"in_channel_id"` InChannelId string `json:"in_channel_id"`
NotInChannelId string `json:"not_in_channel_id"` NotInChannelId string `json:"not_in_channel_id"`
AllowInactive bool `json:"allow_inactive"` AllowInactive bool `json:"allow_inactive"`
WithoutTeam bool `json:"without_team"`
} }
// ToJson convert a User to a json string // 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. // See License.txt for license information.
package model package model
@ -11,9 +11,11 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net"
"net/mail" "net/mail"
"net/url" "net/url"
"regexp" "regexp"
"strconv"
"strings" "strings"
"time" "time"
@ -31,7 +33,6 @@ const (
type StringInterface map[string]interface{} type StringInterface map[string]interface{}
type StringMap map[string]string type StringMap map[string]string
type StringArray []string type StringArray []string
type EncryptStringMap map[string]string
type AppError struct { type AppError struct {
Id string `json:"id"` 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 // MapFromJson will decode the key/value pair map
func MapFromJson(data io.Reader) map[string]string { func MapFromJson(data io.Reader) map[string]string {
decoder := json.NewDecoder(data) 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 { func ArrayToJson(objmap []string) string {
if b, err := json.Marshal(objmap); err != nil { if b, err := json.Marshal(objmap); err != nil {
return "" 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 { func IsLower(s string) bool {
if strings.ToLower(s) == s { if strings.ToLower(s) == s {
return true return true
@ -276,7 +315,7 @@ var reservedName = []string{
func IsValidChannelIdentifier(s string) bool { func IsValidChannelIdentifier(s string) bool {
if !IsValidAlphaNum(s, true) { if !IsValidAlphaNumHyphenUnderscore(s, true) {
return false return false
} }
@ -287,22 +326,20 @@ func IsValidChannelIdentifier(s string) bool {
return true return true
} }
var validAlphaNumUnderscore = regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`) func IsValidAlphaNum(s string) bool {
var validAlphaNum = regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`) validAlphaNum := regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`)
func IsValidAlphaNum(s string, allowUnderscores bool) bool { return validAlphaNum.MatchString(s)
var match bool }
if allowUnderscores {
match = validAlphaNumUnderscore.MatchString(s) func IsValidAlphaNumHyphenUnderscore(s string, withFormat bool) bool {
} else { if withFormat {
match = validAlphaNum.MatchString(s) validAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`)
return validAlphaNumHyphenUnderscore.MatchString(s)
} }
if !match { validSimpleAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
return false return validSimpleAlphaNumHyphenUnderscore.MatchString(s)
}
return true
} }
func Etag(parts ...interface{}) string { 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 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 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 { func IsValidHttpUrl(rawUrl string) bool {
if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 { if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
return false return false
@ -445,3 +480,15 @@ func IsValidWebsocketUrl(rawUrl string) bool {
return true 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. // See License.txt for license information.
package model package model
@ -13,6 +13,11 @@ import (
// It should be maitained in chronological order with most current // It should be maitained in chronological order with most current
// release at the front of the list. // release at the front of the list.
var versions = []string{ var versions = []string{
"4.1.0",
"4.0.0",
"3.10.0",
"3.9.0",
"3.8.0",
"3.7.0", "3.7.0",
"3.6.0", "3.6.0",
"3.5.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 package model
import ( import (
@ -5,6 +8,15 @@ import (
"io" "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 { type GatewayResponse struct {
Status string `json:"janus"` Status string `json:"janus"`
} }
@ -19,3 +31,23 @@ func GatewayResponseFromJson(data io.Reader) *GatewayResponse {
return nil 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. // See License.txt for license information.
package model package model
@ -15,6 +15,7 @@ const (
type WebSocketClient struct { type WebSocketClient struct {
Url string // The location of the server like "ws://localhost:8065" 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" 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 Conn *websocket.Conn // The WebSocket connection
AuthToken string // The token used to open the WebSocket AuthToken string // The token used to open the WebSocket
Sequence int64 // The ever-incrementing sequence attached to each WebSocket action Sequence int64 // The ever-incrementing sequence attached to each WebSocket action
@ -34,6 +35,32 @@ func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
client := &WebSocketClient{ client := &WebSocketClient{
url, url,
url + API_URL_SUFFIX_V3, 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, conn,
authToken, authToken,
1, 1,
@ -49,9 +76,9 @@ func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
func (wsc *WebSocketClient) Connect() *AppError { func (wsc *WebSocketClient) Connect() *AppError {
var err error 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 { 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) 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. // See License.txt for license information.
package model package model
@ -9,35 +9,41 @@ import (
) )
const ( const (
WEBSOCKET_EVENT_TYPING = "typing" WEBSOCKET_EVENT_TYPING = "typing"
WEBSOCKET_EVENT_POSTED = "posted" WEBSOCKET_EVENT_POSTED = "posted"
WEBSOCKET_EVENT_POST_EDITED = "post_edited" WEBSOCKET_EVENT_POST_EDITED = "post_edited"
WEBSOCKET_EVENT_POST_DELETED = "post_deleted" WEBSOCKET_EVENT_POST_DELETED = "post_deleted"
WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted" WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted"
WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created" WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created"
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" WEBSOCKET_EVENT_CHANNEL_UPDATED = "channel_updated"
WEBSOCKET_EVENT_GROUP_ADDED = "group_added" WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
WEBSOCKET_EVENT_NEW_USER = "new_user" WEBSOCKET_EVENT_GROUP_ADDED = "group_added"
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" WEBSOCKET_EVENT_NEW_USER = "new_user"
WEBSOCKET_EVENT_UPDATE_TEAM = "update_team" WEBSOCKET_EVENT_ADDED_TO_TEAM = "added_to_team"
WEBSOCKET_EVENT_USER_ADDED = "user_added" WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
WEBSOCKET_EVENT_USER_UPDATED = "user_updated" WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
WEBSOCKET_EVENT_USER_REMOVED = "user_removed" WEBSOCKET_EVENT_USER_ADDED = "user_added"
WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed" WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message" WEBSOCKET_EVENT_MEMBERROLE_UPDATED = "memberrole_updated"
WEBSOCKET_EVENT_STATUS_CHANGE = "status_change" WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
WEBSOCKET_EVENT_HELLO = "hello" WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed"
WEBSOCKET_EVENT_WEBRTC = "webrtc" WEBSOCKET_EVENT_PREFERENCES_CHANGED = "preferences_changed"
WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge" WEBSOCKET_EVENT_PREFERENCES_DELETED = "preferences_deleted"
WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added" WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message"
WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed" WEBSOCKET_EVENT_STATUS_CHANGE = "status_change"
WEBSOCKET_EVENT_HELLO = "hello"
WEBSOCKET_EVENT_WEBRTC = "webrtc"
WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge"
WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added"
WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed"
WEBSOCKET_EVENT_RESPONSE = "response"
WEBSOCKET_EVENT_EMOJI_ADDED = "emoji_added"
WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed"
) )
type WebSocketMessage interface { type WebSocketMessage interface {
ToJson() string ToJson() string
IsValid() bool IsValid() bool
DoPreComputeJson()
GetPreComputeJson() []byte
EventType() string EventType() string
} }
@ -49,10 +55,10 @@ type WebsocketBroadcast struct {
} }
type WebSocketEvent struct { type WebSocketEvent struct {
Event string `json:"event"` Event string `json:"event"`
Data map[string]interface{} `json:"data"` Data map[string]interface{} `json:"data"`
Broadcast *WebsocketBroadcast `json:"broadcast"` Broadcast *WebsocketBroadcast `json:"broadcast"`
PreComputeJson []byte `json:"-"` Sequence int64 `json:"seq"`
} }
func (m *WebSocketEvent) Add(key string, value interface{}) { func (m *WebSocketEvent) Add(key string, value interface{}) {
@ -72,19 +78,6 @@ func (o *WebSocketEvent) EventType() string {
return o.Event 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 { func (o *WebSocketEvent) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {
@ -106,11 +99,10 @@ func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
} }
type WebSocketResponse struct { type WebSocketResponse struct {
Status string `json:"status"` Status string `json:"status"`
SeqReply int64 `json:"seq_reply,omitempty"` SeqReply int64 `json:"seq_reply,omitempty"`
Data map[string]interface{} `json:"data,omitempty"` Data map[string]interface{} `json:"data,omitempty"`
Error *AppError `json:"error,omitempty"` Error *AppError `json:"error,omitempty"`
PreComputeJson []byte `json:"-"`
} }
func (m *WebSocketResponse) Add(key string, value interface{}) { func (m *WebSocketResponse) Add(key string, value interface{}) {
@ -130,7 +122,7 @@ func (o *WebSocketResponse) IsValid() bool {
} }
func (o *WebSocketResponse) EventType() string { func (o *WebSocketResponse) EventType() string {
return "" return WEBSOCKET_EVENT_RESPONSE
} }
func (o *WebSocketResponse) ToJson() string { 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 { func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)
var o WebSocketResponse 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. // See License.txt for license information.
package model package model

View File

@ -66,8 +66,9 @@ type Dialer struct {
// HandshakeTimeout specifies the duration for the handshake to complete. // HandshakeTimeout specifies the duration for the handshake to complete.
HandshakeTimeout time.Duration HandshakeTimeout time.Duration
// Input and output buffer sizes. If the buffer size is zero, then a // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
// default value of 4096 is used. // 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 ReadBufferSize, WriteBufferSize int
// Subprotocols specifies the client's requested subprotocols. // 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 return nil, resp, ErrBadHandshake
} }
for _, ext := range parseExtensions(req.Header) { for _, ext := range parseExtensions(resp.Header) {
if ext[""] != "permessage-deflate" { if ext[""] != "permessage-deflate" {
continue continue
} }
@ -389,32 +390,3 @@ func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Re
netConn = nil // to avoid close in defer. netConn = nil // to avoid close in defer.
return conn, resp, nil 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" "sync"
) )
var ( const (
flateWriterPool = sync.Pool{} 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 = const tail =
// Add four bytes as specified in RFC // Add four bytes as specified in RFC
"\x00\x00\xff\xff" + "\x00\x00\xff\xff" +
// Add final block to squelch unexpected EOF error from flate reader. // Add final block to squelch unexpected EOF error from flate reader.
"\x01\x00\x00\xff\xff" "\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} tw := &truncWriter{w: w}
i := flateWriterPool.Get() fw, _ := p.Get().(*flate.Writer)
var fw *flate.Writer if fw == nil {
var err error fw, _ = flate.NewWriter(tw, level)
if i == nil {
fw, err = flate.NewWriter(tw, 3)
} else { } else {
fw = i.(*flate.Writer)
fw.Reset(tw) 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 // 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 return n + nn, err
} }
type flateWrapper struct { type flateWriteWrapper struct {
fw *flate.Writer fw *flate.Writer
tw *truncWriter 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 { if w.fw == nil {
return 0, errWriteClosed return 0, errWriteClosed
} }
return w.fw.Write(p) return w.fw.Write(p)
} }
func (w *flateWrapper) Close() error { func (w *flateWriteWrapper) Close() error {
if w.fw == nil { if w.fw == nil {
return errWriteClosed return errWriteClosed
} }
err1 := w.fw.Flush() err1 := w.fw.Flush()
flateWriterPool.Put(w.fw) w.p.Put(w.fw)
w.fw = nil w.fw = nil
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
return errors.New("websocket: internal error, unexpected bytes at end of flate stream") return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
@ -103,3 +118,31 @@ func (w *flateWrapper) Close() error {
} }
return err2 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" "errors"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand"
"net" "net"
"strconv" "strconv"
"sync" "sync"
@ -180,6 +181,11 @@ var (
errInvalidControlFrame = errors.New("websocket: invalid control frame") 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 { func hideTempErr(err error) error {
if e, ok := err.(net.Error); ok && e.Temporary() { if e, ok := err.(net.Error); ok && e.Temporary() {
err = &netError{msg: e.Error(), timeout: e.Timeout()} err = &netError{msg: e.Error(), timeout: e.Timeout()}
@ -235,9 +241,11 @@ type Conn struct {
writeErr error writeErr error
enableWriteCompression bool enableWriteCompression bool
newCompressionWriter func(io.WriteCloser) (io.WriteCloser, error) compressionLevel int
newCompressionWriter func(io.WriteCloser, int) io.WriteCloser
// Read fields // Read fields
reader io.ReadCloser // the current reader returned to the application
readErr error readErr error
br *bufio.Reader br *bufio.Reader
readRemaining int64 // bytes remaining in current frame. readRemaining int64 // bytes remaining in current frame.
@ -253,31 +261,76 @@ type Conn struct {
messageReader *messageReader // the current low-level reader messageReader *messageReader // the current low-level reader
readDecompress bool // whether last read frame had RSV1 set 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 { 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 := make(chan bool, 1)
mu <- true mu <- true
if readBufferSize == 0 { var br *bufio.Reader
readBufferSize = defaultReadBufferSize 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 readBufferSize < maxControlFramePayloadSize { if br == nil {
readBufferSize = maxControlFramePayloadSize if readBufferSize == 0 {
readBufferSize = defaultReadBufferSize
}
if readBufferSize < maxControlFramePayloadSize {
readBufferSize = maxControlFramePayloadSize
}
br = bufio.NewReaderSize(conn, readBufferSize)
} }
if writeBufferSize == 0 {
writeBufferSize = defaultWriteBufferSize 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{ c := &Conn{
isServer: isServer, isServer: isServer,
br: bufio.NewReaderSize(conn, readBufferSize), br: br,
conn: conn, conn: conn,
mu: mu, mu: mu,
readFinal: true, readFinal: true,
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize), writeBuf: writeBuf,
enableWriteCompression: true, enableWriteCompression: true,
compressionLevel: defaultCompressionLevel,
} }
c.SetCloseHandler(nil) c.SetCloseHandler(nil)
c.SetPingHandler(nil) c.SetPingHandler(nil)
@ -443,11 +496,7 @@ func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
} }
c.writer = mw c.writer = mw
if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
w, err := c.newCompressionWriter(c.writer) w := c.newCompressionWriter(c.writer, c.compressionLevel)
if err != nil {
c.writer = nil
return nil, err
}
mw.compress = true mw.compress = true
c.writer = w c.writer = w
} }
@ -654,12 +703,33 @@ func (w *messageWriter) Close() error {
return nil 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, // WriteMessage is a helper method for getting a writer using NextWriter,
// writing the message and closing the writer. // writing the message and closing the writer.
func (c *Conn) WriteMessage(messageType int, data []byte) error { func (c *Conn) WriteMessage(messageType int, data []byte) error {
if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
// Fast path with no allocations and single frame. // Fast path with no allocations and single frame.
if err := c.prepWrite(messageType); err != nil { 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 // permanent. Once this method returns a non-nil error, all subsequent calls to
// this method return the same error. // this method return the same error.
func (c *Conn) NextReader() (messageType int, r io.Reader, err 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.messageReader = nil
c.readLength = 0 c.readLength = 0
@ -867,11 +942,11 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
} }
if frameType == TextMessage || frameType == BinaryMessage { if frameType == TextMessage || frameType == BinaryMessage {
c.messageReader = &messageReader{c} c.messageReader = &messageReader{c}
var r io.Reader = c.messageReader c.reader = c.messageReader
if c.readDecompress { 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 return 0, err
} }
func (r *messageReader) Close() error {
return nil
}
// ReadMessage is a helper method for getting a reader using NextReader and // ReadMessage is a helper method for getting a reader using NextReader and
// reading from that reader to a buffer. // reading from that reader to a buffer.
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { 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 // 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 // if the close message is empty. The default close handler sends a close frame
// back to the peer. // 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) { func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
if h == nil { if h == nil {
h = func(code int, text string) error { 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. // SetPingHandler sets the handler for ping messages received from the peer.
// The appData argument to h is the PING frame application data. The default // The appData argument to h is the PING frame application data. The default
// ping handler sends a pong to the peer. // 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) { func (c *Conn) SetPingHandler(h func(appData string) error) {
if h == nil { if h == nil {
h = func(message string) error { 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. // SetPongHandler sets the handler for pong messages received from the peer.
// The appData argument to h is the PONG frame application data. The default // The appData argument to h is the PONG frame application data. The default
// pong handler does nothing. // 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) { func (c *Conn) SetPongHandler(h func(appData string) error) {
if h == nil { if h == nil {
h = func(string) error { return nil } h = func(string) error { return nil }
@ -1034,6 +1128,18 @@ func (c *Conn) EnableWriteCompression(enable bool) {
c.enableWriteCompression = enable 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. // FormatCloseMessage formats closeCode and text as a WebSocket close message.
func FormatCloseMessage(closeCode int, text string) []byte { func FormatCloseMessage(closeCode int, text string) []byte {
buf := make([]byte, 2+len(text)) buf := make([]byte, 2+len(text))

View File

@ -118,9 +118,10 @@
// //
// Applications are responsible for ensuring that no more than one goroutine // Applications are responsible for ensuring that no more than one goroutine
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
// WriteJSON) concurrently and that no more than one goroutine calls the read // WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, // that no more than one goroutine calls the read methods (NextReader,
// SetPingHandler) concurrently. // SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
// concurrently.
// //
// The Close and WriteControl methods can be called concurrently with all other // The Close and WriteControl methods can be called concurrently with all other
// methods. // methods.
@ -150,19 +151,25 @@
// application's responsibility to check the Origin header before calling // application's responsibility to check the Origin header before calling
// Upgrade. // Upgrade.
// //
// Compression [Experimental] // Compression EXPERIMENTAL
// //
// Per message compression extensions (RFC 7692) are experimentally supported // Per message compression extensions (RFC 7692) are experimentally supported
// by this package in a limited capacity. Setting the EnableCompression option // by this package in a limited capacity. Setting the EnableCompression option
// to true in Dialer or Upgrader will attempt to negotiate per message deflate // to true in Dialer or Upgrader will attempt to negotiate per message deflate
// support. If compression was successfully negotiated with the connection's // support.
// peer, any message received in compressed form will be automatically //
// decompressed. All Read methods will return uncompressed bytes. // 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 // Per message compression of messages written to a connection can be enabled
// or disabled by calling the corresponding Conn method: // or disabled by calling the corresponding Conn method:
// //
// conn.EnableWriteCompression(true) // conn.EnableWriteCompression(false)
// //
// Currently this package does not support compression with "context takeover". // Currently this package does not support compression with "context takeover".
// This means that messages must be compressed and decompressed in isolation, // 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 // echoReadAll echoes messages from the client by reading the entire message
// with ioutil.ReadAll. // 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) conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
log.Println("Upgrade:", err) log.Println("Upgrade:", err)
@ -109,9 +109,21 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
} }
} }
if writeMessage { if writeMessage {
err = conn.WriteMessage(mt, b) if !writePrepared {
if err != nil { err = conn.WriteMessage(mt, b)
log.Println("WriteMessage:", err) 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 { } else {
w, err := conn.NextWriter(mt) w, err := conn.NextWriter(mt)
@ -132,11 +144,15 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
} }
func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { 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) { 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) { func serveHome(w http.ResponseWriter, r *http.Request) {
@ -161,6 +177,7 @@ func main() {
http.HandleFunc("/f", echoCopyFull) http.HandleFunc("/f", echoCopyFull)
http.HandleFunc("/r", echoReadAllWriter) http.HandleFunc("/r", echoReadAllWriter)
http.HandleFunc("/m", echoReadAllWriteMessage) http.HandleFunc("/m", echoReadAllWriteMessage)
http.HandleFunc("/p", echoReadAllWritePreparedMessage)
err := http.ListenAndServe(*addr, nil) err := http.ListenAndServe(*addr, nil)
if err != nil { if err != nil {
log.Fatal("ListenAndServe: ", err) 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 := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
client.hub.register <- client client.hub.register <- client
// Allow collection of memory referenced by the caller by doing all work in
// new goroutines.
go client.writePump() go client.writePump()
client.readPump() go client.readPump()
} }

View File

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

View File

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

View File

@ -6,12 +6,12 @@ package main
import ( import (
"flag" "flag"
"html/template"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
"text/template"
"time" "time"
"github.com/gorilla/websocket" "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 // this source code is governed by a BSD-style license that can be found in the
// LICENSE file. // LICENSE file.
// +build !appengine
package websocket package websocket
import ( import "unsafe"
"math/rand"
"unsafe"
)
const wordSize = int(unsafe.Sizeof(uintptr(0))) 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 { func maskBytes(key [4]byte, pos int, b []byte) int {
// Mask one byte at a time for small buffers. // 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 HandshakeTimeout time.Duration
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer // 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 // size is zero, then buffers allocated by the HTTP server are used. The
// do not limit the size of the messages that can be sent or received. // I/O buffer sizes do not limit the size of the messages that can be sent
// or received.
ReadBufferSize, WriteBufferSize int ReadBufferSize, WriteBufferSize int
// Subprotocols specifies the server's supported protocols in order of // 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. // response.
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
if r.Method != "GET" { 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 { if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific Sec-Websocket-Extensions headers are unsupported") 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")
} }
if !tokenListContainsValue(r.Header, "Connection", "upgrade") { 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") { 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 checkOrigin := u.CheckOrigin
@ -128,12 +129,12 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
checkOrigin = checkSameOrigin checkOrigin = checkSameOrigin
} }
if !checkOrigin(r) { 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") challengeKey := r.Header.Get("Sec-Websocket-Key")
if challengeKey == "" { 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) subprotocol := u.selectSubprotocol(r, responseHeader)
@ -152,7 +153,6 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
var ( var (
netConn net.Conn netConn net.Conn
br *bufio.Reader
err error err error
) )
@ -160,19 +160,18 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
if !ok { if !ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
} }
var rw *bufio.ReadWriter var brw *bufio.ReadWriter
netConn, rw, err = h.Hijack() netConn, brw, err = h.Hijack()
if err != nil { if err != nil {
return u.returnError(w, r, http.StatusInternalServerError, err.Error()) return u.returnError(w, r, http.StatusInternalServerError, err.Error())
} }
br = rw.Reader
if br.Buffered() > 0 { if brw.Reader.Buffered() > 0 {
netConn.Close() netConn.Close()
return nil, errors.New("websocket: client sent data before handshake is complete") 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 c.subprotocol = subprotocol
if compress { if compress {

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