2021-04-01 13:44:56 -04:00
|
|
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
|
|
|
// See LICENSE.txt for license information.
|
|
|
|
|
|
|
|
|
|
package sharedchannel
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/url"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2023-06-11 01:24:35 -04:00
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
|
|
|
"github.com/mattermost/mattermost/server/public/shared/i18n"
|
|
|
|
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
2025-09-18 10:14:24 -04:00
|
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
2021-04-01 13:44:56 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
// Team name regex taken from model.IsValidTeamName
|
|
|
|
|
permaLinkRegex = regexp.MustCompile(`https?://[0-9.\-A-Za-z]+/[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+/pl/([a-zA-Z0-9]+)`)
|
|
|
|
|
permaLinkSharedRegex = regexp.MustCompile(`https?://[0-9.\-A-Za-z]+/[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+/plshared/([a-zA-Z0-9]+)`)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
permalinkMarker = "plshared"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// processPermalinkToRemote processes all permalinks going towards a remote site.
|
|
|
|
|
func (scs *Service) processPermalinkToRemote(p *model.Post) string {
|
2025-09-18 10:14:24 -04:00
|
|
|
// request.CTX is not widely used in the shared channels code.
|
|
|
|
|
// Just create a new request.CTX for now.
|
|
|
|
|
rctx := request.EmptyContext(scs.server.Log())
|
|
|
|
|
|
2021-04-01 13:44:56 -04:00
|
|
|
var sent bool
|
|
|
|
|
return permaLinkRegex.ReplaceAllStringFunc(p.Message, func(msg string) string {
|
|
|
|
|
// Extract the postID (This is simple enough not to warrant full-blown URL parsing.)
|
|
|
|
|
lastSlash := strings.LastIndexByte(msg, '/')
|
|
|
|
|
postID := msg[lastSlash+1:]
|
2022-03-24 03:21:41 -04:00
|
|
|
opts := model.GetPostsOptions{
|
|
|
|
|
SkipFetchThreads: true,
|
|
|
|
|
}
|
2025-09-18 10:14:24 -04:00
|
|
|
postList, err := scs.server.GetStore().Post().Get(rctx, postID, opts, "", map[string]bool{})
|
2021-04-01 13:44:56 -04:00
|
|
|
if err != nil {
|
MM-68204: Use multi-level logging for shared channel and remote cluster service errors (#35949)
* Use multi-level logging for shared channel and remote cluster service errors
Service-specific log levels (LvlRemoteClusterServiceError, LvlSharedChannelServiceError)
were hidden from the main log by default, requiring explicit configuration to see them.
Switch all call sites to use LogM with multi-level combos so each log line is attributed
to both the standard level (error/warn) and the service-specific level. This surfaces
errors in the main log while preserving the ability to isolate them into dedicated files.
Each instance was reviewed and either kept as error (DB failures, security issues, config
errors, exhausted retries on critical data) or downgraded to warn (transient network
failures, remote-side reported errors with retry logic, non-critical data like profile
images, reactions, acknowledgements, and status).
2026-04-06 12:17:47 -04:00
|
|
|
scs.server.Log().LogM(mlog.MlvlSharedChannelServiceWarn, "Unable to get post during replacing permalinks", mlog.Err(err))
|
2021-04-01 13:44:56 -04:00
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
if len(postList.Order) == 0 {
|
MM-68204: Use multi-level logging for shared channel and remote cluster service errors (#35949)
* Use multi-level logging for shared channel and remote cluster service errors
Service-specific log levels (LvlRemoteClusterServiceError, LvlSharedChannelServiceError)
were hidden from the main log by default, requiring explicit configuration to see them.
Switch all call sites to use LogM with multi-level combos so each log line is attributed
to both the standard level (error/warn) and the service-specific level. This surfaces
errors in the main log while preserving the ability to isolate them into dedicated files.
Each instance was reviewed and either kept as error (DB failures, security issues, config
errors, exhausted retries on critical data) or downgraded to warn (transient network
failures, remote-side reported errors with retry logic, non-critical data like profile
images, reactions, acknowledgements, and status).
2026-04-06 12:17:47 -04:00
|
|
|
scs.server.Log().LogM(mlog.MlvlSharedChannelServiceWarn, "No post found for permalink", mlog.String("postID", postID))
|
2021-04-01 13:44:56 -04:00
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If postID is for a different channel
|
|
|
|
|
if postList.Posts[postList.Order[0]].ChannelId != p.ChannelId {
|
|
|
|
|
// Send ephemeral message to OP (only once per message).
|
|
|
|
|
if !sent {
|
|
|
|
|
scs.sendEphemeralPost(p.ChannelId, p.UserId, i18n.T("sharedchannel.permalink.not_found"))
|
|
|
|
|
sent = true
|
|
|
|
|
}
|
|
|
|
|
// But don't modify msg
|
|
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, modify pl to plshared as a marker to be replaced by remote sites
|
|
|
|
|
return strings.Replace(msg, "/pl/", "/"+permalinkMarker+"/", 1)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// processPermalinkFromRemote processes all permalinks coming from a remote site.
|
|
|
|
|
func (scs *Service) processPermalinkFromRemote(p *model.Post, team *model.Team) string {
|
|
|
|
|
return permaLinkSharedRegex.ReplaceAllStringFunc(p.Message, func(remoteLink string) string {
|
|
|
|
|
// Extract host name
|
|
|
|
|
parsed, err := url.Parse(remoteLink)
|
|
|
|
|
if err != nil {
|
MM-68204: Use multi-level logging for shared channel and remote cluster service errors (#35949)
* Use multi-level logging for shared channel and remote cluster service errors
Service-specific log levels (LvlRemoteClusterServiceError, LvlSharedChannelServiceError)
were hidden from the main log by default, requiring explicit configuration to see them.
Switch all call sites to use LogM with multi-level combos so each log line is attributed
to both the standard level (error/warn) and the service-specific level. This surfaces
errors in the main log while preserving the ability to isolate them into dedicated files.
Each instance was reviewed and either kept as error (DB failures, security issues, config
errors, exhausted retries on critical data) or downgraded to warn (transient network
failures, remote-side reported errors with retry logic, non-critical data like profile
images, reactions, acknowledgements, and status).
2026-04-06 12:17:47 -04:00
|
|
|
scs.server.Log().LogM(mlog.MlvlSharedChannelServiceWarn, "Unable to parse the remote link during replacing permalinks", mlog.Err(err))
|
2021-04-01 13:44:56 -04:00
|
|
|
return remoteLink
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Replace with local SiteURL
|
|
|
|
|
parsed.Scheme = scs.siteURL.Scheme
|
|
|
|
|
parsed.Host = scs.siteURL.Host
|
|
|
|
|
|
|
|
|
|
// Replace team name with local team
|
|
|
|
|
teamEnd := strings.Index(parsed.Path, "/"+permalinkMarker)
|
|
|
|
|
parsed.Path = "/" + team.Name + parsed.Path[teamEnd:]
|
|
|
|
|
|
|
|
|
|
// Replace plshared with pl
|
|
|
|
|
return strings.Replace(parsed.String(), "/"+permalinkMarker+"/", "/pl/", 1)
|
|
|
|
|
})
|
|
|
|
|
}
|