summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/admin.go35
-rw-r--r--api/channel.go58
-rw-r--r--api/channel_benchmark_test.go11
-rw-r--r--api/channel_test.go80
-rw-r--r--api/command.go2
-rw-r--r--api/command_test.go8
-rw-r--r--api/context.go24
-rw-r--r--api/file.go67
-rw-r--r--api/post.go2
-rw-r--r--api/team.go5
-rw-r--r--api/user.go62
11 files changed, 237 insertions, 117 deletions
diff --git a/api/admin.go b/api/admin.go
index 568d8f6e8..d9714d6d2 100644
--- a/api/admin.go
+++ b/api/admin.go
@@ -23,8 +23,10 @@ func InitAdmin(r *mux.Router) {
sr.Handle("/logs", ApiUserRequired(getLogs)).Methods("GET")
sr.Handle("/config", ApiUserRequired(getConfig)).Methods("GET")
sr.Handle("/save_config", ApiUserRequired(saveConfig)).Methods("POST")
- sr.Handle("/client_props", ApiAppHandler(getClientProperties)).Methods("GET")
sr.Handle("/test_email", ApiUserRequired(testEmail)).Methods("POST")
+ sr.Handle("/client_props", ApiAppHandler(getClientProperties)).Methods("GET")
+ sr.Handle("/log_client", ApiAppHandler(logClient)).Methods("POST")
+
}
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -59,6 +61,26 @@ func getClientProperties(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.MapToJson(utils.ClientProperties)))
}
+func logClient(c *Context, w http.ResponseWriter, r *http.Request) {
+ m := model.MapFromJson(r.Body)
+
+ lvl := m["level"]
+ msg := m["message"]
+
+ if len(msg) > 400 {
+ msg = msg[0:399]
+ }
+
+ if lvl == "ERROR" {
+ err := model.NewAppError("client", msg, "")
+ c.LogError(err)
+ }
+
+ rm := make(map[string]string)
+ rm["SUCCESS"] = "true"
+ w.Write([]byte(model.MapToJson(rm)))
+}
+
func getConfig(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.HasSystemAdminPermissions("getConfig") {
return
@@ -82,18 +104,11 @@ func saveConfig(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if len(cfg.ServiceSettings.ListenAddress) == 0 {
- c.SetInvalidParam("saveConfig", "config")
- return
- }
-
- if cfg.TeamSettings.MaxUsersPerTeam == 0 {
- c.SetInvalidParam("saveConfig", "config")
+ if err := cfg.IsValid(); err != nil {
+ c.Err = err
return
}
- // TODO run some cleanup validators
-
utils.SaveConfig(utils.CfgFileName, cfg)
utils.LoadConfig(utils.CfgFileName)
json := utils.Cfg.ToJson()
diff --git a/api/channel.go b/api/channel.go
index 896e22793..5e13fa18a 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -23,7 +23,7 @@ func InitChannel(r *mux.Router) {
sr.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST")
sr.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST")
sr.Handle("/update_desc", ApiUserRequired(updateChannelDesc)).Methods("POST")
- sr.Handle("/update_notify_level", ApiUserRequired(updateNotifyLevel)).Methods("POST")
+ sr.Handle("/update_notify_props", ApiUserRequired(updateNotifyProps)).Methods("POST")
sr.Handle("/{id:[A-Za-z0-9]+}/", ApiUserRequiredActivity(getChannel, false)).Methods("GET")
sr.Handle("/{id:[A-Za-z0-9]+}/extra_info", ApiUserRequired(getChannelExtraInfo)).Methods("GET")
sr.Handle("/{id:[A-Za-z0-9]+}/join", ApiUserRequired(joinChannel)).Methods("POST")
@@ -76,7 +76,7 @@ func CreateChannel(c *Context, channel *model.Channel, addMember bool) (*model.C
if addMember {
cm := &model.ChannelMember{ChannelId: sc.Id, UserId: c.Session.UserId,
- Roles: model.CHANNEL_ROLE_ADMIN, NotifyLevel: model.CHANNEL_NOTIFY_ALL}
+ Roles: model.CHANNEL_ROLE_ADMIN, NotifyProps: model.GetDefaultChannelNotifyProps()}
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
return nil, cmresult.Err
@@ -134,8 +134,7 @@ func CreateDirectChannel(c *Context, otherUserId string) (*model.Channel, *model
if sc, err := CreateChannel(c, channel, true); err != nil {
return nil, err
} else {
- cm := &model.ChannelMember{ChannelId: sc.Id, UserId: otherUserId,
- Roles: "", NotifyLevel: model.CHANNEL_NOTIFY_ALL}
+ cm := &model.ChannelMember{ChannelId: sc.Id, UserId: otherUserId, Roles: "", NotifyProps: model.GetDefaultChannelNotifyProps()}
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
return nil, cmresult.Err
@@ -282,7 +281,7 @@ func getChannels(c *Context, w http.ResponseWriter, r *http.Request) {
// lets make sure the user is valid
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
c.Err = result.Err
- c.RemoveSessionCookie(w)
+ c.RemoveSessionCookie(w, r)
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
return
}
@@ -372,7 +371,8 @@ func JoinChannel(c *Context, channelId string, role string) {
}
if channel.Type == model.CHANNEL_OPEN {
- cm := &model.ChannelMember{ChannelId: channel.Id, UserId: c.Session.UserId, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: role}
+ cm := &model.ChannelMember{ChannelId: channel.Id, UserId: c.Session.UserId,
+ Roles: role, NotifyProps: model.GetDefaultChannelNotifyProps()}
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
c.Err = cmresult.Err
@@ -405,7 +405,9 @@ func JoinDefaultChannels(user *model.User, channelRole string) *model.AppError {
if result := <-Srv.Store.Channel().GetByName(user.TeamId, "town-square"); result.Err != nil {
err = result.Err
} else {
- cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole}
+ cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id,
+ Roles: channelRole, NotifyProps: model.GetDefaultChannelNotifyProps()}
+
if cmResult := <-Srv.Store.Channel().SaveMember(cm); cmResult.Err != nil {
err = cmResult.Err
}
@@ -414,7 +416,9 @@ func JoinDefaultChannels(user *model.User, channelRole string) *model.AppError {
if result := <-Srv.Store.Channel().GetByName(user.TeamId, "off-topic"); result.Err != nil {
err = result.Err
} else {
- cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole}
+ cm := &model.ChannelMember{ChannelId: result.Data.(*model.Channel).Id, UserId: user.Id,
+ Roles: channelRole, NotifyProps: model.GetDefaultChannelNotifyProps()}
+
if cmResult := <-Srv.Store.Channel().SaveMember(cm); cmResult.Err != nil {
err = cmResult.Err
}
@@ -694,7 +698,7 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
oUser := oresult.Data.(*model.User)
- cm := &model.ChannelMember{ChannelId: channel.Id, UserId: userId, NotifyLevel: model.CHANNEL_NOTIFY_ALL}
+ cm := &model.ChannelMember{ChannelId: channel.Id, UserId: userId, NotifyProps: model.GetDefaultChannelNotifyProps()}
if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil {
l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", userId, id, cmresult.Err)
@@ -784,23 +788,18 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
}
-func updateNotifyLevel(c *Context, w http.ResponseWriter, r *http.Request) {
+func updateNotifyProps(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.MapFromJson(r.Body)
+
userId := data["user_id"]
if len(userId) != 26 {
- c.SetInvalidParam("updateNotifyLevel", "user_id")
+ c.SetInvalidParam("updateMarkUnreadLevel", "user_id")
return
}
channelId := data["channel_id"]
if len(channelId) != 26 {
- c.SetInvalidParam("updateNotifyLevel", "channel_id")
- return
- }
-
- notifyLevel := data["notify_level"]
- if len(notifyLevel) == 0 || !model.IsChannelNotifyLevelValid(notifyLevel) {
- c.SetInvalidParam("updateNotifyLevel", "notify_level")
+ c.SetInvalidParam("updateMarkUnreadLevel", "channel_id")
return
}
@@ -814,10 +813,29 @@ func updateNotifyLevel(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if result := <-Srv.Store.Channel().UpdateNotifyLevel(channelId, userId, notifyLevel); result.Err != nil {
+ result := <-Srv.Store.Channel().GetMember(channelId, userId)
+ if result.Err != nil {
+ c.Err = result.Err
+ return
+ }
+
+ member := result.Data.(model.ChannelMember)
+
+ // update whichever notify properties have been provided, but don't change the others
+ if markUnread, exists := data["mark_unread"]; exists {
+ member.NotifyProps["mark_unread"] = markUnread
+ }
+
+ if desktop, exists := data["desktop"]; exists {
+ member.NotifyProps["desktop"] = desktop
+ }
+
+ if result := <-Srv.Store.Channel().UpdateMember(&member); result.Err != nil {
c.Err = result.Err
return
+ } else {
+ // return the updated notify properties including any unchanged ones
+ w.Write([]byte(model.MapToJson(member.NotifyProps)))
}
- w.Write([]byte(model.MapToJson(data)))
}
diff --git a/api/channel_benchmark_test.go b/api/channel_benchmark_test.go
index 77e679c14..7820f4a03 100644
--- a/api/channel_benchmark_test.go
+++ b/api/channel_benchmark_test.go
@@ -255,7 +255,7 @@ func BenchmarkRemoveChannelMember(b *testing.B) {
}
}
-func BenchmarkUpdateNotifyLevel(b *testing.B) {
+func BenchmarkUpdateNotifyProps(b *testing.B) {
var (
NUM_CHANNELS_RANGE = utils.Range{NUM_CHANNELS, NUM_CHANNELS}
)
@@ -271,9 +271,10 @@ func BenchmarkUpdateNotifyLevel(b *testing.B) {
for i := range data {
newmap := map[string]string{
- "channel_id": channels[i].Id,
- "user_id": user.Id,
- "notify_level": model.CHANNEL_NOTIFY_MENTION,
+ "channel_id": channels[i].Id,
+ "user_id": user.Id,
+ "desktop": model.CHANNEL_NOTIFY_MENTION,
+ "mark_unread": model.CHANNEL_MARK_UNREAD_MENTION,
}
data[i] = newmap
}
@@ -282,7 +283,7 @@ func BenchmarkUpdateNotifyLevel(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := range channels {
- Client.Must(Client.UpdateNotifyLevel(data[j]))
+ Client.Must(Client.UpdateNotifyProps(data[j]))
}
}
}
diff --git a/api/channel_test.go b/api/channel_test.go
index 7845ac499..e6c7ed80e 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -803,7 +803,7 @@ func TestRemoveChannelMember(t *testing.T) {
}
-func TestUpdateNotifyLevel(t *testing.T) {
+func TestUpdateNotifyProps(t *testing.T) {
Setup()
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
@@ -821,55 +821,94 @@ func TestUpdateNotifyLevel(t *testing.T) {
data := make(map[string]string)
data["channel_id"] = channel1.Id
data["user_id"] = user.Id
- data["notify_level"] = model.CHANNEL_NOTIFY_MENTION
+ data["desktop"] = model.CHANNEL_NOTIFY_MENTION
timeBeforeUpdate := model.GetMillis()
time.Sleep(100 * time.Millisecond)
- if _, err := Client.UpdateNotifyLevel(data); err != nil {
+ // test updating desktop
+ if result, err := Client.UpdateNotifyProps(data); err != nil {
t.Fatal(err)
+ } else if notifyProps := result.Data.(map[string]string); notifyProps["desktop"] != model.CHANNEL_NOTIFY_MENTION {
+ t.Fatal("NotifyProps[\"desktop\"] did not update properly")
+ } else if notifyProps["mark_unread"] != model.CHANNEL_MARK_UNREAD_ALL {
+ t.Fatalf("NotifyProps[\"mark_unread\"] changed to %v", notifyProps["mark_unread"])
}
rget := Client.Must(Client.GetChannels(""))
rdata := rget.Data.(*model.ChannelList)
- if len(rdata.Members) == 0 || rdata.Members[channel1.Id].NotifyLevel != data["notify_level"] {
- t.Fatal("NotifyLevel did not update properly")
+ if len(rdata.Members) == 0 || rdata.Members[channel1.Id].NotifyProps["desktop"] != data["desktop"] {
+ t.Fatal("NotifyProps[\"desktop\"] did not update properly")
+ } else if rdata.Members[channel1.Id].LastUpdateAt <= timeBeforeUpdate {
+ t.Fatal("LastUpdateAt did not update")
}
- if rdata.Members[channel1.Id].LastUpdateAt <= timeBeforeUpdate {
- t.Fatal("LastUpdateAt did not update")
+ // test an empty update
+ delete(data, "desktop")
+
+ if result, err := Client.UpdateNotifyProps(data); err != nil {
+ t.Fatal(err)
+ } else if notifyProps := result.Data.(map[string]string); notifyProps["mark_unread"] != model.CHANNEL_MARK_UNREAD_ALL {
+ t.Fatalf("NotifyProps[\"mark_unread\"] changed to %v", notifyProps["mark_unread"])
+ } else if notifyProps["desktop"] != model.CHANNEL_NOTIFY_MENTION {
+ t.Fatalf("NotifyProps[\"desktop\"] changed to %v", notifyProps["desktop"])
}
+ // test updating mark unread
+ data["mark_unread"] = model.CHANNEL_MARK_UNREAD_MENTION
+
+ if result, err := Client.UpdateNotifyProps(data); err != nil {
+ t.Fatal(err)
+ } else if notifyProps := result.Data.(map[string]string); notifyProps["mark_unread"] != model.CHANNEL_MARK_UNREAD_MENTION {
+ t.Fatal("NotifyProps[\"mark_unread\"] did not update properly")
+ } else if notifyProps["desktop"] != model.CHANNEL_NOTIFY_MENTION {
+ t.Fatalf("NotifyProps[\"desktop\"] changed to %v", notifyProps["desktop"])
+ }
+
+ // test updating both
+ data["desktop"] = model.CHANNEL_NOTIFY_NONE
+ data["mark_unread"] = model.CHANNEL_MARK_UNREAD_MENTION
+
+ if result, err := Client.UpdateNotifyProps(data); err != nil {
+ t.Fatal(err)
+ } else if notifyProps := result.Data.(map[string]string); notifyProps["desktop"] != model.CHANNEL_NOTIFY_NONE {
+ t.Fatal("NotifyProps[\"desktop\"] did not update properly")
+ } else if notifyProps["mark_unread"] != model.CHANNEL_MARK_UNREAD_MENTION {
+ t.Fatal("NotifyProps[\"mark_unread\"] did not update properly")
+ }
+
+ // test error cases
data["user_id"] = "junk"
- if _, err := Client.UpdateNotifyLevel(data); err == nil {
+ if _, err := Client.UpdateNotifyProps(data); err == nil {
t.Fatal("Should have errored - bad user id")
}
data["user_id"] = "12345678901234567890123456"
- if _, err := Client.UpdateNotifyLevel(data); err == nil {
+ if _, err := Client.UpdateNotifyProps(data); err == nil {
t.Fatal("Should have errored - bad user id")
}
data["user_id"] = user.Id
data["channel_id"] = "junk"
- if _, err := Client.UpdateNotifyLevel(data); err == nil {
+ if _, err := Client.UpdateNotifyProps(data); err == nil {
t.Fatal("Should have errored - bad channel id")
}
data["channel_id"] = "12345678901234567890123456"
- if _, err := Client.UpdateNotifyLevel(data); err == nil {
+ if _, err := Client.UpdateNotifyProps(data); err == nil {
t.Fatal("Should have errored - bad channel id")
}
- data["channel_id"] = channel1.Id
- data["notify_level"] = ""
- if _, err := Client.UpdateNotifyLevel(data); err == nil {
- t.Fatal("Should have errored - empty notify level")
+ data["desktop"] = "junk"
+ data["mark_unread"] = model.CHANNEL_MARK_UNREAD_ALL
+ if _, err := Client.UpdateNotifyProps(data); err == nil {
+ t.Fatal("Should have errored - bad desktop notify level")
}
- data["notify_level"] = "junk"
- if _, err := Client.UpdateNotifyLevel(data); err == nil {
- t.Fatal("Should have errored - bad notify level")
+ data["desktop"] = model.CHANNEL_NOTIFY_ALL
+ data["mark_unread"] = "junk"
+ if _, err := Client.UpdateNotifyProps(data); err == nil {
+ t.Fatal("Should have errored - bad mark unread level")
}
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
@@ -879,8 +918,9 @@ func TestUpdateNotifyLevel(t *testing.T) {
data["channel_id"] = channel1.Id
data["user_id"] = user2.Id
- data["notify_level"] = model.CHANNEL_NOTIFY_MENTION
- if _, err := Client.UpdateNotifyLevel(data); err == nil {
+ data["desktop"] = model.CHANNEL_NOTIFY_MENTION
+ data["mark_unread"] = model.CHANNEL_MARK_UNREAD_MENTION
+ if _, err := Client.UpdateNotifyProps(data); err == nil {
t.Fatal("Should have errored - user not in channel")
}
}
diff --git a/api/command.go b/api/command.go
index 0d2f7597b..427922344 100644
--- a/api/command.go
+++ b/api/command.go
@@ -195,7 +195,7 @@ func joinCommand(c *Context, command *model.Command) bool {
return false
}
- command.GotoLocation = "/channels/" + v.Name
+ command.GotoLocation = c.GetTeamURL() + "/channels/" + v.Name
command.Response = model.RESP_EXECUTED
return true
}
diff --git a/api/command_test.go b/api/command_test.go
index fe52dd41b..360c4da58 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -4,7 +4,9 @@
package api
import (
+ "strings"
"testing"
+ "time"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
@@ -126,12 +128,12 @@ func TestJoinCommands(t *testing.T) {
}
rs5 := Client.Must(Client.Command("", "/join "+channel2.Name, false)).Data.(*model.Command)
- if rs5.GotoLocation != "/channels/"+channel2.Name {
+ if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) {
t.Fatal("failed to join channel")
}
rs6 := Client.Must(Client.Command("", "/join "+channel3.Name, false)).Data.(*model.Command)
- if rs6.GotoLocation == "/channels/"+channel3.Name {
+ if strings.HasSuffix(rs6.GotoLocation, "/"+team.Name+"/channels/"+channel3.Name) {
t.Fatal("should not have joined direct message channel")
}
@@ -175,6 +177,8 @@ func TestEchoCommand(t *testing.T) {
t.Fatal("Echo command failed to execute")
}
+ time.Sleep(100 * time.Millisecond)
+
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
if len(p1.Order) != 1 {
t.Fatal("Echo command failed to send")
diff --git a/api/context.go b/api/context.go
index 02c3dc902..e80582b2a 100644
--- a/api/context.go
+++ b/api/context.go
@@ -137,7 +137,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
if session == nil || session.IsExpired() {
- c.RemoveSessionCookie(w)
+ c.RemoveSessionCookie(w, r)
c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "token="+token)
c.Err.StatusCode = http.StatusUnauthorized
} else if !session.IsOAuth && isTokenFromQueryString {
@@ -303,7 +303,6 @@ func (c *Context) HasSystemAdminPermissions(where string) bool {
}
func (c *Context) IsSystemAdmin() bool {
- // TODO XXX FIXME && IsPrivateIpAddress(c.IpAddress)
if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) {
return true
}
@@ -317,7 +316,7 @@ func (c *Context) IsTeamAdmin() bool {
return false
}
-func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
+func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
sessionCache.Remove(c.Session.Token)
@@ -330,6 +329,21 @@ func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
}
http.SetCookie(w, cookie)
+
+ multiToken := ""
+ if oldMultiCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil {
+ multiToken = oldMultiCookie.Value
+ }
+
+ multiCookie := &http.Cookie{
+ Name: model.MULTI_SESSION_TOKEN,
+ Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)),
+ Path: "/",
+ MaxAge: model.SESSION_TIME_WEB_IN_SECS,
+ HttpOnly: true,
+ }
+
+ http.SetCookie(w, multiCookie)
}
func (c *Context) SetInvalidParam(where string, name string) {
@@ -346,7 +360,7 @@ func (c *Context) setTeamURL(url string, valid bool) {
c.teamURLValid = valid
}
-func (c *Context) setTeamURLFromSession() {
+func (c *Context) SetTeamURLFromSession() {
if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err == nil {
c.setTeamURL(c.GetSiteURL()+"/"+result.Data.(*model.Team).Name, true)
}
@@ -362,7 +376,7 @@ func (c *Context) GetTeamURLFromTeam(team *model.Team) string {
func (c *Context) GetTeamURL() string {
if !c.teamURLValid {
- c.setTeamURLFromSession()
+ c.SetTeamURLFromSession()
if !c.teamURLValid {
l4g.Debug("TeamURL accessed when not valid. Team URL should not be used in api functions or those that are team independent")
}
diff --git a/api/file.go b/api/file.go
index 5ed422811..5dc1db650 100644
--- a/api/file.go
+++ b/api/file.go
@@ -5,16 +5,15 @@ package api
import (
"bytes"
- "code.google.com/p/graphics-go/graphics"
l4g "code.google.com/p/log4go"
"fmt"
+ "github.com/disintegration/imaging"
"github.com/goamz/goamz/aws"
"github.com/goamz/goamz/s3"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
- "github.com/nfnt/resize"
"github.com/rwcarlsen/goexif/exif"
_ "golang.org/x/image/bmp"
"image"
@@ -24,7 +23,6 @@ import (
"image/jpeg"
"io"
"io/ioutil"
- "math"
"mime"
"net/http"
"net/url"
@@ -163,7 +161,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
name := filename[:strings.LastIndex(filename, ".")]
go func() {
// Decode image bytes into Image object
- img, _, err := image.Decode(bytes.NewReader(fileData[i]))
+ img, imgType, err := image.Decode(bytes.NewReader(fileData[i]))
if err != nil {
l4g.Error("Unable to decode image channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
return
@@ -175,47 +173,30 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
// Get the image's orientation and ignore any errors since not all images will have orientation data
orientation, _ := getImageOrientation(fileData[i])
- // Create a temporary image that will be manipulated and then used to make the thumbnail and preview image
- var temp *image.RGBA
- switch orientation {
- case Upright, UprightMirrored, UpsideDown, UpsideDownMirrored:
- temp = image.NewRGBA(img.Bounds())
- case RotatedCCW, RotatedCCWMirrored, RotatedCW, RotatedCWMirrored:
- bounds := img.Bounds()
- temp = image.NewRGBA(image.Rect(bounds.Min.Y, bounds.Min.X, bounds.Max.Y, bounds.Max.X))
-
- width, height = height, width
+ if imgType == "png" {
+ dst := image.NewRGBA(img.Bounds())
+ draw.Draw(dst, dst.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
+ draw.Draw(dst, dst.Bounds(), img, img.Bounds().Min, draw.Over)
+ img = dst
}
- // Draw a white background since JPEGs lack transparency
- draw.Draw(temp, temp.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)
-
- // Copy the original image onto the temporary one while rotating it as necessary
switch orientation {
- case UpsideDown, UpsideDownMirrored:
- // rotate 180 degrees
- err := graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: math.Pi})
- if err != nil {
- l4g.Error("Unable to rotate image")
- }
- case RotatedCW, RotatedCWMirrored:
- // rotate 90 degrees CCW
- graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: 3 * math.Pi / 2})
- if err != nil {
- l4g.Error("Unable to rotate image")
- }
- case RotatedCCW, RotatedCCWMirrored:
- // rotate 90 degrees CW
- graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: math.Pi / 2})
- if err != nil {
- l4g.Error("Unable to rotate image")
- }
- case Upright, UprightMirrored:
- draw.Draw(temp, temp.Bounds(), img, img.Bounds().Min, draw.Over)
+ case UprightMirrored:
+ img = imaging.FlipH(img)
+ case UpsideDown:
+ img = imaging.Rotate180(img)
+ case UpsideDownMirrored:
+ img = imaging.FlipV(img)
+ case RotatedCWMirrored:
+ img = imaging.Transpose(img)
+ case RotatedCCW:
+ img = imaging.Rotate270(img)
+ case RotatedCCWMirrored:
+ img = imaging.Transverse(img)
+ case RotatedCW:
+ img = imaging.Rotate90(img)
}
- img = temp
-
// Create thumbnail
go func() {
thumbWidth := float64(utils.Cfg.FileSettings.ThumbnailWidth)
@@ -227,9 +208,9 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
if imgHeight < thumbHeight && imgWidth < thumbWidth {
thumbnail = img
} else if imgHeight/imgWidth < thumbHeight/thumbWidth {
- thumbnail = resize.Resize(0, utils.Cfg.FileSettings.ThumbnailHeight, img, resize.Lanczos3)
+ thumbnail = imaging.Resize(img, 0, utils.Cfg.FileSettings.ThumbnailHeight, imaging.Lanczos)
} else {
- thumbnail = resize.Resize(utils.Cfg.FileSettings.ThumbnailWidth, 0, img, resize.Lanczos3)
+ thumbnail = imaging.Resize(img, utils.Cfg.FileSettings.ThumbnailWidth, 0, imaging.Lanczos)
}
buf := new(bytes.Buffer)
@@ -249,7 +230,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
go func() {
var preview image.Image
if width > int(utils.Cfg.FileSettings.PreviewWidth) {
- preview = resize.Resize(utils.Cfg.FileSettings.PreviewWidth, utils.Cfg.FileSettings.PreviewHeight, img, resize.Lanczos3)
+ preview = imaging.Resize(img, utils.Cfg.FileSettings.PreviewWidth, utils.Cfg.FileSettings.PreviewHeight, imaging.Lanczos)
} else {
preview = img
}
diff --git a/api/post.go b/api/post.go
index 2b683fb7d..65dad0eb6 100644
--- a/api/post.go
+++ b/api/post.go
@@ -88,6 +88,8 @@ func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.P
}
}
+ post.CreateAt = 0
+
post.Hashtags, _ = model.ParseHashtags(post.Message)
post.UserId = c.Session.UserId
diff --git a/api/team.go b/api/team.go
index cff34390a..8e5d634aa 100644
--- a/api/team.go
+++ b/api/team.go
@@ -75,7 +75,10 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- m["follow_link"] = bodyPage.Props["Link"]
+ if !utils.Cfg.EmailSettings.RequireEmailVerification {
+ m["follow_link"] = bodyPage.Props["Link"]
+ }
+
w.Header().Set("Access-Control-Allow-Origin", " *")
w.Write([]byte(model.MapToJson(m)))
}
diff --git a/api/user.go b/api/user.go
index a5c3fca2b..2d7dd9ab1 100644
--- a/api/user.go
+++ b/api/user.go
@@ -8,13 +8,13 @@ import (
l4g "code.google.com/p/log4go"
b64 "encoding/base64"
"fmt"
+ "github.com/disintegration/imaging"
"github.com/golang/freetype"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"github.com/mssola/user_agent"
- "github.com/nfnt/resize"
"hash/fnv"
"image"
"image/color"
@@ -394,6 +394,41 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
http.SetCookie(w, sessionCookie)
+ multiToken := ""
+ if originalMultiSessionCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil {
+ multiToken = originalMultiSessionCookie.Value
+ }
+
+ // Attempt to clean all the old tokens or duplicate tokens
+ if len(multiToken) > 0 {
+ tokens := strings.Split(multiToken, " ")
+
+ multiToken = ""
+ seen := make(map[string]string)
+ seen[session.TeamId] = session.TeamId
+ for _, token := range tokens {
+ if sr := <-Srv.Store.Session().Get(token); sr.Err == nil {
+ s := sr.Data.(*model.Session)
+ if !s.IsExpired() && seen[s.TeamId] == "" {
+ multiToken += " " + token
+ seen[s.TeamId] = s.TeamId
+ }
+ }
+ }
+ }
+
+ multiToken = strings.TrimSpace(session.Token + " " + multiToken)
+
+ multiSessionCookie := &http.Cookie{
+ Name: model.MULTI_SESSION_TOKEN,
+ Value: multiToken,
+ Path: "/",
+ MaxAge: maxAge,
+ HttpOnly: true,
+ }
+
+ http.SetCookie(w, multiSessionCookie)
+
c.Session = *session
c.LogAuditWithUserId(user.Id, "success")
}
@@ -467,10 +502,14 @@ func RevokeAllSession(c *Context, userId string) {
for _, session := range sessions {
c.LogAuditWithUserId(userId, "session_id="+session.Id)
- sessionCache.Remove(session.Token)
- if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
- c.Err = result.Err
- return
+ if session.IsOAuth {
+ RevokeAccessToken(session.Token)
+ } else {
+ sessionCache.Remove(session.Token)
+ if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil {
+ c.Err = result.Err
+ return
+ }
}
}
}
@@ -510,7 +549,7 @@ func logout(c *Context, w http.ResponseWriter, r *http.Request) {
func Logout(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("")
- c.RemoveSessionCookie(w)
+ c.RemoveSessionCookie(w, r)
if result := <-Srv.Store.Session().Remove(c.Session.Id); result.Err != nil {
c.Err = result.Err
return
@@ -525,7 +564,7 @@ func getMe(c *Context, w http.ResponseWriter, r *http.Request) {
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
c.Err = result.Err
- c.RemoveSessionCookie(w)
+ c.RemoveSessionCookie(w, r)
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
return
} else if HandleEtag(result.Data.(*model.User).Etag(), w, r) {
@@ -799,7 +838,7 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
}
// Scale profile image
- img = resize.Resize(utils.Cfg.FileSettings.ProfileWidth, utils.Cfg.FileSettings.ProfileHeight, img, resize.Lanczos3)
+ img = imaging.Resize(img, utils.Cfg.FileSettings.ProfileWidth, utils.Cfg.FileSettings.ProfileHeight, imaging.Lanczos)
buf := new(bytes.Buffer)
err = png.Encode(buf, img)
@@ -994,7 +1033,7 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
sessions := result.Data.([]*model.Session)
for _, s := range sessions {
- sessionCache.Remove(s.Id)
+ sessionCache.Remove(s.Token)
}
}
@@ -1417,7 +1456,7 @@ func GetAuthorizationCode(c *Context, w http.ResponseWriter, r *http.Request, te
func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, *model.Team, *model.AppError) {
sso := utils.Cfg.GetSSOService(service)
- if sso != nil && !sso.Enable {
+ if sso == nil || !sso.Enable {
return nil, nil, model.NewAppError("AuthorizeOAuthUser", "Unsupported OAuth service provider", "service="+service)
}
@@ -1459,6 +1498,9 @@ func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser
return nil, nil, model.NewAppError("AuthorizeOAuthUser", "Token request failed", err.Error())
} else {
ar = model.AccessResponseFromJson(resp.Body)
+ if ar == nil {
+ return nil, nil, model.NewAppError("AuthorizeOAuthUser", "Bad response from token request", "")
+ }
}
if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {