Beispiel #1
0
func sendReactionEvent(event string, channelId string, reaction *model.Reaction, postHadReactions bool) {
	// send out that a reaction has been added/removed
	go func() {
		message := model.NewWebSocketEvent(event, "", channelId, "", nil)
		message.Add("reaction", reaction.ToJson())

		app.Publish(message)
	}()

	// send out that a post was updated if post.HasReactions has changed
	go func() {
		var post *model.Post
		if result := <-app.Srv.Store.Post().Get(reaction.PostId); result.Err != nil {
			l4g.Warn(utils.T("api.reaction.send_reaction_event.post.app_error"))
			return
		} else {
			post = result.Data.(*model.PostList).Posts[reaction.PostId]
		}

		if post.HasReactions != postHadReactions {
			message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", channelId, "", nil)
			message.Add("post", post.ToJson())

			app.Publish(message)
		}
	}()
}
Beispiel #2
0
func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError {
	if channel.DeleteAt > 0 {
		return model.NewLocAppError("RemoveUserFromChannel", "api.channel.remove_user_from_channel.deleted.app_error", nil, "")
	}

	if channel.Name == model.DEFAULT_CHANNEL {
		return model.NewLocAppError("RemoveUserFromChannel", "api.channel.remove.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
	}

	if cmresult := <-app.Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil {
		return cmresult.Err
	}

	app.InvalidateCacheForUser(userIdToRemove)
	app.InvalidateCacheForChannel(channel.Id)

	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil)
	message.Add("user_id", userIdToRemove)
	message.Add("remover_id", removerUserId)
	go app.Publish(message)

	// because the removed user no longer belongs to the channel we need to send a separate websocket event
	userMsg := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", "", userIdToRemove, nil)
	userMsg.Add("channel_id", channel.Id)
	userMsg.Add("remover_id", removerUserId)
	go app.Publish(userMsg)

	return nil
}
func setCollapsePreference(c *Context, isCollapse bool) *model.CommandResponse {
	pref := model.Preference{
		UserId:   c.Session.UserId,
		Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
		Name:     model.PREFERENCE_NAME_COLLAPSE_SETTING,
		Value:    strconv.FormatBool(isCollapse),
	}

	if result := <-app.Srv.Store.Preference().Save(&model.Preferences{pref}); result.Err != nil {
		return &model.CommandResponse{Text: c.T("api.command_expand_collapse.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
	}

	socketMessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCE_CHANGED, "", "", c.Session.UserId, nil)
	socketMessage.Add("preference", pref.ToJson())
	go app.Publish(socketMessage)

	var rmsg string

	if isCollapse {
		rmsg = c.T("api.command_collapse.success")
	} else {
		rmsg = c.T("api.command_expand.success")
	}
	return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg}
}
Beispiel #4
0
func setLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["channel_id"]

	data := model.StringInterfaceFromJson(r.Body)
	newLastViewedAt := int64(data["last_viewed_at"].(float64))

	app.Srv.Store.Channel().SetLastViewedAt(id, c.Session.UserId, newLastViewedAt)

	chanPref := model.Preference{
		UserId:   c.Session.UserId,
		Category: c.TeamId,
		Name:     model.PREFERENCE_NAME_LAST_CHANNEL,
		Value:    id,
	}

	teamPref := model.Preference{
		UserId:   c.Session.UserId,
		Category: model.PREFERENCE_CATEGORY_LAST,
		Name:     model.PREFERENCE_NAME_LAST_TEAM,
		Value:    c.TeamId,
	}

	app.Srv.Store.Preference().Save(&model.Preferences{teamPref, chanPref})

	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil)
	message.Add("channel_id", id)

	go app.Publish(message)

	result := make(map[string]string)
	result["id"] = id
	w.Write([]byte(model.MapToJson(result)))
}
Beispiel #5
0
func LeaveTeam(team *model.Team, user *model.User) *model.AppError {

	var teamMember model.TeamMember

	if result := <-app.Srv.Store.Team().GetMember(team.Id, user.Id); result.Err != nil {
		return model.NewLocAppError("RemoveUserFromTeam", "api.team.remove_user_from_team.missing.app_error", nil, result.Err.Error())
	} else {
		teamMember = result.Data.(model.TeamMember)
	}

	var channelList *model.ChannelList

	if result := <-app.Srv.Store.Channel().GetChannels(team.Id, user.Id); result.Err != nil {
		if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" {
			channelList = &model.ChannelList{}
		} else {
			return result.Err
		}

	} else {
		channelList = result.Data.(*model.ChannelList)
	}

	for _, channel := range *channelList {
		if channel.Type != model.CHANNEL_DIRECT {
			app.InvalidateCacheForChannel(channel.Id)
			if result := <-app.Srv.Store.Channel().RemoveMember(channel.Id, user.Id); result.Err != nil {
				return result.Err
			}
		}
	}

	// Send the websocket message before we actually do the remove so the user being removed gets it.
	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LEAVE_TEAM, team.Id, "", "", nil)
	message.Add("user_id", user.Id)
	message.Add("team_id", team.Id)
	app.Publish(message)

	teamMember.Roles = ""
	teamMember.DeleteAt = model.GetMillis()

	if result := <-app.Srv.Store.Team().UpdateMember(&teamMember); result.Err != nil {
		return result.Err
	}

	if uua := <-app.Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil {
		return uua.Err
	}

	// delete the preferences that set the last channel used in the team and other team specific preferences
	if result := <-app.Srv.Store.Preference().DeleteCategory(user.Id, team.Id); result.Err != nil {
		return result.Err
	}

	app.RemoveAllSessionsForUserId(user.Id)
	app.InvalidateCacheForUser(user.Id)

	return nil
}
Beispiel #6
0
func webrtcMessage(req *model.WebSocketRequest) (map[string]interface{}, *model.AppError) {
	var ok bool
	var toUserId string
	if toUserId, ok = req.Data["to_user_id"].(string); !ok || len(toUserId) != 26 {
		return nil, NewInvalidWebSocketParamError(req.Action, "to_user_id")
	}

	event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_WEBRTC, "", "", toUserId, nil)
	event.Data = req.Data
	go app.Publish(event)

	return nil, nil
}
Beispiel #7
0
func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) {
	view := model.ChannelViewFromJson(r.Body)

	if err := SetActiveChannel(c.Session.UserId, view.ChannelId); err != nil {
		c.Err = err
		return
	}

	if len(view.ChannelId) == 0 {
		ReturnStatusOK(w)
		return
	}

	channelIds := []string{view.ChannelId}

	var pchan store.StoreChannel
	if len(view.PrevChannelId) > 0 {
		channelIds = append(channelIds, view.PrevChannelId)

		if *utils.Cfg.EmailSettings.SendPushNotifications && !c.Session.IsMobileApp() {
			pchan = app.Srv.Store.User().GetUnreadCountForChannel(c.Session.UserId, view.ChannelId)
		}
	}

	uchan := app.Srv.Store.Channel().UpdateLastViewedAt(channelIds, c.Session.UserId)

	if pchan != nil {
		if result := <-pchan; result.Err != nil {
			c.Err = result.Err
			return
		} else {
			if result.Data.(int64) > 0 {
				app.ClearPushNotification(c.Session.UserId, view.ChannelId)
			}
		}
	}

	if result := <-uchan; result.Err != nil {
		c.Err = result.Err
		return
	}

	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil)
	message.Add("channel_id", view.ChannelId)
	go app.Publish(message)

	ReturnStatusOK(w)
}
Beispiel #8
0
func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {

	team := model.TeamFromJson(r.Body)

	if team == nil {
		c.SetInvalidParam("updateTeam", "team")
		return
	}

	team.Id = c.TeamId

	if !HasPermissionToTeamContext(c, team.Id, model.PERMISSION_MANAGE_TEAM) {
		c.Err = model.NewLocAppError("updateTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId)
		c.Err.StatusCode = http.StatusForbidden
		return
	}

	var oldTeam *model.Team
	if result := <-app.Srv.Store.Team().Get(team.Id); result.Err != nil {
		c.Err = result.Err
		return
	} else {
		oldTeam = result.Data.(*model.Team)
	}

	oldTeam.DisplayName = team.DisplayName
	oldTeam.Description = team.Description
	oldTeam.InviteId = team.InviteId
	oldTeam.AllowOpenInvite = team.AllowOpenInvite
	oldTeam.CompanyName = team.CompanyName
	oldTeam.AllowedDomains = team.AllowedDomains
	//oldTeam.Type = team.Type

	if result := <-app.Srv.Store.Team().Update(oldTeam); result.Err != nil {
		c.Err = result.Err
		return
	}

	oldTeam.Sanitize()

	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_UPDATE_TEAM, "", "", "", nil)
	message.Add("team", oldTeam.ToJson())
	go app.Publish(message)

	w.Write([]byte(oldTeam.ToJson()))
}
Beispiel #9
0
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
	post := model.PostFromJson(r.Body)

	if post == nil {
		c.SetInvalidParam("updatePost", "post")
		return
	}

	pchan := app.Srv.Store.Post().Get(post.Id)

	if !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_EDIT_POST) {
		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.NewLocAppError("updatePost", "api.post.update_post.find.app_error", nil, "id="+post.Id)
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		if oldPost.UserId != c.Session.UserId {
			c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil, "oldUserId="+oldPost.UserId)
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if oldPost.DeleteAt != 0 {
			c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil,
				c.T("api.post.update_post.permissions_details.app_error", map[string]interface{}{"PostId": post.Id}))
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if oldPost.IsSystemMessage() {
			c.Err = model.NewLocAppError("updatePost", "api.post.update_post.system_message.app_error", nil, "id="+post.Id)
			c.Err.StatusCode = http.StatusForbidden
			return
		}
	}

	newPost := &model.Post{}
	*newPost = *oldPost

	newPost.Message = post.Message
	newPost.EditAt = model.GetMillis()
	newPost.Hashtags, _ = model.ParseHashtags(post.Message)

	if result := <-app.Srv.Store.Post().Update(newPost, oldPost); result.Err != nil {
		c.Err = result.Err
		return
	} else {
		rpost := result.Data.(*model.Post)

		message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", rpost.ChannelId, "", nil)
		message.Add("post", rpost.ToJson())

		go app.Publish(message)

		app.InvalidateCacheForChannelPosts(rpost.ChannelId)

		w.Write([]byte(rpost.ToJson()))
	}
}
Beispiel #10
0
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)

	channelId := params["channel_id"]
	if len(channelId) != 26 {
		c.SetInvalidParam("deletePost", "channelId")
		return
	}

	postId := params["post_id"]
	if len(postId) != 26 {
		c.SetInvalidParam("deletePost", "postId")
		return
	}

	if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_EDIT_POST) {
		return
	}

	pchan := app.Srv.Store.Post().Get(postId)

	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.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "")
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if post.UserId != c.Session.UserId && !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_EDIT_OTHERS_POSTS) {
			c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "")
			c.Err.StatusCode = http.StatusForbidden
			return
		}

		if dresult := <-app.Srv.Store.Post().Delete(postId, model.GetMillis()); dresult.Err != nil {
			c.Err = dresult.Err
			return
		}

		message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, "", nil)
		message.Add("post", post.ToJson())

		go app.Publish(message)
		go DeletePostFiles(post)
		go DeleteFlaggedPost(c.Session.UserId, post)

		app.InvalidateCacheForChannelPosts(post.ChannelId)

		result := make(map[string]string)
		result["id"] = postId
		w.Write([]byte(model.MapToJson(result)))
	}
}
Beispiel #11
0
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["channel_id"]

	data := model.StringInterfaceFromJson(r.Body)

	var active bool
	var ok bool
	if active, ok = data["active"].(bool); !ok {
		active = true
	}

	doClearPush := false
	if *utils.Cfg.EmailSettings.SendPushNotifications && !c.Session.IsMobileApp() && active {
		if result := <-app.Srv.Store.User().GetUnreadCountForChannel(c.Session.UserId, id); result.Err != nil {
			l4g.Error(utils.T("api.channel.update_last_viewed_at.get_unread_count_for_channel.error"), c.Session.UserId, id, result.Err.Error())
		} else {
			if result.Data.(int64) > 0 {
				doClearPush = true
			}
		}
	}

	go func() {
		if err := SetActiveChannel(c.Session.UserId, id); err != nil {
			l4g.Error(err.Error())
		}
	}()

	app.Srv.Store.Channel().UpdateLastViewedAt([]string{id}, c.Session.UserId)

	// Must be after update so that unread count is correct
	if doClearPush {
		go app.ClearPushNotification(c.Session.UserId, id)
	}

	chanPref := model.Preference{
		UserId:   c.Session.UserId,
		Category: c.TeamId,
		Name:     model.PREFERENCE_NAME_LAST_CHANNEL,
		Value:    id,
	}

	teamPref := model.Preference{
		UserId:   c.Session.UserId,
		Category: model.PREFERENCE_CATEGORY_LAST,
		Name:     model.PREFERENCE_NAME_LAST_TEAM,
		Value:    c.TeamId,
	}

	app.Srv.Store.Preference().Save(&model.Preferences{teamPref, chanPref})

	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil)
	message.Add("channel_id", id)

	go app.Publish(message)

	result := make(map[string]string)
	result["id"] = id
	w.Write([]byte(model.MapToJson(result)))
}
Beispiel #12
0
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {

	params := mux.Vars(r)
	id := params["channel_id"]

	sc := app.Srv.Store.Channel().Get(id, true)
	scm := app.Srv.Store.Channel().GetMember(id, c.Session.UserId)
	cmc := app.Srv.Store.Channel().GetMemberCount(id, false)
	uc := app.Srv.Store.User().Get(c.Session.UserId)
	ihc := app.Srv.Store.Webhook().GetIncomingByChannel(id)
	ohc := app.Srv.Store.Webhook().GetOutgoingByChannel(id)

	if cresult := <-sc; cresult.Err != nil {
		c.Err = cresult.Err
		return
	} else if uresult := <-uc; uresult.Err != nil {
		c.Err = cresult.Err
		return
	} else if scmresult := <-scm; scmresult.Err != nil {
		c.Err = scmresult.Err
		return
	} else if cmcresult := <-cmc; cmcresult.Err != nil {
		c.Err = cmcresult.Err
		return
	} else if ihcresult := <-ihc; ihcresult.Err != nil {
		c.Err = ihcresult.Err
		return
	} else if ohcresult := <-ohc; ohcresult.Err != nil {
		c.Err = ohcresult.Err
		return
	} else {
		channel := cresult.Data.(*model.Channel)
		memberCount := cmcresult.Data.(int64)
		user := uresult.Data.(*model.User)
		incomingHooks := ihcresult.Data.([]*model.IncomingWebhook)
		outgoingHooks := ohcresult.Data.([]*model.OutgoingWebhook)
		// Don't need to do anything with channel member, just wanted to confirm it exists

		// Allow delete if user is the only member left in channel
		if memberCount > 1 {
			if channel.Type == model.CHANNEL_OPEN && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_DELETE_PUBLIC_CHANNEL) {
				return
			}

			if channel.Type == model.CHANNEL_PRIVATE && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_DELETE_PRIVATE_CHANNEL) {
				return
			}
		}

		if channel.DeleteAt > 0 {
			c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.deleted.app_error", nil, "")
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		if channel.Name == model.DEFAULT_CHANNEL {
			c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.cannot.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
			c.Err.StatusCode = http.StatusBadRequest
			return
		}

		post := &model.Post{
			ChannelId: channel.Id,
			Message:   fmt.Sprintf(c.T("api.channel.delete_channel.archived"), user.Username),
			Type:      model.POST_CHANNEL_DELETED,
			UserId:    c.Session.UserId,
		}

		if _, err := app.CreatePost(post, c.TeamId, false); err != nil {
			l4g.Error(utils.T("api.channel.delete_channel.failed_post.error"), err)
		}

		now := model.GetMillis()
		for _, hook := range incomingHooks {
			if result := <-app.Srv.Store.Webhook().DeleteIncoming(hook.Id, now); result.Err != nil {
				l4g.Error(utils.T("api.channel.delete_channel.incoming_webhook.error"), hook.Id)
			}
		}

		for _, hook := range outgoingHooks {
			if result := <-app.Srv.Store.Webhook().DeleteOutgoing(hook.Id, now); result.Err != nil {
				l4g.Error(utils.T("api.channel.delete_channel.outgoing_webhook.error"), hook.Id)
			}
		}

		if dresult := <-app.Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); dresult.Err != nil {
			c.Err = dresult.Err
			return
		}
		app.InvalidateCacheForChannel(channel.Id)

		c.LogAudit("name=" + channel.Name)

		message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_DELETED, c.TeamId, "", "", nil)
		message.Add("channel_id", channel.Id)

		app.Publish(message)

		result := make(map[string]string)
		result["id"] = channel.Id
		w.Write([]byte(model.MapToJson(result)))
	}
}
Beispiel #13
0
func TestWebSocketEvent(t *testing.T) {
	th := Setup().InitBasic()
	WebSocketClient, err := th.CreateWebSocketClient()
	if err != nil {
		t.Fatal(err)
	}
	defer WebSocketClient.Close()

	WebSocketClient.Listen()

	time.Sleep(300 * time.Millisecond)
	if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK {
		t.Fatal("should have responded OK to authentication challenge")
	}

	omitUser := make(map[string]bool, 1)
	omitUser["somerandomid"] = true
	evt1 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", th.BasicChannel.Id, "", omitUser)
	evt1.Add("user_id", "somerandomid")
	app.Publish(evt1)

	time.Sleep(300 * time.Millisecond)

	stop := make(chan bool)
	eventHit := false

	go func() {
		for {
			select {
			case resp := <-WebSocketClient.EventChannel:
				if resp.Event == model.WEBSOCKET_EVENT_TYPING && resp.Data["user_id"].(string) == "somerandomid" {
					eventHit = true
				}
			case <-stop:
				return
			}
		}
	}()

	time.Sleep(400 * time.Millisecond)

	stop <- true

	if !eventHit {
		t.Fatal("did not receive typing event")
	}

	evt2 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", "somerandomid", "", nil)
	go app.Publish(evt2)
	time.Sleep(300 * time.Millisecond)

	eventHit = false

	go func() {
		for {
			select {
			case resp := <-WebSocketClient.EventChannel:
				if resp.Event == model.WEBSOCKET_EVENT_TYPING {
					eventHit = true
				}
			case <-stop:
				return
			}
		}
	}()

	time.Sleep(400 * time.Millisecond)

	stop <- true

	if eventHit {
		t.Fatal("got typing event for bad channel id")
	}
}