Files
mostlymatter/model/incoming_webhook.go
Claudio Costa 92837fa1ee [MM-22051] Remove To/From JSON (#18070)
* Posts

* Add missing translation

* Fix internal store marshaling

* [MM-22051] Remove To/From JSON (Channels) (#18116)

* Channels

* Channel members

* ChannelSearch

* Channel categories, list, sidebar, stats, view

* Fix conversions

* [MM-22051] Remove To/From JSON (Users) (#18121)

* User related structs

* Fix return

* Team related structures (#18127)

* [MM-22051] Remove To/From JSON (Status, Bot, Reaction, Thread, FileInfo) (#18130)

* Status

* Bot

* Reaction

* Thread

* FileInfo

* Some fixes

* Translations update from Weblate (#18143)

* Translated using Weblate (German)

Currently translated at 100.0% (2309 of 2309 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/de/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (2309 of 2309 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2309 of 2309 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/hu/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/de/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/hu/

* Translated using Weblate (English (Australia))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/en_AU/

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/bg/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/ja/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: mattermost-languages-shipped/mattermost-server
Translate-URL: https://translate.mattermost.com/projects/mattermost/mattermost-server_master/zh_Hans/

Co-authored-by: JtheBAB <srast@bioc.uzh.ch>
Co-authored-by: Kaya Zeren <kayazeren@gmail.com>
Co-authored-by: Tóth Csaba // Online ERP Hungary Kft <csaba.toth@online-erp.hu>
Co-authored-by: Matthew Williams <Matthew.Williams@outlook.com.au>
Co-authored-by: Nikolai Zahariev <nikolaiz@yahoo.com>
Co-authored-by: kaakaa <stooner.hoe@gmail.com>
Co-authored-by: aeomin <lin@aeomin.net>

Co-authored-by: Weblate (bot) <hosted@weblate.org>
Co-authored-by: JtheBAB <srast@bioc.uzh.ch>
Co-authored-by: Kaya Zeren <kayazeren@gmail.com>
Co-authored-by: Tóth Csaba // Online ERP Hungary Kft <csaba.toth@online-erp.hu>
Co-authored-by: Matthew Williams <Matthew.Williams@outlook.com.au>
Co-authored-by: Nikolai Zahariev <nikolaiz@yahoo.com>
Co-authored-by: kaakaa <stooner.hoe@gmail.com>
Co-authored-by: aeomin <lin@aeomin.net>

* [MM-22051] Remove To/From JSON methods from model (#18138)

* Scheme

* Role

* Session

* Config

* Status

* Fix logic

* Emoji

* GuestsInvite

* Group

* Command

* ClusterInfo

* License

* Job

* System

* Plugin

* Command2

* IncomingWebhook

* OutgoingWebhook

* Fix tests

* Update traslation

* Some fixes

* Add missing return

* Simplify

* Make Config.ToJSONFiltered() return []byte

* Make Busy.ToJSON() return []byte

* Include error in log

* Split logic

* [MM-22051] Remove To/From JSON (final) (#18150)

* SwitchRequest

* PluginEventData

* Permalink

* PushNotification

* SuggestCommand

* PluginsResponse

* WebSocketMessage

* RemoteCluster

* SharedChannel

* PluginStatuses

* InitialLoad

* ClusterDiscovery

* ClusterStats

* MfaSecret

* GroupSyncable

* SAML

* WebSocketRequest

* TypingRequest

* SecurityBulletin

* OAuthApp

* IntegrationAction

* DataRetention

* Preference

* FileInfoList

* Compliance

* Preferences

* FileInfoSearchResults

* TermsOfService

* InstallMarketplacePluginRequest

* GitLabUser

* UploadSessions

* Remove unused helpers

* Fix tests

* [MM-23280] Fix linting for ToJSON/FromJSON (#18153)

* SwitchRequest

* PluginEventData

* Permalink

* PushNotification

* SuggestCommand

* PluginsResponse

* WebSocketMessage

* RemoteCluster

* SharedChannel

* PluginStatuses

* InitialLoad

* ClusterDiscovery

* ClusterStats

* MfaSecret

* GroupSyncable

* SAML

* WebSocketRequest

* TypingRequest

* SecurityBulletin

* OAuthApp

* IntegrationAction

* DataRetention

* Preference

* FileInfoList

* Compliance

* Preferences

* FileInfoSearchResults

* TermsOfService

* InstallMarketplacePluginRequest

* GitLabUser

* UploadSessions

* Remove unused helpers

* Fix tests

* Fix linting for ToJSON/FromJSON

* Fix conversions

Co-authored-by: Weblate (bot) <hosted@weblate.org>
Co-authored-by: JtheBAB <srast@bioc.uzh.ch>
Co-authored-by: Kaya Zeren <kayazeren@gmail.com>
Co-authored-by: Tóth Csaba // Online ERP Hungary Kft <csaba.toth@online-erp.hu>
Co-authored-by: Matthew Williams <Matthew.Williams@outlook.com.au>
Co-authored-by: Nikolai Zahariev <nikolaiz@yahoo.com>
Co-authored-by: kaakaa <stooner.hoe@gmail.com>
Co-authored-by: aeomin <lin@aeomin.net>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2021-09-01 14:43:12 +02:00

185 строки
5.8 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"bytes"
"encoding/json"
"io"
"net/http"
"regexp"
)
const (
DefaultWebhookUsername = "webhook"
)
type IncomingWebhook struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
UserId string `json:"user_id"`
ChannelId string `json:"channel_id"`
TeamId string `json:"team_id"`
DisplayName string `json:"display_name"`
Description string `json:"description"`
Username string `json:"username"`
IconURL string `json:"icon_url"`
ChannelLocked bool `json:"channel_locked"`
}
type IncomingWebhookRequest struct {
Text string `json:"text"`
Username string `json:"username"`
IconURL string `json:"icon_url"`
ChannelName string `json:"channel"`
Props StringInterface `json:"props"`
Attachments []*SlackAttachment `json:"attachments"`
Type string `json:"type"`
IconEmoji string `json:"icon_emoji"`
}
func (o *IncomingWebhook) IsValid() *AppError {
if !IsValidId(o.Id) {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.id.app_error", nil, "", http.StatusBadRequest)
}
if o.CreateAt == 0 {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.UpdateAt == 0 {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if !IsValidId(o.UserId) {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.user_id.app_error", nil, "", http.StatusBadRequest)
}
if !IsValidId(o.ChannelId) {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.channel_id.app_error", nil, "", http.StatusBadRequest)
}
if !IsValidId(o.TeamId) {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.DisplayName) > 64 {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "", http.StatusBadRequest)
}
if len(o.Description) > 500 {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "", http.StatusBadRequest)
}
if len(o.Username) > 64 {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.username.app_error", nil, "", http.StatusBadRequest)
}
if len(o.IconURL) > 1024 {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.icon_url.app_error", nil, "", http.StatusBadRequest)
}
return nil
}
func (o *IncomingWebhook) PreSave() {
if o.Id == "" {
o.Id = NewId()
}
o.CreateAt = GetMillis()
o.UpdateAt = o.CreateAt
}
func (o *IncomingWebhook) PreUpdate() {
o.UpdateAt = GetMillis()
}
// escapeControlCharsFromPayload escapes control chars (\n, \t) from a byte slice.
// Context:
// JSON strings are not supposed to contain control characters such as \n, \t,
// ... but some incoming webhooks might still send invalid JSON and we want to
// try to handle that. An example invalid JSON string from an incoming webhook
// might look like this (strings for both "text" and "fallback" attributes are
// invalid JSON strings because they contain unescaped newlines and tabs):
// `{
// "text": "this is a test
// that contains a newline and tabs",
// "attachments": [
// {
// "fallback": "Required plain-text summary of the attachment
// that contains a newline and tabs",
// "color": "#36a64f",
// ...
// "text": "Optional text that appears within the attachment
// that contains a newline and tabs",
// ...
// "thumb_url": "http://example.com/path/to/thumb.png"
// }
// ]
// }`
// This function will search for `"key": "value"` pairs, and escape \n, \t
// from the value.
func escapeControlCharsFromPayload(by []byte) []byte {
// we'll search for `"text": "..."` or `"fallback": "..."`, ...
keys := "text|fallback|pretext|author_name|title|value"
// the regexp reads like this:
// (?s): this flag let . match \n (default is false)
// "(keys)": we search for the keys defined above
// \s*:\s*: followed by 0..n spaces/tabs, a colon then 0..n spaces/tabs
// ": a double-quote
// (\\"|[^"])*: any number of times the `\"` string or any char but a double-quote
// ": a double-quote
r := `(?s)"(` + keys + `)"\s*:\s*"(\\"|[^"])*"`
re := regexp.MustCompile(r)
// the function that will escape \n and \t on the regexp matches
repl := func(b []byte) []byte {
if bytes.Contains(b, []byte("\n")) {
b = bytes.Replace(b, []byte("\n"), []byte("\\n"), -1)
}
if bytes.Contains(b, []byte("\t")) {
b = bytes.Replace(b, []byte("\t"), []byte("\\t"), -1)
}
return b
}
return re.ReplaceAllFunc(by, repl)
}
func decodeIncomingWebhookRequest(by []byte) (*IncomingWebhookRequest, error) {
decoder := json.NewDecoder(bytes.NewReader(by))
var o IncomingWebhookRequest
err := decoder.Decode(&o)
if err == nil {
return &o, nil
}
return nil, err
}
func IncomingWebhookRequestFromJSON(data io.Reader) (*IncomingWebhookRequest, *AppError) {
buf := new(bytes.Buffer)
buf.ReadFrom(data)
by := buf.Bytes()
// Try to decode the JSON data. Only if it fails, try to escape control
// characters from the strings contained in the JSON data.
o, err := decodeIncomingWebhookRequest(by)
if err != nil {
o, err = decodeIncomingWebhookRequest(escapeControlCharsFromPayload(by))
if err != nil {
return nil, NewAppError("IncomingWebhookRequestFromJSON", "model.incoming_hook.parse_data.app_error", nil, err.Error(), http.StatusBadRequest)
}
}
o.Attachments = StringifySlackFieldValue(o.Attachments)
return o, nil
}