1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2// See LICENSE.txt for license information.
3
4package model
5
6import (
7	"encoding/json"
8	"io"
9	"net/http"
10	"strings"
11)
12
13const (
14	COMMAND_METHOD_POST = "P"
15	COMMAND_METHOD_GET  = "G"
16	MIN_TRIGGER_LENGTH  = 1
17	MAX_TRIGGER_LENGTH  = 128
18)
19
20type Command struct {
21	Id               string `json:"id"`
22	Token            string `json:"token"`
23	CreateAt         int64  `json:"create_at"`
24	UpdateAt         int64  `json:"update_at"`
25	DeleteAt         int64  `json:"delete_at"`
26	CreatorId        string `json:"creator_id"`
27	TeamId           string `json:"team_id"`
28	Trigger          string `json:"trigger"`
29	Method           string `json:"method"`
30	Username         string `json:"username"`
31	IconURL          string `json:"icon_url"`
32	AutoComplete     bool   `json:"auto_complete"`
33	AutoCompleteDesc string `json:"auto_complete_desc"`
34	AutoCompleteHint string `json:"auto_complete_hint"`
35	DisplayName      string `json:"display_name"`
36	Description      string `json:"description"`
37	URL              string `json:"url"`
38	// PluginId records the id of the plugin that created this Command. If it is blank, the Command
39	// was not created by a plugin.
40	PluginId         string            `json:"plugin_id"`
41	AutocompleteData *AutocompleteData `db:"-" json:"autocomplete_data,omitempty"`
42	// AutocompleteIconData is a base64 encoded svg
43	AutocompleteIconData string `db:"-" json:"autocomplete_icon_data,omitempty"`
44}
45
46func (o *Command) ToJson() string {
47	b, _ := json.Marshal(o)
48	return string(b)
49}
50
51func CommandFromJson(data io.Reader) *Command {
52	var o *Command
53	json.NewDecoder(data).Decode(&o)
54	return o
55}
56
57func CommandListToJson(l []*Command) string {
58	b, _ := json.Marshal(l)
59	return string(b)
60}
61
62func CommandListFromJson(data io.Reader) []*Command {
63	var o []*Command
64	json.NewDecoder(data).Decode(&o)
65	return o
66}
67
68func (o *Command) IsValid() *AppError {
69
70	if !IsValidId(o.Id) {
71		return NewAppError("Command.IsValid", "model.command.is_valid.id.app_error", nil, "", http.StatusBadRequest)
72	}
73
74	if len(o.Token) != 26 {
75		return NewAppError("Command.IsValid", "model.command.is_valid.token.app_error", nil, "", http.StatusBadRequest)
76	}
77
78	if o.CreateAt == 0 {
79		return NewAppError("Command.IsValid", "model.command.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
80	}
81
82	if o.UpdateAt == 0 {
83		return NewAppError("Command.IsValid", "model.command.is_valid.update_at.app_error", nil, "", http.StatusBadRequest)
84	}
85
86	// If the CreatorId is blank, this should be a command created by a plugin.
87	if o.CreatorId == "" && !IsValidPluginId(o.PluginId) {
88		return NewAppError("Command.IsValid", "model.command.is_valid.plugin_id.app_error", nil, "", http.StatusBadRequest)
89	}
90
91	// If the PluginId is blank, this should be a command associated with a userId.
92	if o.PluginId == "" && !IsValidId(o.CreatorId) {
93		return NewAppError("Command.IsValid", "model.command.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
94	}
95
96	if o.CreatorId != "" && o.PluginId != "" {
97		return NewAppError("Command.IsValid", "model.command.is_valid.plugin_id.app_error", nil, "command cannot have both a CreatorId and a PluginId", http.StatusBadRequest)
98	}
99
100	if !IsValidId(o.TeamId) {
101		return NewAppError("Command.IsValid", "model.command.is_valid.team_id.app_error", nil, "", http.StatusBadRequest)
102	}
103
104	if len(o.Trigger) < MIN_TRIGGER_LENGTH || len(o.Trigger) > MAX_TRIGGER_LENGTH || strings.Index(o.Trigger, "/") == 0 || strings.Contains(o.Trigger, " ") {
105		return NewAppError("Command.IsValid", "model.command.is_valid.trigger.app_error", nil, "", http.StatusBadRequest)
106	}
107
108	if len(o.URL) == 0 || len(o.URL) > 1024 {
109		return NewAppError("Command.IsValid", "model.command.is_valid.url.app_error", nil, "", http.StatusBadRequest)
110	}
111
112	if !IsValidHttpUrl(o.URL) {
113		return NewAppError("Command.IsValid", "model.command.is_valid.url_http.app_error", nil, "", http.StatusBadRequest)
114	}
115
116	if !(o.Method == COMMAND_METHOD_GET || o.Method == COMMAND_METHOD_POST) {
117		return NewAppError("Command.IsValid", "model.command.is_valid.method.app_error", nil, "", http.StatusBadRequest)
118	}
119
120	if len(o.DisplayName) > 64 {
121		return NewAppError("Command.IsValid", "model.command.is_valid.display_name.app_error", nil, "", http.StatusBadRequest)
122	}
123
124	if len(o.Description) > 128 {
125		return NewAppError("Command.IsValid", "model.command.is_valid.description.app_error", nil, "", http.StatusBadRequest)
126	}
127
128	if o.AutocompleteData != nil {
129		if err := o.AutocompleteData.IsValid(); err != nil {
130			return NewAppError("Command.IsValid", "model.command.is_valid.autocomplete_data.app_error", nil, err.Error(), http.StatusBadRequest)
131		}
132	}
133
134	return nil
135}
136
137func (o *Command) PreSave() {
138	if o.Id == "" {
139		o.Id = NewId()
140	}
141
142	if o.Token == "" {
143		o.Token = NewId()
144	}
145
146	o.CreateAt = GetMillis()
147	o.UpdateAt = o.CreateAt
148}
149
150func (o *Command) PreUpdate() {
151	o.UpdateAt = GetMillis()
152}
153
154func (o *Command) Sanitize() {
155	o.Token = ""
156	o.CreatorId = ""
157	o.Method = ""
158	o.URL = ""
159	o.Username = ""
160	o.IconURL = ""
161}
162