func (c *WebConn) readPump() { defer func() { hub.Unregister(c) c.WebSocket.Close() }() c.WebSocket.SetReadLimit(MAX_SIZE) c.WebSocket.SetReadDeadline(time.Now().Add(PONG_WAIT)) c.WebSocket.SetPongHandler(func(string) error { c.WebSocket.SetReadDeadline(time.Now().Add(PONG_WAIT)) go func() { if result := <-Srv.Store.User().UpdateLastPingAt(c.UserId, model.GetMillis()); result.Err != nil { l4g.Error("Failed to updated LastPingAt for user_id=%v, err=%v", c.UserId, result.Err) } }() return nil }) for { var msg model.Message if err := c.WebSocket.ReadJSON(&msg); err != nil { return } else { msg.TeamId = c.TeamId msg.UserId = c.UserId store.PublishAndForget(&msg) } } }
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { post := model.PostFromJson(r.Body) if post == nil { c.SetInvalidParam("updatePost", "post") return } cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId) pchan := Srv.Store.Post().Get(post.Id) if !c.HasPermissionsToChannel(cchan, "updatePost") { return } var oldPost *model.Post if result := <-pchan; result.Err != nil { c.Err = result.Err return } else { oldPost = result.Data.(*model.PostList).Posts[post.Id] if oldPost == nil { c.Err = model.NewAppError("updatePost", "We couldn't find the existing post or comment to update.", "id="+post.Id) c.Err.StatusCode = http.StatusBadRequest return } if oldPost.UserId != c.Session.UserId { c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "oldUserId="+oldPost.UserId) c.Err.StatusCode = http.StatusForbidden return } if oldPost.DeleteAt != 0 { c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "Already delted id="+post.Id) c.Err.StatusCode = http.StatusForbidden return } } hashtags, _ := model.ParseHashtags(post.Message) if result := <-Srv.Store.Post().Update(oldPost, post.Message, hashtags); result.Err != nil { c.Err = result.Err return } else { rpost := result.Data.(*model.Post) message := model.NewMessage(c.Session.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED) message.Add("post_id", rpost.Id) message.Add("channel_id", rpost.ChannelId) message.Add("message", rpost.Message) store.PublishAndForget(message) w.Write([]byte(rpost.ToJson())) } }
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { channelRole := "" if team.Email == user.Email { user.Roles = model.ROLE_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN } else { user.Roles = "" } user.MakeNonNil() if len(user.Props["theme"]) == 0 { user.AddProp("theme", utils.Cfg.TeamSettings.DefaultThemeColor) } if result := <-Srv.Store.User().Save(user); result.Err != nil { c.Err = result.Err return nil } else { ruser := result.Data.(*model.User) // Do not error if user cannot be added to the town-square channel if cresult := <-Srv.Store.Channel().GetByName(team.Id, "town-square"); cresult.Err != nil { l4g.Error("Failed to get town-square err=%v", cresult.Err) } else { cm := &model.ChannelMember{ChannelId: cresult.Data.(*model.Channel).Id, UserId: ruser.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole} if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil { l4g.Error("Failed to add member town-square err=%v", cmresult.Err) } } //fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to get town-square err=%v", cresult.Err) } } else { FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl) } ruser.Sanitize(map[string]bool{}) //This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) store.PublishAndForget(message) return ruser } }
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId) message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED) message.Add("channel_id", id) store.PublishAndForget(message) result := make(map[string]string) result["id"] = id w.Write([]byte(model.MapToJson(result))) }
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { channelRole := "" if team.Email == user.Email { user.Roles = model.ROLE_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN } else { user.Roles = "" } user.MakeNonNil() if len(user.Props["theme"]) == 0 { user.AddProp("theme", utils.Cfg.TeamSettings.DefaultThemeColor) } if result := <-Srv.Store.User().Save(user); result.Err != nil { c.Err = result.Err return nil } else { ruser := result.Data.(*model.User) // Soft error if there is an issue joining the default channels if err := JoinDefaultChannels(c, ruser, channelRole); err != nil { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } //fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) } } else { FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl) } ruser.Sanitize(map[string]bool{}) // This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) store.PublishAndForget(message) return ruser } }
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) channelId := params["id"] if len(channelId) != 26 { c.SetInvalidParam("deletePost", "channelId") return } postId := params["post_id"] if len(postId) != 26 { c.SetInvalidParam("deletePost", "postId") return } cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) pchan := Srv.Store.Post().Get(postId) if !c.HasPermissionsToChannel(cchan, "deletePost") { return } if result := <-pchan; result.Err != nil { c.Err = result.Err return } else { post := result.Data.(*model.PostList).Posts[postId] if post == nil { c.SetInvalidParam("deletePost", "postId") return } if post.ChannelId != channelId { c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "") c.Err.StatusCode = http.StatusForbidden return } if post.UserId != c.Session.UserId { c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "") c.Err.StatusCode = http.StatusForbidden return } if dresult := <-Srv.Store.Post().Delete(postId, model.GetMillis()); dresult.Err != nil { c.Err = dresult.Err return } message := model.NewMessage(c.Session.TeamId, post.ChannelId, c.Session.UserId, model.ACTION_POST_DELETED) message.Add("post_id", post.Id) message.Add("channel_id", post.ChannelId) store.PublishAndForget(message) result := make(map[string]string) result["id"] = postId w.Write([]byte(model.MapToJson(result))) } }
func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { go func() { // Get a list of user names (to be used as keywords) and ids for the given team uchan := Srv.Store.User().GetProfiles(teamId) echan := Srv.Store.Channel().GetMembers(post.ChannelId) cchan := Srv.Store.Channel().Get(post.ChannelId) tchan := Srv.Store.Team().Get(teamId) var channel *model.Channel var channelName string var bodyText string var subjectText string if result := <-cchan; result.Err != nil { l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err) return } else { channel = result.Data.(*model.Channel) if channel.Type == model.CHANNEL_DIRECT { bodyText = "You have one new message." subjectText = "New Private Message" } else { bodyText = "You have one new mention." subjectText = "New Mention" channelName = channel.DisplayName } } var mentionedUsers []string if result := <-uchan; result.Err != nil { l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err) return } else { profileMap := result.Data.(map[string]*model.User) if _, ok := profileMap[post.UserId]; !ok { l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId) return } senderName := profileMap[post.UserId].Username toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] channelName = profileMap[userIds[1]].Username } else { otherUserId = userIds[0] channelName = profileMap[userIds[0]].Username } otherUser := profileMap[otherUserId] sendEmail := true if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) { toEmailMap[otherUserId] = true } } else { // Find out who is a member of the channel, only keep those profiles if eResult := <-echan; eResult.Err != nil { l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message) return } else { tempProfileMap := make(map[string]*model.User) members := eResult.Data.([]model.ChannelMember) for _, member := range members { tempProfileMap[member.UserId] = profileMap[member.UserId] } profileMap = tempProfileMap } // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { splitName := strings.Split(profile.FullName, " ") if len(splitName) > 0 && splitName[0] != "" { keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id) } } } // Add @all to keywords if user has them turned on if profile.NotifyProps["all"] == "true" { keywordMap["@all"] = append(keywordMap["@all"], profile.Id) } // Add @channel to keywords if user has them turned on if profile.NotifyProps["channel"] == "true" { keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id) } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.FieldsFunc(post.Message, splitF) for _, word := range splitMessage { // Non-case-sensitive check for regular keys userIds1, keyMatch := keywordMap[strings.ToLower(word)] // Case-sensitive check for first name userIds2, firstNameMatch := keywordMap[word] userIds := append(userIds1, userIds2...) // If one of the non-case-senstive keys or the first name matches the word // then we add en entry to the sendEmail map if keyMatch || firstNameMatch { for _, userId := range userIds { if post.UserId == userId { continue } sendEmail := true if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) { toEmailMap[userId] = true } else { toEmailMap[userId] = false } } } } for id, _ := range toEmailMap { fireAndForgetMentionUpdate(post.ChannelId, id) } } if len(toEmailMap) != 0 { mentionedUsers = make([]string, 0, len(toEmailMap)) for k := range toEmailMap { mentionedUsers = append(mentionedUsers, k) } var teamName string if result := <-tchan; result.Err != nil { l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err) return } else { teamName = result.Data.(*model.Team).Name } // Build and send the emails location, _ := time.LoadLocation("UTC") tm := time.Unix(post.CreateAt/1000, 0).In(location) subjectPage := NewServerTemplatePage("post_subject", teamUrl) subjectPage.Props["TeamName"] = teamName subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year()) for id, doSend := range toEmailMap { if !doSend { continue } // skip if inactive if profileMap[id].DeleteAt > 0 { continue } firstName := strings.Split(profileMap[id].FullName, " ")[0] bodyPage := NewServerTemplatePage("post_body", teamUrl) bodyPage.Props["FullName"] = firstName bodyPage.Props["TeamName"] = teamName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText bodyPage.Props["SenderName"] = senderName bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour()) bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute()) bodyPage.Props["Month"] = tm.Month().String()[:3] bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) bodyPage.Props["TeamLink"] = teamUrl + "/channels/" + channel.Name if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) } else { sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) for _, session := range sessions { if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" { alreadySeen[session.DeviceId] = session.DeviceId utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1) } } } } } } } message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } store.PublishAndForget(message) }() }
func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] data := model.MapFromJson(r.Body) userId := data["user_id"] if len(userId) != 26 { c.SetInvalidParam("addChannelMember", "user_id") return } cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId) sc := Srv.Store.Channel().Get(id) ouc := Srv.Store.User().Get(c.Session.UserId) nuc := Srv.Store.User().Get(userId) // Only need to be a member of the channel to add a new member if !c.HasPermissionsToChannel(cchan, "addChannelMember") { return } if nresult := <-nuc; nresult.Err != nil { c.Err = model.NewAppError("addChannelMember", "Failed to find user to be added", "") return } else if cresult := <-sc; cresult.Err != nil { c.Err = model.NewAppError("addChannelMember", "Failed to find channel", "") return } else { channel := cresult.Data.(*model.Channel) nUser := nresult.Data.(*model.User) if channel.DeleteAt > 0 { c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "") c.Err.StatusCode = http.StatusBadRequest return } if oresult := <-ouc; oresult.Err != nil { c.Err = model.NewAppError("addChannelMember", "Failed to find user doing the adding", "") return } else { oUser := oresult.Data.(*model.User) cm := &model.ChannelMember{ChannelId: channel.Id, UserId: userId, NotifyLevel: model.CHANNEL_NOTIFY_ALL} 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) c.Err = model.NewAppError("addChannelMember", "Failed to add user to channel", "") return } post := &model.Post{ChannelId: id, Message: fmt.Sprintf( `%v added to the channel by %v`, nUser.Username, oUser.Username), Type: model.POST_JOIN_LEAVE} if _, err := CreatePost(c, post, false); err != nil { l4g.Error("Failed to post add message %v", err) c.Err = model.NewAppError("addChannelMember", "Failed to add member to channel", "") return } c.LogAudit("name=" + channel.Name + " user_id=" + userId) message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_ADDED) store.PublishAndForget(message) <-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id) w.Write([]byte(cm.ToJson())) } } }