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"`
InterNodeUrl string `json:"internode_url"` IpAddress string `json:"ipaddress"`
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 {
UserId string `json:"user_id"`
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
TeamId string `json:"team_id"`
RootId string `json:"root_id"` RootId string `json:"root_id"`
ParentId string `json:"parent_id"` ParentId string `json:"parent_id"`
Command string `json:"command"` 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_NO_CHANNEL_NOTIFICATION = "generic_no_channel"
GENERIC_NOTIFICATION = "generic" GENERIC_NOTIFICATION = "generic"
FULL_NOTIFICATION = "full" 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 = ""
@ -80,6 +89,9 @@ const (
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_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" SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = "" LDAP_SETTINGS_DEFAULT_FIRST_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 {
@ -184,10 +218,12 @@ type SqlSettings struct {
DriverName string DriverName string
DataSource string DataSource string
DataSourceReplicas []string DataSourceReplicas []string
DataSourceSearchReplicas []string
MaxIdleConns int MaxIdleConns int
MaxOpenConns int MaxOpenConns int
Trace bool Trace bool
AtRestEncryptKey string 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,6 +261,8 @@ 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 {
@ -239,19 +274,21 @@ type EmailSettings struct {
FeedbackName string FeedbackName string
FeedbackEmail string FeedbackEmail string
FeedbackOrganization *string FeedbackOrganization *string
EnableSMTPAuth *bool
SMTPUsername string SMTPUsername string
SMTPPassword string SMTPPassword string
SMTPServer string SMTPServer string
SMTPPort string SMTPPort string
ConnectionSecurity string ConnectionSecurity string
InviteSalt string InviteSalt string
PasswordResetSalt 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 {
@ -274,9 +311,20 @@ type SupportSettings struct {
AboutLink *string AboutLink *string
HelpLink *string HelpLink *string
ReportAProblemLink *string ReportAProblemLink *string
AdministratorsGuideLink *string
TroubleshootingForumLink *string
CommercialSupportLink *string
SupportEmail *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
@ -295,9 +343,11 @@ type TeamSettings struct {
RestrictPrivateChannelCreation *string RestrictPrivateChannelCreation *string
RestrictPublicChannelDeletion *string RestrictPublicChannelDeletion *string
RestrictPrivateChannelDeletion *string RestrictPrivateChannelDeletion *string
RestrictPrivateChannelManageMembers *string
UserStatusAwayTimeout *int64 UserStatusAwayTimeout *int64
MaxChannelsPerTeam *int64 MaxChannelsPerTeam *int64
MaxNotificationsPerChannel *int64 MaxNotificationsPerChannel *int64
TeammateNameDisplay *string
} }
type LdapSettings struct { type LdapSettings struct {
@ -389,6 +439,30 @@ 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
@ -400,6 +474,7 @@ type Config struct {
RateLimitSettings RateLimitSettings RateLimitSettings RateLimitSettings
PrivacySettings PrivacySettings PrivacySettings PrivacySettings
SupportSettings SupportSettings SupportSettings SupportSettings
AnnouncementSettings AnnouncementSettings
GitLabSettings SSOSettings GitLabSettings SSOSettings
GoogleSettings SSOSettings GoogleSettings SSOSettings
Office365Settings SSOSettings Office365Settings SSOSettings
@ -412,6 +487,10 @@ type Config struct {
MetricsSettings MetricsSettings MetricsSettings MetricsSettings
AnalyticsSettings AnalyticsSettings AnalyticsSettings AnalyticsSettings
WebrtcSettings WebrtcSettings 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,14 +712,22 @@ 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.
if *o.TeamSettings.RestrictPublicChannelManagement == PERMISSIONS_CHANNEL_ADMIN {
*o.TeamSettings.RestrictPublicChannelCreation = PERMISSIONS_TEAM_ADMIN
} else {
*o.TeamSettings.RestrictPublicChannelCreation = *o.TeamSettings.RestrictPublicChannelManagement *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.
if *o.TeamSettings.RestrictPrivateChannelManagement == PERMISSIONS_CHANNEL_ADMIN {
*o.TeamSettings.RestrictPrivateChannelCreation = PERMISSIONS_TEAM_ADMIN
} else {
*o.TeamSettings.RestrictPrivateChannelCreation = *o.TeamSettings.RestrictPrivateChannelManagement *o.TeamSettings.RestrictPrivateChannelCreation = *o.TeamSettings.RestrictPrivateChannelManagement
} }
}
if o.TeamSettings.RestrictPublicChannelDeletion == nil { if o.TeamSettings.RestrictPublicChannelDeletion == nil {
o.TeamSettings.RestrictPublicChannelDeletion = new(string) o.TeamSettings.RestrictPublicChannelDeletion = new(string)
@ -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) { if j.CreateAt == 0 {
delete(tasks, name) return NewAppError("Job.IsValid", "model.job.is_valid.create_at.app_error", nil, "id="+j.Id, http.StatusBadRequest)
} }
func GetTaskByName(name string) *ScheduledTask { switch j.Type {
if task, ok := tasks[name]; ok { case JOB_TYPE_DATA_RETENTION:
return task 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 {
return string(b)
}
} }
func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask { func JobFromJson(data io.Reader) *Job {
task := &ScheduledTask{ var job Job
Name: name, if err := json.NewDecoder(data).Decode(&job); err == nil {
Interval: timeToExecution, return &job
Recurring: false, } else {
function: function, return nil
}
} }
taskRunner := func() { func JobsToJson(jobs []*Job) string {
go task.function() if b, err := json.Marshal(jobs); err != nil {
removeTaskByName(task.Name) return ""
} else {
return string(b)
}
} }
task.timer = time.AfterFunc(timeToExecution, taskRunner) func JobsFromJson(data io.Reader) []*Job {
var jobs []*Job
addTask(task) if err := json.NewDecoder(data).Decode(&jobs); err == nil {
return jobs
return task } else {
return nil
}
} }
func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask { func (js *Job) DataToJson() string {
task := &ScheduledTask{ if b, err := json.Marshal(js.Data); err != nil {
Name: name, return ""
Interval: interval, } else {
Recurring: true, return string(b)
function: function, }
} }
taskRecurer := func() { type Worker interface {
go task.function() Run()
task.timer.Reset(task.Interval) Stop()
JobChannel() chan<- Job
} }
task.timer = time.AfterFunc(interval, taskRecurer) type Scheduler interface {
Run()
addTask(task) Stop()
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,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
@ -49,6 +49,10 @@ type Features struct {
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"`
} }
@ -66,6 +70,8 @@ func (f *Features) ToMap() map[string]interface{} {
"mhpns": *f.MHPNS, "mhpns": *f.MHPNS,
"saml": *f.SAML, "saml": *f.SAML,
"password": *f.PasswordRequirements, "password": *f.PasswordRequirements,
"elastic_search": *f.Elasticsearch,
"email_notification_contents": *f.EmailNotificationContents,
"future": *f.FutureFeatures, "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
@ -11,10 +11,16 @@ import (
const ( const (
SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" SESSION_COOKIE_TOKEN = "MMAUTHTOKEN"
SESSION_COOKIE_USER = "MMUSERID"
SESSION_CACHE_SIZE = 35000 SESSION_CACHE_SIZE = 35000
SESSION_PROP_PLATFORM = "platform" SESSION_PROP_PLATFORM = "platform"
SESSION_PROP_OS = "os" SESSION_PROP_OS = "os"
SESSION_PROP_BROWSER = "browser" 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()
} }
if me.Token == "" {
me.Token = NewId() 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 (
ME = "me"
USER_NOTIFY_ALL = "all" USER_NOTIFY_ALL = "all"
USER_NOTIFY_MENTION = "mention" USER_NOTIFY_MENTION = "mention"
USER_NOTIFY_NONE = "none" USER_NOTIFY_NONE = "none"
DESKTOP_NOTIFY_PROP = "desktop" DESKTOP_NOTIFY_PROP = "desktop"
DESKTOP_SOUND_NOTIFY_PROP = "desktop_sound"
DESKTOP_DURATION_NOTIFY_PROP = "desktop_duration"
MARK_UNREAD_NOTIFY_PROP = "mark_unread" MARK_UNREAD_NOTIFY_PROP = "mark_unread"
PUSH_NOTIFY_PROP = "push" PUSH_NOTIFY_PROP = "push"
PUSH_STATUS_NOTIFY_PROP = "push_status"
EMAIL_NOTIFY_PROP = "email" 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 {
@ -74,8 +83,8 @@ type UserPatch struct {
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"`
} }
@ -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)
} else {
match = validAlphaNum.MatchString(s)
} }
if !match { func IsValidAlphaNumHyphenUnderscore(s string, withFormat bool) bool {
return false if withFormat {
validAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`)
return validAlphaNumHyphenUnderscore.MatchString(s)
} }
return true validSimpleAlphaNumHyphenUnderscore := regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
return validSimpleAlphaNumHyphenUnderscore.MatchString(s)
} }
func Etag(parts ...interface{}) string { 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
@ -15,15 +15,20 @@ const (
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_CHANNEL_UPDATED = "channel_updated"
WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added" WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
WEBSOCKET_EVENT_GROUP_ADDED = "group_added" WEBSOCKET_EVENT_GROUP_ADDED = "group_added"
WEBSOCKET_EVENT_NEW_USER = "new_user" WEBSOCKET_EVENT_NEW_USER = "new_user"
WEBSOCKET_EVENT_ADDED_TO_TEAM = "added_to_team"
WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team" WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
WEBSOCKET_EVENT_UPDATE_TEAM = "update_team" WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
WEBSOCKET_EVENT_USER_ADDED = "user_added" WEBSOCKET_EVENT_USER_ADDED = "user_added"
WEBSOCKET_EVENT_USER_UPDATED = "user_updated" WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
WEBSOCKET_EVENT_MEMBERROLE_UPDATED = "memberrole_updated"
WEBSOCKET_EVENT_USER_REMOVED = "user_removed" WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed" WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed"
WEBSOCKET_EVENT_PREFERENCES_CHANGED = "preferences_changed"
WEBSOCKET_EVENT_PREFERENCES_DELETED = "preferences_deleted"
WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message" WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message"
WEBSOCKET_EVENT_STATUS_CHANGE = "status_change" WEBSOCKET_EVENT_STATUS_CHANGE = "status_change"
WEBSOCKET_EVENT_HELLO = "hello" WEBSOCKET_EVENT_HELLO = "hello"
@ -31,13 +36,14 @@ const (
WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge" WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge"
WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added" WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added"
WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed" 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
} }
@ -52,7 +58,7 @@ 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 {
@ -110,7 +103,6 @@ type WebSocketResponse struct {
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
var br *bufio.Reader
if readBufferSize == 0 && brw != nil && brw.Reader != nil {
// Reuse the supplied bufio.Reader if the buffer has a useful size.
// This code assumes that peek on a reader returns
// bufio.Reader.buf[:0].
brw.Reader.Reset(conn)
if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 {
br = brw.Reader
}
}
if br == nil {
if readBufferSize == 0 { if readBufferSize == 0 {
readBufferSize = defaultReadBufferSize readBufferSize = defaultReadBufferSize
} }
if readBufferSize < maxControlFramePayloadSize { if readBufferSize < maxControlFramePayloadSize {
readBufferSize = maxControlFramePayloadSize readBufferSize = maxControlFramePayloadSize
} }
br = bufio.NewReaderSize(conn, readBufferSize)
}
var writeBuf []byte
if writeBufferSize == 0 && brw != nil && brw.Writer != nil {
// Use the bufio.Writer's buffer if the buffer has a useful size. This
// code assumes that bufio.Writer.buf[:1] is passed to the
// bufio.Writer's underlying writer.
var wh writeHook
brw.Writer.Reset(&wh)
brw.Writer.WriteByte(0)
brw.Flush()
if cap(wh.p) >= maxFrameHeaderSize+256 {
writeBuf = wh.p[:cap(wh.p)]
}
}
if writeBuf == nil {
if writeBufferSize == 0 { if writeBufferSize == 0 {
writeBufferSize = defaultWriteBufferSize 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,10 +109,22 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
} }
} }
if writeMessage { if writeMessage {
if !writePrepared {
err = conn.WriteMessage(mt, b) err = conn.WriteMessage(mt, b)
if err != nil { if err != nil {
log.Println("WriteMessage:", err) 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)
if err != nil { if err != nil {
@ -132,11 +144,15 @@ func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) {
} }
func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { 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,7 +12,6 @@ import (
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"text/template"
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@ -21,7 +20,6 @@ import (
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