summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
author=Corey Hulen <corey@hulen.com>2015-10-26 22:11:42 -0700
committer=Corey Hulen <corey@hulen.com>2015-10-26 22:11:42 -0700
commitaec99ceb9d47d6354ac5a96bbc290126b55d30f5 (patch)
treeecb72b0d51f6edc7341ac5bef0a4f61583f81dde /api
parente750a8fd361ef6dfce557530a10aaf5ce5a7f37e (diff)
parent28847c6b4b864d747bbfdf5c53354dcb24e5f895 (diff)
downloadchat-aec99ceb9d47d6354ac5a96bbc290126b55d30f5.tar.gz
chat-aec99ceb9d47d6354ac5a96bbc290126b55d30f5.tar.bz2
chat-aec99ceb9d47d6354ac5a96bbc290126b55d30f5.zip
Merge branch 'master' into PLT-25
Diffstat (limited to 'api')
-rw-r--r--api/command.go77
-rw-r--r--api/file.go22
-rw-r--r--api/post.go42
-rw-r--r--api/post_test.go40
-rw-r--r--api/user.go13
5 files changed, 134 insertions, 60 deletions
diff --git a/api/command.go b/api/command.go
index 54f863c48..b2a4f4a0b 100644
--- a/api/command.go
+++ b/api/command.go
@@ -17,14 +17,23 @@ import (
type commandHandler func(c *Context, command *model.Command) bool
-var commands = []commandHandler{
- logoutCommand,
- joinCommand,
- loadTestCommand,
- echoCommand,
- shrugCommand,
-}
-
+var (
+ cmds = map[string]string{
+ "logoutCommand": "/logout",
+ "joinCommand": "/join",
+ "loadTestCommand": "/loadtest",
+ "echoCommand": "/echo",
+ "shrugCommand": "/shrug",
+ }
+ commands = []commandHandler{
+ logoutCommand,
+ joinCommand,
+ loadTestCommand,
+ echoCommand,
+ shrugCommand,
+ }
+ commandNotImplementedErr = model.NewAppError("checkCommand", "Command not implemented", "")
+)
var echoSem chan bool
func InitCommand(r *mux.Router) {
@@ -45,7 +54,14 @@ func command(c *Context, w http.ResponseWriter, r *http.Request) {
checkCommand(c, command)
if c.Err != nil {
- return
+ if c.Err != commandNotImplementedErr {
+ return
+ } else {
+ c.Err = nil
+ command.Response = model.RESP_NOT_IMPLEMENTED
+ w.Write([]byte(command.ToJson()))
+ return
+ }
} else {
w.Write([]byte(command.ToJson()))
}
@@ -66,6 +82,23 @@ func checkCommand(c *Context, command *model.Command) bool {
}
}
+ if !command.Suggest {
+ implemented := false
+ for _, cmd := range cmds {
+ bounds := len(cmd)
+ if len(command.Command) < bounds {
+ continue
+ }
+ if command.Command[:bounds] == cmd {
+ implemented = true
+ }
+ }
+ if !implemented {
+ c.Err = commandNotImplementedErr
+ return false
+ }
+ }
+
for _, v := range commands {
if v(c, command) || c.Err != nil {
@@ -78,7 +111,7 @@ func checkCommand(c *Context, command *model.Command) bool {
func logoutCommand(c *Context, command *model.Command) bool {
- cmd := "/logout"
+ cmd := cmds["logoutCommand"]
if strings.Index(command.Command, cmd) == 0 {
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Logout"})
@@ -97,7 +130,7 @@ func logoutCommand(c *Context, command *model.Command) bool {
}
func echoCommand(c *Context, command *model.Command) bool {
- cmd := "/echo"
+ cmd := cmds["echoCommand"]
maxThreads := 100
if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
@@ -162,10 +195,10 @@ func echoCommand(c *Context, command *model.Command) bool {
}
func shrugCommand(c *Context, command *model.Command) bool {
- cmd := "/shrug"
+ cmd := cmds["shrugCommand"]
if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
- message := "¯\\_(ツ)_/¯"
+ message := `¯\\\_(ツ)_/¯`
parameters := strings.SplitN(command.Command, " ", 2)
if len(parameters) > 1 {
@@ -192,7 +225,7 @@ func shrugCommand(c *Context, command *model.Command) bool {
func joinCommand(c *Context, command *model.Command) bool {
// looks for "/join channel-name"
- cmd := "/join"
+ cmd := cmds["joinCommand"]
if strings.Index(command.Command, cmd) == 0 {
@@ -242,7 +275,7 @@ func joinCommand(c *Context, command *model.Command) bool {
}
func loadTestCommand(c *Context, command *model.Command) bool {
- cmd := "/loadtest"
+ cmd := cmds["loadTestCommand"]
// This command is only available when EnableTesting is true
if !utils.Cfg.ServiceSettings.EnableTesting {
@@ -304,7 +337,7 @@ func contains(items []string, token string) bool {
}
func loadTestSetupCommand(c *Context, command *model.Command) bool {
- cmd := "/loadtest setup"
+ cmd := cmds["loadTestCommand"] + " setup"
if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
@@ -390,8 +423,8 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool {
}
func loadTestUsersCommand(c *Context, command *model.Command) bool {
- cmd1 := "/loadtest users"
- cmd2 := "/loadtest users fuzz"
+ cmd1 := cmds["loadTestCommand"] + " users"
+ cmd2 := cmds["loadTestCommand"] + " users fuzz"
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
cmd := cmd1
@@ -420,8 +453,8 @@ func loadTestUsersCommand(c *Context, command *model.Command) bool {
}
func loadTestChannelsCommand(c *Context, command *model.Command) bool {
- cmd1 := "/loadtest channels"
- cmd2 := "/loadtest channels fuzz"
+ cmd1 := cmds["loadTestCommand"] + " channels"
+ cmd2 := cmds["loadTestCommand"] + " channels fuzz"
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
cmd := cmd1
@@ -451,8 +484,8 @@ func loadTestChannelsCommand(c *Context, command *model.Command) bool {
}
func loadTestPostsCommand(c *Context, command *model.Command) bool {
- cmd1 := "/loadtest posts"
- cmd2 := "/loadtest posts fuzz"
+ cmd1 := cmds["loadTestCommand"] + " posts"
+ cmd2 := cmds["loadTestCommand"] + " posts fuzz"
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
cmd := cmd1
diff --git a/api/file.go b/api/file.go
index 94eea516a..f65be145d 100644
--- a/api/file.go
+++ b/api/file.go
@@ -52,6 +52,8 @@ const (
RotatedCCW = 6
RotatedCCWMirrored = 7
RotatedCW = 8
+
+ MaxImageSize = 4096 * 2160 // 4k resolution
)
var fileInfoCache *utils.Cache = utils.NewLru(1000)
@@ -125,6 +127,21 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
uid := model.NewId()
+ if model.IsFileExtImage(filepath.Ext(files[i].Filename)) {
+ imageNameList = append(imageNameList, uid+"/"+filename)
+ imageDataList = append(imageDataList, buf.Bytes())
+
+ // Decode image config first to check dimensions before loading the whole thing into memory later on
+ config, _, err := image.DecodeConfig(bytes.NewReader(buf.Bytes()))
+ if err != nil {
+ c.Err = model.NewAppError("uploadFile", "Unable to upload image file.", err.Error())
+ return
+ } else if config.Width*config.Height > MaxImageSize {
+ c.Err = model.NewAppError("uploadFile", "Unable to upload image file. File is too large.", err.Error())
+ return
+ }
+ }
+
path := "teams/" + c.Session.TeamId + "/channels/" + channelId + "/users/" + c.Session.UserId + "/" + uid + "/" + filename
if err := writeFile(buf.Bytes(), path); err != nil {
@@ -132,11 +149,6 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if model.IsFileExtImage(filepath.Ext(files[i].Filename)) {
- imageNameList = append(imageNameList, uid+"/"+filename)
- imageDataList = append(imageDataList, buf.Bytes())
- }
-
encName := utils.UrlEncode(filename)
fileUrl := "/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + encName
diff --git a/api/post.go b/api/post.go
index 79f84e04d..b8588fe6a 100644
--- a/api/post.go
+++ b/api/post.go
@@ -820,45 +820,23 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- plainSearchParams, hashtagSearchParams := model.ParseSearchParams(terms)
+ paramsList := model.ParseSearchParams(terms)
+ channels := []store.StoreChannel{}
- var hchan store.StoreChannel
- if hashtagSearchParams != nil {
- hchan = Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, hashtagSearchParams)
+ for _, params := range paramsList {
+ channels = append(channels, Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, params))
}
- var pchan store.StoreChannel
- if plainSearchParams != nil {
- pchan = Srv.Store.Post().Search(c.Session.TeamId, c.Session.UserId, plainSearchParams)
- }
-
- mainList := &model.PostList{}
- if hchan != nil {
- if result := <-hchan; result.Err != nil {
+ posts := &model.PostList{}
+ for _, channel := range channels {
+ if result := <-channel; result.Err != nil {
c.Err = result.Err
return
} else {
- mainList = result.Data.(*model.PostList)
+ data := result.Data.(*model.PostList)
+ posts.Extend(data)
}
}
- plainList := &model.PostList{}
- if pchan != nil {
- if result := <-pchan; result.Err != nil {
- c.Err = result.Err
- return
- } else {
- plainList = result.Data.(*model.PostList)
- }
- }
-
- for _, postId := range plainList.Order {
- if _, ok := mainList.Posts[postId]; !ok {
- mainList.AddPost(plainList.Posts[postId])
- mainList.AddOrder(postId)
- }
-
- }
-
- w.Write([]byte(mainList.ToJson()))
+ w.Write([]byte(posts.ToJson()))
}
diff --git a/api/post_test.go b/api/post_test.go
index ac9d5668b..e54e9ef0c 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -427,12 +427,18 @@ func TestSearchPostsInChannel(t *testing.T) {
channel2 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
+ channel3 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel)
+
post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"}
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
post3 := &model.Post{ChannelId: channel2.Id, Message: "other message with no return"}
post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
+ post4 := &model.Post{ChannelId: channel3.Id, Message: "other message with no return"}
+ post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post)
+
if result := Client.Must(Client.SearchPosts("channel:")).Data.(*model.PostList); len(result.Order) != 0 {
t.Fatalf("wrong number of posts returned %v", len(result.Order))
}
@@ -476,6 +482,10 @@ func TestSearchPostsInChannel(t *testing.T) {
if result := Client.Must(Client.SearchPosts("sgtitlereview channel: " + channel2.Name)).Data.(*model.PostList); len(result.Order) != 1 {
t.Fatalf("wrong number of posts returned %v", len(result.Order))
}
+
+ if result := Client.Must(Client.SearchPosts("channel: " + channel2.Name + " channel: " + channel3.Name)).Data.(*model.PostList); len(result.Order) != 3 {
+ t.Fatalf("wrong number of posts returned :) %v :) %v", result.Posts, result.Order)
+ }
}
func TestSearchPostsFromUser(t *testing.T) {
@@ -510,11 +520,12 @@ func TestSearchPostsFromUser(t *testing.T) {
post2 := &model.Post{ChannelId: channel2.Id, Message: "sgtitlereview\n with return"}
post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
+ // includes "X has joined the channel" messages for both user2 and user3
+
if result := Client.Must(Client.SearchPosts("from: " + user1.Username)).Data.(*model.PostList); len(result.Order) != 1 {
t.Fatalf("wrong number of posts returned %v", len(result.Order))
}
- // note that this includes the "User2 has joined the channel" system messages
if result := Client.Must(Client.SearchPosts("from: " + user2.Username)).Data.(*model.PostList); len(result.Order) != 3 {
t.Fatalf("wrong number of posts returned %v", len(result.Order))
}
@@ -526,6 +537,33 @@ func TestSearchPostsFromUser(t *testing.T) {
if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " in:" + channel1.Name)).Data.(*model.PostList); len(result.Order) != 1 {
t.Fatalf("wrong number of posts returned %v", len(result.Order))
}
+
+ user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user3.Id))
+
+ Client.LoginByEmail(team.Name, user3.Email, "pwd")
+ Client.Must(Client.JoinChannel(channel1.Id))
+ Client.Must(Client.JoinChannel(channel2.Id))
+
+ // wait for the join/leave messages to be created for user3 since they're done asynchronously
+ time.Sleep(100 * time.Millisecond)
+
+ if result := Client.Must(Client.SearchPosts("from: " + user2.Username)).Data.(*model.PostList); len(result.Order) != 3 {
+ t.Fatalf("wrong number of posts returned %v", len(result.Order))
+ }
+
+ if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username)).Data.(*model.PostList); len(result.Order) != 5 {
+ t.Fatalf("wrong number of posts returned %v", len(result.Order))
+ }
+
+ if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username + " in:" + channel2.Name)).Data.(*model.PostList); len(result.Order) != 3 {
+ t.Fatalf("wrong number of posts returned %v", len(result.Order))
+ }
+
+ if result := Client.Must(Client.SearchPosts("from: " + user2.Username + " from: " + user3.Username + " in:" + channel2.Name + " joined")).Data.(*model.PostList); len(result.Order) != 2 {
+ t.Fatalf("wrong number of posts returned %v", len(result.Order))
+ }
}
func TestGetPostsCache(t *testing.T) {
diff --git a/api/user.go b/api/user.go
index 3071e1b26..3796a50ee 100644
--- a/api/user.go
+++ b/api/user.go
@@ -814,6 +814,7 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "max-age=86400, public") // 24 hrs
}
+ w.Header().Set("Content-Type", "image/png")
w.Write(img)
}
}
@@ -854,6 +855,18 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ // Decode image config first to check dimensions before loading the whole thing into memory later on
+ config, _, err := image.DecodeConfig(file)
+ if err != nil {
+ c.Err = model.NewAppError("uploadProfileFile", "Could not decode profile image config.", err.Error())
+ return
+ } else if config.Width*config.Height > MaxImageSize {
+ c.Err = model.NewAppError("uploadProfileFile", "Unable to upload profile image. File is too large.", err.Error())
+ return
+ }
+
+ file.Seek(0, 0)
+
// Decode image into Image object
img, _, err := image.Decode(file)
if err != nil {