github.com/vnforks/kid@v5.11.1+incompatible/app/post.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/mattermost/mattermost-server/mlog"
    14  	"github.com/mattermost/mattermost-server/model"
    15  	"github.com/mattermost/mattermost-server/plugin"
    16  	"github.com/mattermost/mattermost-server/store"
    17  	"github.com/mattermost/mattermost-server/utils"
    18  )
    19  
    20  const (
    21  	PENDING_POST_IDS_CACHE_SIZE = 25000
    22  	PENDING_POST_IDS_CACHE_TTL  = 30 * time.Second
    23  )
    24  
    25  func (a *App) CreatePostAsUser(post *model.Post, currentSessionId string) (*model.Post, *model.AppError) {
    26  	// Check that channel has not been deleted
    27  	result := <-a.Srv.Store.Channel().Get(post.ChannelId, true)
    28  	if result.Err != nil {
    29  		err := model.NewAppError("CreatePostAsUser", "api.context.invalid_param.app_error", map[string]interface{}{"Name": "post.channel_id"}, result.Err.Error(), http.StatusBadRequest)
    30  		return nil, err
    31  	}
    32  	channel := result.Data.(*model.Channel)
    33  
    34  	if strings.HasPrefix(post.Type, model.POST_SYSTEM_MESSAGE_PREFIX) {
    35  		err := model.NewAppError("CreatePostAsUser", "api.context.invalid_param.app_error", map[string]interface{}{"Name": "post.type"}, "", http.StatusBadRequest)
    36  		return nil, err
    37  	}
    38  
    39  	if channel.DeleteAt != 0 {
    40  		err := model.NewAppError("createPost", "api.post.create_post.can_not_post_to_deleted.error", nil, "", http.StatusBadRequest)
    41  		return nil, err
    42  	}
    43  
    44  	rp, err := a.CreatePost(post, channel, true)
    45  	if err != nil {
    46  		if err.Id == "api.post.create_post.root_id.app_error" ||
    47  			err.Id == "api.post.create_post.channel_root_id.app_error" ||
    48  			err.Id == "api.post.create_post.parent_id.app_error" {
    49  			err.StatusCode = http.StatusBadRequest
    50  		}
    51  
    52  		if err.Id == "api.post.create_post.town_square_read_only" {
    53  			result := <-a.Srv.Store.User().Get(post.UserId)
    54  			if result.Err != nil {
    55  				return nil, result.Err
    56  			}
    57  			user := result.Data.(*model.User)
    58  
    59  			T := utils.GetUserTranslations(user.Locale)
    60  			a.SendEphemeralPost(
    61  				post.UserId,
    62  				&model.Post{
    63  					ChannelId: channel.Id,
    64  					ParentId:  post.ParentId,
    65  					RootId:    post.RootId,
    66  					UserId:    post.UserId,
    67  					Message:   T("api.post.create_post.town_square_read_only"),
    68  					CreateAt:  model.GetMillis() + 1,
    69  				},
    70  			)
    71  		}
    72  		return nil, err
    73  	}
    74  
    75  	// Update the LastViewAt only if the post does not have from_webhook prop set (eg. Zapier app)
    76  	if _, ok := post.Props["from_webhook"]; !ok {
    77  		if _, err := a.MarkChannelsAsViewed([]string{post.ChannelId}, post.UserId, currentSessionId); err != nil {
    78  			mlog.Error(fmt.Sprintf("Encountered error updating last viewed, channel_id=%s, user_id=%s, err=%v", post.ChannelId, post.UserId, err))
    79  		}
    80  	}
    81  
    82  	return rp, nil
    83  }
    84  
    85  func (a *App) CreatePostMissingChannel(post *model.Post, triggerWebhooks bool) (*model.Post, *model.AppError) {
    86  	result := <-a.Srv.Store.Channel().Get(post.ChannelId, true)
    87  	if result.Err != nil {
    88  		return nil, result.Err
    89  	}
    90  	channel := result.Data.(*model.Channel)
    91  
    92  	return a.CreatePost(post, channel, triggerWebhooks)
    93  }
    94  
    95  // deduplicateCreatePost attempts to make posting idempotent within a caching window.
    96  func (a *App) deduplicateCreatePost(post *model.Post) (foundPost *model.Post, err *model.AppError) {
    97  	// We rely on the client sending the pending post id across "duplicate" requests. If there
    98  	// isn't one, we can't deduplicate, so allow creation normally.
    99  	if post.PendingPostId == "" {
   100  		return nil, nil
   101  	}
   102  
   103  	const unknownPostId = ""
   104  
   105  	// Query the cache atomically for the given pending post id, saving a record if
   106  	// it hasn't previously been seen.
   107  	value, loaded := a.Srv.seenPendingPostIdsCache.GetOrAdd(post.PendingPostId, unknownPostId, PENDING_POST_IDS_CACHE_TTL)
   108  
   109  	// If we were the first thread to save this pending post id into the cache,
   110  	// proceed with create post normally.
   111  	if !loaded {
   112  		return nil, nil
   113  	}
   114  
   115  	postId := value.(string)
   116  
   117  	// If another thread saved the cache record, but hasn't yet updated it with the actual post
   118  	// id (because it's still saving), notify the client with an error. Ideally, we'd wait
   119  	// for the other thread, but coordinating that adds complexity to the happy path.
   120  	if postId == unknownPostId {
   121  		return nil, model.NewAppError("deduplicateCreatePost", "api.post.deduplicate_create_post.pending", nil, "", http.StatusInternalServerError)
   122  	}
   123  
   124  	// If the other thread finished creating the post, return the created post back to the
   125  	// client, making the API call feel idempotent.
   126  	actualPost, err := a.GetSinglePost(postId)
   127  	if err != nil {
   128  		return nil, model.NewAppError("deduplicateCreatePost", "api.post.deduplicate_create_post.failed_to_get", nil, err.Error(), http.StatusInternalServerError)
   129  	}
   130  
   131  	mlog.Debug("Deduplicated create post", mlog.String("post_id", actualPost.Id), mlog.String("pending_post_id", post.PendingPostId))
   132  
   133  	return actualPost, nil
   134  }
   135  
   136  func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhooks bool) (savedPost *model.Post, err *model.AppError) {
   137  	foundPost, err := a.deduplicateCreatePost(post)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	if foundPost != nil {
   142  		return foundPost, nil
   143  	}
   144  
   145  	// If we get this far, we've recorded the client-provided pending post id to the cache.
   146  	// Remove it if we fail below, allowing a proper retry by the client.
   147  	defer func() {
   148  		if post.PendingPostId == "" {
   149  			return
   150  		}
   151  
   152  		if err != nil {
   153  			a.Srv.seenPendingPostIdsCache.Remove(post.PendingPostId)
   154  			return
   155  		}
   156  
   157  		a.Srv.seenPendingPostIdsCache.AddWithExpiresInSecs(post.PendingPostId, savedPost.Id, int64(PENDING_POST_IDS_CACHE_TTL.Seconds()))
   158  	}()
   159  
   160  	post.SanitizeProps()
   161  
   162  	var pchan store.StoreChannel
   163  	if len(post.RootId) > 0 {
   164  		pchan = a.Srv.Store.Post().Get(post.RootId)
   165  	}
   166  
   167  	result := <-a.Srv.Store.User().Get(post.UserId)
   168  	if result.Err != nil {
   169  		return nil, result.Err
   170  	}
   171  	user := result.Data.(*model.User)
   172  
   173  	if a.License() != nil && *a.Config().TeamSettings.ExperimentalTownSquareIsReadOnly &&
   174  		!post.IsSystemMessage() &&
   175  		channel.Name == model.DEFAULT_CHANNEL &&
   176  		!a.RolesGrantPermission(user.GetRoles(), model.PERMISSION_MANAGE_SYSTEM.Id) {
   177  		return nil, model.NewAppError("createPost", "api.post.create_post.town_square_read_only", nil, "", http.StatusForbidden)
   178  	}
   179  
   180  	// Verify the parent/child relationships are correct
   181  	var parentPostList *model.PostList
   182  	if pchan != nil {
   183  		result = <-pchan
   184  		if result.Err != nil {
   185  			return nil, model.NewAppError("createPost", "api.post.create_post.root_id.app_error", nil, "", http.StatusBadRequest)
   186  		}
   187  		parentPostList = result.Data.(*model.PostList)
   188  		if len(parentPostList.Posts) == 0 || !parentPostList.IsChannelId(post.ChannelId) {
   189  			return nil, model.NewAppError("createPost", "api.post.create_post.channel_root_id.app_error", nil, "", http.StatusInternalServerError)
   190  		}
   191  
   192  		rootPost := parentPostList.Posts[post.RootId]
   193  		if len(rootPost.RootId) > 0 {
   194  			return nil, model.NewAppError("createPost", "api.post.create_post.root_id.app_error", nil, "", http.StatusBadRequest)
   195  		}
   196  
   197  		if post.ParentId == "" {
   198  			post.ParentId = post.RootId
   199  		}
   200  
   201  		if post.RootId != post.ParentId {
   202  			parent := parentPostList.Posts[post.ParentId]
   203  			if parent == nil {
   204  				return nil, model.NewAppError("createPost", "api.post.create_post.parent_id.app_error", nil, "", http.StatusInternalServerError)
   205  			}
   206  		}
   207  	}
   208  
   209  	post.Hashtags, _ = model.ParseHashtags(post.Message)
   210  
   211  	if err := a.FillInPostProps(post, channel); err != nil {
   212  		return nil, err
   213  	}
   214  
   215  	// Temporary fix so old plugins don't clobber new fields in SlackAttachment struct, see MM-13088
   216  	if attachments, ok := post.Props["attachments"].([]*model.SlackAttachment); ok {
   217  		jsonAttachments, err := json.Marshal(attachments)
   218  		if err == nil {
   219  			attachmentsInterface := []interface{}{}
   220  			err = json.Unmarshal(jsonAttachments, &attachmentsInterface)
   221  			post.Props["attachments"] = attachmentsInterface
   222  		}
   223  		if err != nil {
   224  			mlog.Error("Could not convert post attachments to map interface, err=%s" + err.Error())
   225  		}
   226  	}
   227  
   228  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   229  		var rejectionError *model.AppError
   230  		pluginContext := a.PluginContext()
   231  		pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   232  			replacementPost, rejectionReason := hooks.MessageWillBePosted(pluginContext, post)
   233  			if rejectionReason != "" {
   234  				rejectionError = model.NewAppError("createPost", "Post rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest)
   235  				return false
   236  			}
   237  			if replacementPost != nil {
   238  				post = replacementPost
   239  			}
   240  
   241  			return true
   242  		}, plugin.MessageWillBePostedId)
   243  		if rejectionError != nil {
   244  			return nil, rejectionError
   245  		}
   246  	}
   247  
   248  	result = <-a.Srv.Store.Post().Save(post)
   249  	if result.Err != nil {
   250  		return nil, result.Err
   251  	}
   252  	rpost := result.Data.(*model.Post)
   253  
   254  	// Update the mapping from pending post id to the actual post id, for any clients that
   255  	// might be duplicating requests.
   256  	a.Srv.seenPendingPostIdsCache.AddWithExpiresInSecs(post.PendingPostId, rpost.Id, int64(PENDING_POST_IDS_CACHE_TTL.Seconds()))
   257  
   258  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   259  		a.Srv.Go(func() {
   260  			pluginContext := a.PluginContext()
   261  			pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   262  				hooks.MessageHasBeenPosted(pluginContext, rpost)
   263  				return true
   264  			}, plugin.MessageHasBeenPostedId)
   265  		})
   266  	}
   267  
   268  	esInterface := a.Elasticsearch
   269  	if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
   270  		a.Srv.Go(func() {
   271  			if err := esInterface.IndexPost(rpost, channel.TeamId); err != nil {
   272  				mlog.Error("Encountered error indexing post", mlog.String("post_id", post.Id), mlog.Err(err))
   273  			}
   274  		})
   275  	}
   276  
   277  	if a.Metrics != nil {
   278  		a.Metrics.IncrementPostCreate()
   279  	}
   280  
   281  	if len(post.FileIds) > 0 {
   282  		if err := a.attachFilesToPost(post); err != nil {
   283  			mlog.Error("Encountered error attaching files to post", mlog.String("post_id", post.Id), mlog.Any("file_ids", post.FileIds), mlog.Err(result.Err))
   284  		}
   285  
   286  		if a.Metrics != nil {
   287  			a.Metrics.IncrementPostFileAttachment(len(post.FileIds))
   288  		}
   289  	}
   290  
   291  	// Normally, we would let the API layer call PreparePostForClient, but we do it here since it also needs
   292  	// to be done when we send the post over the websocket in handlePostEvents
   293  	rpost = a.PreparePostForClient(rpost, true)
   294  
   295  	if err := a.handlePostEvents(rpost, user, channel, triggerWebhooks, parentPostList); err != nil {
   296  		mlog.Error("Failed to handle post events", mlog.Err(err))
   297  	}
   298  
   299  	return rpost, nil
   300  }
   301  
   302  func (a *App) attachFilesToPost(post *model.Post) *model.AppError {
   303  	var attachedIds []string
   304  	for _, fileId := range post.FileIds {
   305  		result := <-a.Srv.Store.FileInfo().AttachToPost(fileId, post.Id, post.UserId)
   306  		if result.Err != nil {
   307  			mlog.Warn("Failed to attach file to post", mlog.String("file_id", fileId), mlog.String("post_id", post.Id), mlog.Err(result.Err))
   308  			continue
   309  		}
   310  
   311  		attachedIds = append(attachedIds, fileId)
   312  	}
   313  
   314  	if len(post.FileIds) != len(attachedIds) {
   315  		// We couldn't attach all files to the post, so ensure that post.FileIds reflects what was actually attached
   316  		post.FileIds = attachedIds
   317  
   318  		result := <-a.Srv.Store.Post().Overwrite(post)
   319  		if result.Err != nil {
   320  			return result.Err
   321  		}
   322  	}
   323  
   324  	return nil
   325  }
   326  
   327  // FillInPostProps should be invoked before saving posts to fill in properties such as
   328  // channel_mentions.
   329  //
   330  // If channel is nil, FillInPostProps will look up the channel corresponding to the post.
   331  func (a *App) FillInPostProps(post *model.Post, channel *model.Channel) *model.AppError {
   332  	channelMentions := post.ChannelMentions()
   333  	channelMentionsProp := make(map[string]interface{})
   334  
   335  	if len(channelMentions) > 0 {
   336  		if channel == nil {
   337  			result := <-a.Srv.Store.Channel().GetForPost(post.Id)
   338  			if result.Err != nil {
   339  				return model.NewAppError("FillInPostProps", "api.context.invalid_param.app_error", map[string]interface{}{"Name": "post.channel_id"}, result.Err.Error(), http.StatusBadRequest)
   340  			}
   341  			channel = result.Data.(*model.Channel)
   342  		}
   343  
   344  		mentionedChannels, err := a.GetChannelsByNames(channelMentions, channel.TeamId)
   345  		if err != nil {
   346  			return err
   347  		}
   348  
   349  		for _, mentioned := range mentionedChannels {
   350  			if mentioned.Type == model.CHANNEL_OPEN {
   351  				channelMentionsProp[mentioned.Name] = map[string]interface{}{
   352  					"display_name": mentioned.DisplayName,
   353  				}
   354  			}
   355  		}
   356  	}
   357  
   358  	if len(channelMentionsProp) > 0 {
   359  		post.AddProp("channel_mentions", channelMentionsProp)
   360  	} else if post.Props != nil {
   361  		delete(post.Props, "channel_mentions")
   362  	}
   363  
   364  	return nil
   365  }
   366  
   367  func (a *App) handlePostEvents(post *model.Post, user *model.User, channel *model.Channel, triggerWebhooks bool, parentPostList *model.PostList) *model.AppError {
   368  	var team *model.Team
   369  	if len(channel.TeamId) > 0 {
   370  		result := <-a.Srv.Store.Team().Get(channel.TeamId)
   371  		if result.Err != nil {
   372  			return result.Err
   373  		}
   374  		team = result.Data.(*model.Team)
   375  	} else {
   376  		// Blank team for DMs
   377  		team = &model.Team{}
   378  	}
   379  
   380  	a.InvalidateCacheForChannel(channel)
   381  	a.InvalidateCacheForChannelPosts(channel.Id)
   382  
   383  	if _, err := a.SendNotifications(post, team, channel, user, parentPostList); err != nil {
   384  		return err
   385  	}
   386  
   387  	if triggerWebhooks {
   388  		a.Srv.Go(func() {
   389  			if err := a.handleWebhookEvents(post, team, channel, user); err != nil {
   390  				mlog.Error(err.Error())
   391  			}
   392  		})
   393  	}
   394  
   395  	return nil
   396  }
   397  
   398  func (a *App) SendEphemeralPost(userId string, post *model.Post) *model.Post {
   399  	post.Type = model.POST_EPHEMERAL
   400  
   401  	// fill in fields which haven't been specified which have sensible defaults
   402  	if post.Id == "" {
   403  		post.Id = model.NewId()
   404  	}
   405  	if post.CreateAt == 0 {
   406  		post.CreateAt = model.GetMillis()
   407  	}
   408  	if post.Props == nil {
   409  		post.Props = model.StringInterface{}
   410  	}
   411  
   412  	post.GenerateActionIds()
   413  	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, "", post.ChannelId, userId, nil)
   414  	post = a.PreparePostForClient(post, true)
   415  	post = model.AddPostActionCookies(post, a.PostActionCookieSecret())
   416  	message.Add("post", post.ToJson())
   417  	a.Publish(message)
   418  
   419  	return post
   420  }
   421  
   422  func (a *App) UpdateEphemeralPost(userId string, post *model.Post) *model.Post {
   423  	post.Type = model.POST_EPHEMERAL
   424  
   425  	post.UpdateAt = model.GetMillis()
   426  	if post.Props == nil {
   427  		post.Props = model.StringInterface{}
   428  	}
   429  
   430  	post.GenerateActionIds()
   431  	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", post.ChannelId, userId, nil)
   432  	post = a.PreparePostForClient(post, true)
   433  	post = model.AddPostActionCookies(post, a.PostActionCookieSecret())
   434  	message.Add("post", post.ToJson())
   435  	a.Publish(message)
   436  
   437  	return post
   438  }
   439  
   440  func (a *App) DeleteEphemeralPost(userId string, post *model.Post) *model.Post {
   441  	post.Type = model.POST_EPHEMERAL
   442  	post.DeleteAt = model.GetMillis()
   443  	post.UpdateAt = post.DeleteAt
   444  	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, userId, nil)
   445  
   446  	message.Add("post", post.ToJson())
   447  	a.Publish(message)
   448  
   449  	return post
   450  }
   451  
   452  func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) {
   453  	post.SanitizeProps()
   454  
   455  	result := <-a.Srv.Store.Post().Get(post.Id)
   456  	if result.Err != nil {
   457  		return nil, result.Err
   458  	}
   459  	oldPost := result.Data.(*model.PostList).Posts[post.Id]
   460  
   461  	if oldPost == nil {
   462  		err := model.NewAppError("UpdatePost", "api.post.update_post.find.app_error", nil, "id="+post.Id, http.StatusBadRequest)
   463  		return nil, err
   464  	}
   465  
   466  	if oldPost.DeleteAt != 0 {
   467  		err := model.NewAppError("UpdatePost", "api.post.update_post.permissions_details.app_error", map[string]interface{}{"PostId": post.Id}, "", http.StatusBadRequest)
   468  		return nil, err
   469  	}
   470  
   471  	if oldPost.IsSystemMessage() {
   472  		err := model.NewAppError("UpdatePost", "api.post.update_post.system_message.app_error", nil, "id="+post.Id, http.StatusBadRequest)
   473  		return nil, err
   474  	}
   475  
   476  	if a.License() != nil {
   477  		if *a.Config().ServiceSettings.PostEditTimeLimit != -1 && model.GetMillis() > oldPost.CreateAt+int64(*a.Config().ServiceSettings.PostEditTimeLimit*1000) && post.Message != oldPost.Message {
   478  			err := model.NewAppError("UpdatePost", "api.post.update_post.permissions_time_limit.app_error", map[string]interface{}{"timeLimit": *a.Config().ServiceSettings.PostEditTimeLimit}, "", http.StatusBadRequest)
   479  			return nil, err
   480  		}
   481  	}
   482  
   483  	channel, err := a.GetChannel(oldPost.ChannelId)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  
   488  	if channel.DeleteAt != 0 {
   489  		err := model.NewAppError("UpdatePost", "api.post.update_post.can_not_update_post_in_deleted.error", nil, "", http.StatusBadRequest)
   490  		return nil, err
   491  	}
   492  
   493  	newPost := &model.Post{}
   494  	*newPost = *oldPost
   495  
   496  	if newPost.Message != post.Message {
   497  		newPost.Message = post.Message
   498  		newPost.EditAt = model.GetMillis()
   499  		newPost.Hashtags, _ = model.ParseHashtags(post.Message)
   500  	}
   501  
   502  	if !safeUpdate {
   503  		newPost.IsPinned = post.IsPinned
   504  		newPost.HasReactions = post.HasReactions
   505  		newPost.FileIds = post.FileIds
   506  		newPost.Props = post.Props
   507  	}
   508  
   509  	// Avoid deep-equal checks if EditAt was already modified through message change
   510  	if newPost.EditAt == oldPost.EditAt && (!oldPost.FileIds.Equals(newPost.FileIds) || !oldPost.AttachmentsEqual(newPost)) {
   511  		newPost.EditAt = model.GetMillis()
   512  	}
   513  
   514  	if err := a.FillInPostProps(post, nil); err != nil {
   515  		return nil, err
   516  	}
   517  
   518  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   519  		var rejectionReason string
   520  		pluginContext := a.PluginContext()
   521  		pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   522  			newPost, rejectionReason = hooks.MessageWillBeUpdated(pluginContext, newPost, oldPost)
   523  			return post != nil
   524  		}, plugin.MessageWillBeUpdatedId)
   525  		if newPost == nil {
   526  			return nil, model.NewAppError("UpdatePost", "Post rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest)
   527  		}
   528  	}
   529  
   530  	result = <-a.Srv.Store.Post().Update(newPost, oldPost)
   531  	if result.Err != nil {
   532  		return nil, result.Err
   533  	}
   534  	rpost := result.Data.(*model.Post)
   535  
   536  	if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
   537  		a.Srv.Go(func() {
   538  			pluginContext := a.PluginContext()
   539  			pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
   540  				hooks.MessageHasBeenUpdated(pluginContext, newPost, oldPost)
   541  				return true
   542  			}, plugin.MessageHasBeenUpdatedId)
   543  		})
   544  	}
   545  
   546  	esInterface := a.Elasticsearch
   547  	if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
   548  		a.Srv.Go(func() {
   549  			rchannel := <-a.Srv.Store.Channel().GetForPost(rpost.Id)
   550  			if rchannel.Err != nil {
   551  				mlog.Error(fmt.Sprintf("Couldn't get channel %v for post %v for Elasticsearch indexing.", rpost.ChannelId, rpost.Id))
   552  				return
   553  			}
   554  			if err := esInterface.IndexPost(rpost, rchannel.Data.(*model.Channel).TeamId); err != nil {
   555  				mlog.Error("Encountered error indexing post", mlog.String("post_id", post.Id), mlog.Err(err))
   556  			}
   557  		})
   558  	}
   559  
   560  	rpost = a.PreparePostForClient(rpost, false)
   561  
   562  	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", rpost.ChannelId, "", nil)
   563  	message.Add("post", rpost.ToJson())
   564  	a.Publish(message)
   565  
   566  	a.InvalidateCacheForChannelPosts(rpost.ChannelId)
   567  
   568  	return rpost, nil
   569  }
   570  
   571  func (a *App) PatchPost(postId string, patch *model.PostPatch) (*model.Post, *model.AppError) {
   572  	post, err := a.GetSinglePost(postId)
   573  	if err != nil {
   574  		return nil, err
   575  	}
   576  
   577  	channel, err := a.GetChannel(post.ChannelId)
   578  	if err != nil {
   579  		return nil, err
   580  	}
   581  
   582  	if channel.DeleteAt != 0 {
   583  		err = model.NewAppError("PatchPost", "api.post.patch_post.can_not_update_post_in_deleted.error", nil, "", http.StatusBadRequest)
   584  		return nil, err
   585  	}
   586  
   587  	post.Patch(patch)
   588  
   589  	updatedPost, err := a.UpdatePost(post, false)
   590  	if err != nil {
   591  		return nil, err
   592  	}
   593  
   594  	return updatedPost, nil
   595  }
   596  
   597  func (a *App) GetPostsPage(channelId string, page int, perPage int) (*model.PostList, *model.AppError) {
   598  	result := <-a.Srv.Store.Post().GetPosts(channelId, page*perPage, perPage, true)
   599  	if result.Err != nil {
   600  		return nil, result.Err
   601  	}
   602  	return result.Data.(*model.PostList), nil
   603  }
   604  
   605  func (a *App) GetPosts(channelId string, offset int, limit int) (*model.PostList, *model.AppError) {
   606  	result := <-a.Srv.Store.Post().GetPosts(channelId, offset, limit, true)
   607  	if result.Err != nil {
   608  		return nil, result.Err
   609  	}
   610  	return result.Data.(*model.PostList), nil
   611  }
   612  
   613  func (a *App) GetPostsEtag(channelId string) string {
   614  	return (<-a.Srv.Store.Post().GetEtag(channelId, true)).Data.(string)
   615  }
   616  
   617  func (a *App) GetPostsSince(channelId string, time int64) (*model.PostList, *model.AppError) {
   618  	result := <-a.Srv.Store.Post().GetPostsSince(channelId, time, true)
   619  	if result.Err != nil {
   620  		return nil, result.Err
   621  	}
   622  	return result.Data.(*model.PostList), nil
   623  }
   624  
   625  func (a *App) GetSinglePost(postId string) (*model.Post, *model.AppError) {
   626  	result := <-a.Srv.Store.Post().GetSingle(postId)
   627  	if result.Err != nil {
   628  		return nil, result.Err
   629  	}
   630  	return result.Data.(*model.Post), nil
   631  }
   632  
   633  func (a *App) GetPostThread(postId string) (*model.PostList, *model.AppError) {
   634  	result := <-a.Srv.Store.Post().Get(postId)
   635  	if result.Err != nil {
   636  		return nil, result.Err
   637  	}
   638  	return result.Data.(*model.PostList), nil
   639  }
   640  
   641  func (a *App) GetFlaggedPosts(userId string, offset int, limit int) (*model.PostList, *model.AppError) {
   642  	result := <-a.Srv.Store.Post().GetFlaggedPosts(userId, offset, limit)
   643  	if result.Err != nil {
   644  		return nil, result.Err
   645  	}
   646  	return result.Data.(*model.PostList), nil
   647  }
   648  
   649  func (a *App) GetFlaggedPostsForTeam(userId, teamId string, offset int, limit int) (*model.PostList, *model.AppError) {
   650  	result := <-a.Srv.Store.Post().GetFlaggedPostsForTeam(userId, teamId, offset, limit)
   651  	if result.Err != nil {
   652  		return nil, result.Err
   653  	}
   654  	return result.Data.(*model.PostList), nil
   655  }
   656  
   657  func (a *App) GetFlaggedPostsForChannel(userId, channelId string, offset int, limit int) (*model.PostList, *model.AppError) {
   658  	result := <-a.Srv.Store.Post().GetFlaggedPostsForChannel(userId, channelId, offset, limit)
   659  	if result.Err != nil {
   660  		return nil, result.Err
   661  	}
   662  	return result.Data.(*model.PostList), nil
   663  }
   664  
   665  func (a *App) GetPermalinkPost(postId string, userId string) (*model.PostList, *model.AppError) {
   666  	result := <-a.Srv.Store.Post().Get(postId)
   667  	if result.Err != nil {
   668  		return nil, result.Err
   669  	}
   670  	list := result.Data.(*model.PostList)
   671  
   672  	if len(list.Order) != 1 {
   673  		return nil, model.NewAppError("getPermalinkTmp", "api.post_get_post_by_id.get.app_error", nil, "", http.StatusNotFound)
   674  	}
   675  	post := list.Posts[list.Order[0]]
   676  
   677  	channel, err := a.GetChannel(post.ChannelId)
   678  	if err != nil {
   679  		return nil, err
   680  	}
   681  
   682  	if err = a.JoinChannel(channel, userId); err != nil {
   683  		return nil, err
   684  	}
   685  
   686  	return list, nil
   687  }
   688  
   689  func (a *App) GetPostsBeforePost(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) {
   690  	result := <-a.Srv.Store.Post().GetPostsBefore(channelId, postId, perPage, page*perPage)
   691  	if result.Err != nil {
   692  		return nil, result.Err
   693  	}
   694  	return result.Data.(*model.PostList), nil
   695  }
   696  
   697  func (a *App) GetPostsAfterPost(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) {
   698  	result := <-a.Srv.Store.Post().GetPostsAfter(channelId, postId, perPage, page*perPage)
   699  	if result.Err != nil {
   700  		return nil, result.Err
   701  	}
   702  	return result.Data.(*model.PostList), nil
   703  }
   704  
   705  func (a *App) GetPostsAroundPost(postId, channelId string, offset, limit int, before bool) (*model.PostList, *model.AppError) {
   706  	var pchan store.StoreChannel
   707  	if before {
   708  		pchan = a.Srv.Store.Post().GetPostsBefore(channelId, postId, limit, offset)
   709  	} else {
   710  		pchan = a.Srv.Store.Post().GetPostsAfter(channelId, postId, limit, offset)
   711  	}
   712  
   713  	result := <-pchan
   714  	if result.Err != nil {
   715  		return nil, result.Err
   716  	}
   717  	return result.Data.(*model.PostList), nil
   718  }
   719  
   720  func (a *App) DeletePost(postId, deleteByID string) (*model.Post, *model.AppError) {
   721  	result := <-a.Srv.Store.Post().GetSingle(postId)
   722  	if result.Err != nil {
   723  		result.Err.StatusCode = http.StatusBadRequest
   724  		return nil, result.Err
   725  	}
   726  	post := result.Data.(*model.Post)
   727  
   728  	channel, err := a.GetChannel(post.ChannelId)
   729  	if err != nil {
   730  		return nil, err
   731  	}
   732  
   733  	if channel.DeleteAt != 0 {
   734  		err := model.NewAppError("DeletePost", "api.post.delete_post.can_not_delete_post_in_deleted.error", nil, "", http.StatusBadRequest)
   735  		return nil, err
   736  	}
   737  
   738  	if result := <-a.Srv.Store.Post().Delete(postId, model.GetMillis(), deleteByID); result.Err != nil {
   739  		return nil, result.Err
   740  	}
   741  
   742  	message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, "", nil)
   743  	message.Add("post", a.PreparePostForClient(post, false).ToJson())
   744  	a.Publish(message)
   745  
   746  	a.Srv.Go(func() {
   747  		a.DeletePostFiles(post)
   748  	})
   749  	a.Srv.Go(func() {
   750  		a.DeleteFlaggedPosts(post.Id)
   751  	})
   752  
   753  	esInterface := a.Elasticsearch
   754  	if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
   755  		a.Srv.Go(func() {
   756  			if err := esInterface.DeletePost(post); err != nil {
   757  				mlog.Error("Encountered error deleting post", mlog.String("post_id", post.Id), mlog.Err(err))
   758  			}
   759  		})
   760  	}
   761  
   762  	a.InvalidateCacheForChannelPosts(post.ChannelId)
   763  
   764  	return post, nil
   765  }
   766  
   767  func (a *App) DeleteFlaggedPosts(postId string) {
   768  	if result := <-a.Srv.Store.Preference().DeleteCategoryAndName(model.PREFERENCE_CATEGORY_FLAGGED_POST, postId); result.Err != nil {
   769  		mlog.Warn(fmt.Sprintf("Unable to delete flagged post preference when deleting post, err=%v", result.Err))
   770  		return
   771  	}
   772  }
   773  
   774  func (a *App) DeletePostFiles(post *model.Post) {
   775  	if len(post.FileIds) == 0 {
   776  		return
   777  	}
   778  
   779  	if result := <-a.Srv.Store.FileInfo().DeleteForPost(post.Id); result.Err != nil {
   780  		mlog.Warn(fmt.Sprintf("Encountered error when deleting files for post, post_id=%v, err=%v", post.Id, result.Err), mlog.String("post_id", post.Id))
   781  	}
   782  }
   783  
   784  func (a *App) parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId string, includeDeleted bool) (*model.Channel, error) {
   785  	if strings.HasPrefix(channelName, "@") && strings.Contains(channelName, ",") {
   786  		var userIds []string
   787  		users, err := a.GetUsersByUsernames(strings.Split(channelName[1:], ","), false)
   788  		if err != nil {
   789  			return nil, err
   790  		}
   791  		for _, user := range users {
   792  			userIds = append(userIds, user.Id)
   793  		}
   794  
   795  		channel, err := a.GetGroupChannel(userIds)
   796  		if err != nil {
   797  			return nil, err
   798  		}
   799  		return channel, nil
   800  	}
   801  
   802  	if strings.HasPrefix(channelName, "@") && !strings.Contains(channelName, ",") {
   803  		user, err := a.GetUserByUsername(channelName[1:])
   804  		if err != nil {
   805  			return nil, err
   806  		}
   807  		channel, err := a.GetOrCreateDirectChannel(userId, user.Id)
   808  		if err != nil {
   809  			return nil, err
   810  		}
   811  		return channel, nil
   812  	}
   813  
   814  	channel, err := a.GetChannelByName(channelName, teamId, includeDeleted)
   815  	if err != nil {
   816  		return nil, err
   817  	}
   818  	return channel, nil
   819  }
   820  
   821  func (a *App) searchPostsInTeam(teamId string, userId string, paramsList []*model.SearchParams, modifierFun func(*model.SearchParams)) (*model.PostList, *model.AppError) {
   822  	channels := []store.StoreChannel{}
   823  
   824  	for _, params := range paramsList {
   825  		// Don't allow users to search for everything.
   826  		if params.Terms == "*" {
   827  			continue
   828  		}
   829  		modifierFun(params)
   830  		channels = append(channels, a.Srv.Store.Post().Search(teamId, userId, params))
   831  	}
   832  
   833  	posts := model.NewPostList()
   834  	for _, channel := range channels {
   835  		result := <-channel
   836  		if result.Err != nil {
   837  			return nil, result.Err
   838  		}
   839  		data := result.Data.(*model.PostList)
   840  		posts.Extend(data)
   841  	}
   842  
   843  	posts.SortByCreateAt()
   844  	return posts, nil
   845  }
   846  
   847  func (a *App) SearchPostsInTeam(teamId string, paramsList []*model.SearchParams) (*model.PostList, *model.AppError) {
   848  	if !*a.Config().ServiceSettings.EnablePostSearch {
   849  		return nil, model.NewAppError("SearchPostsInTeam", "store.sql_post.search.disabled", nil, fmt.Sprintf("teamId=%v", teamId), http.StatusNotImplemented)
   850  	}
   851  	return a.searchPostsInTeam(teamId, "", paramsList, func(params *model.SearchParams) {
   852  		params.SearchWithoutUserId = true
   853  	})
   854  }
   855  
   856  func (a *App) SearchPostsInTeamForUser(terms string, userId string, teamId string, isOrSearch bool, includeDeletedChannels bool, timeZoneOffset int, page, perPage int) (*model.PostSearchResults, *model.AppError) {
   857  	paramsList := model.ParseSearchParams(terms, timeZoneOffset)
   858  	includeDeleted := includeDeletedChannels && *a.Config().TeamSettings.ExperimentalViewArchivedChannels
   859  
   860  	esInterface := a.Elasticsearch
   861  	license := a.License()
   862  	if esInterface != nil && *a.Config().ElasticsearchSettings.EnableSearching && license != nil && *license.Features.Elasticsearch {
   863  		finalParamsList := []*model.SearchParams{}
   864  
   865  		for _, params := range paramsList {
   866  			params.OrTerms = isOrSearch
   867  			// Don't allow users to search for "*"
   868  			if params.Terms != "*" {
   869  				// Convert channel names to channel IDs
   870  				for idx, channelName := range params.InChannels {
   871  					channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels)
   872  					if err != nil {
   873  						mlog.Error(fmt.Sprint(err))
   874  						continue
   875  					}
   876  					params.InChannels[idx] = channel.Id
   877  				}
   878  
   879  				// Convert usernames to user IDs
   880  				for idx, username := range params.FromUsers {
   881  					if user, err := a.GetUserByUsername(username); err != nil {
   882  						mlog.Error(fmt.Sprint(err))
   883  					} else {
   884  						params.FromUsers[idx] = user.Id
   885  					}
   886  				}
   887  
   888  				finalParamsList = append(finalParamsList, params)
   889  			}
   890  		}
   891  
   892  		// If the processed search params are empty, return empty search results.
   893  		if len(finalParamsList) == 0 {
   894  			return model.MakePostSearchResults(model.NewPostList(), nil), nil
   895  		}
   896  
   897  		// We only allow the user to search in channels they are a member of.
   898  		userChannels, err := a.GetChannelsForUser(teamId, userId, includeDeleted)
   899  		if err != nil {
   900  			mlog.Error(fmt.Sprint(err))
   901  			return nil, err
   902  		}
   903  
   904  		postIds, matches, err := a.Elasticsearch.SearchPosts(userChannels, finalParamsList, page, perPage)
   905  		if err != nil {
   906  			return nil, err
   907  		}
   908  
   909  		// Get the posts
   910  		postList := model.NewPostList()
   911  		if len(postIds) > 0 {
   912  			presult := <-a.Srv.Store.Post().GetPostsByIds(postIds)
   913  			if presult.Err != nil {
   914  				return nil, presult.Err
   915  			}
   916  			for _, p := range presult.Data.([]*model.Post) {
   917  				if p.DeleteAt == 0 {
   918  					postList.AddPost(p)
   919  					postList.AddOrder(p.Id)
   920  				}
   921  			}
   922  		}
   923  
   924  		return model.MakePostSearchResults(postList, matches), nil
   925  	}
   926  
   927  	if !*a.Config().ServiceSettings.EnablePostSearch {
   928  		return nil, model.NewAppError("SearchPostsInTeamForUser", "store.sql_post.search.disabled", nil, fmt.Sprintf("teamId=%v userId=%v", teamId, userId), http.StatusNotImplemented)
   929  	}
   930  
   931  	// Since we don't support paging we just return nothing for later pages
   932  	if page > 0 {
   933  		return model.MakePostSearchResults(model.NewPostList(), nil), nil
   934  	}
   935  
   936  	posts, err := a.searchPostsInTeam(teamId, userId, paramsList, func(params *model.SearchParams) {
   937  		params.IncludeDeletedChannels = includeDeleted
   938  		params.OrTerms = isOrSearch
   939  		for idx, channelName := range params.InChannels {
   940  			if strings.HasPrefix(channelName, "@") {
   941  				channel, err := a.parseAndFetchChannelIdByNameFromInFilter(channelName, userId, teamId, includeDeletedChannels)
   942  				if err != nil {
   943  					mlog.Error(fmt.Sprint(err))
   944  					continue
   945  				}
   946  				params.InChannels[idx] = channel.Name
   947  			}
   948  		}
   949  	})
   950  	if err != nil {
   951  		return nil, err
   952  	}
   953  	return model.MakePostSearchResults(posts, nil), nil
   954  }
   955  
   956  func (a *App) GetFileInfosForPostWithMigration(postId string) ([]*model.FileInfo, *model.AppError) {
   957  	pchan := a.Srv.Store.Post().GetSingle(postId)
   958  
   959  	infos, err := a.GetFileInfosForPost(postId)
   960  	if err != nil {
   961  		return nil, err
   962  	}
   963  
   964  	if len(infos) == 0 {
   965  		// No FileInfos were returned so check if they need to be created for this post
   966  		result := <-pchan
   967  		if result.Err != nil {
   968  			return nil, result.Err
   969  		}
   970  		post := result.Data.(*model.Post)
   971  
   972  		if len(post.Filenames) > 0 {
   973  			a.Srv.Store.FileInfo().InvalidateFileInfosForPostCache(postId)
   974  			// The post has Filenames that need to be replaced with FileInfos
   975  			infos = a.MigrateFilenamesToFileInfos(post)
   976  		}
   977  	}
   978  
   979  	return infos, nil
   980  }
   981  
   982  func (a *App) GetFileInfosForPost(postId string) ([]*model.FileInfo, *model.AppError) {
   983  	result := <-a.Srv.Store.FileInfo().GetForPost(postId, false, true)
   984  	if result.Err != nil {
   985  		return nil, result.Err
   986  	}
   987  
   988  	return result.Data.([]*model.FileInfo), nil
   989  }
   990  
   991  func (a *App) PostWithProxyAddedToImageURLs(post *model.Post) *model.Post {
   992  	if f := a.ImageProxyAdder(); f != nil {
   993  		return post.WithRewrittenImageURLs(f)
   994  	}
   995  	return post
   996  }
   997  
   998  func (a *App) PostWithProxyRemovedFromImageURLs(post *model.Post) *model.Post {
   999  	if f := a.ImageProxyRemover(); f != nil {
  1000  		return post.WithRewrittenImageURLs(f)
  1001  	}
  1002  	return post
  1003  }
  1004  
  1005  func (a *App) PostPatchWithProxyRemovedFromImageURLs(patch *model.PostPatch) *model.PostPatch {
  1006  	if f := a.ImageProxyRemover(); f != nil {
  1007  		return patch.WithRewrittenImageURLs(f)
  1008  	}
  1009  	return patch
  1010  }
  1011  
  1012  func (a *App) ImageProxyAdder() func(string) string {
  1013  	if !*a.Config().ImageProxySettings.Enable {
  1014  		return nil
  1015  	}
  1016  
  1017  	return func(url string) string {
  1018  		return a.Srv.ImageProxy.GetProxiedImageURL(url)
  1019  	}
  1020  }
  1021  
  1022  func (a *App) ImageProxyRemover() (f func(string) string) {
  1023  	if !*a.Config().ImageProxySettings.Enable {
  1024  		return nil
  1025  	}
  1026  
  1027  	return func(url string) string {
  1028  		return a.Srv.ImageProxy.GetUnproxiedImageURL(url)
  1029  	}
  1030  }
  1031  
  1032  func (a *App) MaxPostSize() int {
  1033  	result := <-a.Srv.Store.Post().GetMaxPostSize()
  1034  	if result.Err != nil {
  1035  		mlog.Error(fmt.Sprint(result.Err))
  1036  		return model.POST_MESSAGE_MAX_RUNES_V1
  1037  	}
  1038  	return result.Data.(int)
  1039  }