summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost')
-rw-r--r--vendor/github.com/mattermost/mattermost-server/LICENSE.txt (renamed from vendor/github.com/mattermost/platform/LICENSE.txt)2
-rw-r--r--vendor/github.com/mattermost/mattermost-server/NOTICE.txt3721
-rw-r--r--vendor/github.com/mattermost/mattermost-server/mlog/default.go50
-rw-r--r--vendor/github.com/mattermost/mattermost-server/mlog/global.go44
-rw-r--r--vendor/github.com/mattermost/mattermost-server/mlog/log.go173
-rw-r--r--vendor/github.com/mattermost/mattermost-server/mlog/stdlog.go87
-rw-r--r--vendor/github.com/mattermost/mattermost-server/mlog/sugar.go28
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/access.go (renamed from vendor/github.com/mattermost/platform/model/access.go)38
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/analytics_row.go (renamed from vendor/github.com/mattermost/platform/model/analytics_row.go)28
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/audit.go (renamed from vendor/github.com/mattermost/platform/model/audit.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/audits.go (renamed from vendor/github.com/mattermost/platform/model/audits.go)9
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/authorize.go (renamed from vendor/github.com/mattermost/platform/model/authorize.go)45
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/builtin.go (renamed from vendor/github.com/mattermost/platform/model/builtin.go)0
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/bundle_info.go (renamed from vendor/github.com/mattermost/platform/model/bundle_info.go)9
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel.go (renamed from vendor/github.com/mattermost/platform/model/channel.go)100
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_count.go (renamed from vendor/github.com/mattermost/platform/model/channel_count.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_data.go (renamed from vendor/github.com/mattermost/platform/model/channel_data.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_list.go (renamed from vendor/github.com/mattermost/platform/model/channel_list.go)20
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_member.go (renamed from vendor/github.com/mattermost/platform/model/channel_member.go)73
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_member_history.go (renamed from vendor/github.com/mattermost/platform/model/channel_member_history.go)1
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_member_history_result.go15
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_mentions.go28
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_search.go (renamed from vendor/github.com/mattermost/platform/model/channel_search.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_stats.go (renamed from vendor/github.com/mattermost/platform/model/channel_stats.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/channel_view.go (renamed from vendor/github.com/mattermost/platform/model/channel_view.go)38
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/client4.go (renamed from vendor/github.com/mattermost/platform/model/client4.go)692
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/cluster_discovery.go (renamed from vendor/github.com/mattermost/platform/model/cluster_discovery.go)12
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/cluster_info.go (renamed from vendor/github.com/mattermost/platform/model/cluster_info.go)32
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/cluster_message.go (renamed from vendor/github.com/mattermost/platform/model/cluster_message.go)21
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/cluster_stats.go (renamed from vendor/github.com/mattermost/platform/model/cluster_stats.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/command.go (renamed from vendor/github.com/mattermost/platform/model/command.go)36
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/command_args.go (renamed from vendor/github.com/mattermost/platform/model/command_args.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/command_response.go (renamed from vendor/github.com/mattermost/platform/model/command_response.go)32
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/command_webhook.go (renamed from vendor/github.com/mattermost/platform/model/command_webhook.go)0
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/compliance.go (renamed from vendor/github.com/mattermost/platform/model/compliance.go)30
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/compliance_post.go (renamed from vendor/github.com/mattermost/platform/model/compliance_post.go)3
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/config.go (renamed from vendor/github.com/mattermost/platform/model/config.go)651
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/data_retention_policy.go (renamed from vendor/github.com/mattermost/platform/model/data_retention_policy.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/emoji.go (renamed from vendor/github.com/mattermost/platform/model/emoji.go)58
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/emoji_data.go6
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/emoji_search.go25
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/file.go (renamed from vendor/github.com/mattermost/platform/model/file.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/file_info.go (renamed from vendor/github.com/mattermost/platform/model/file_info.go)16
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/gitlab.go (renamed from vendor/github.com/mattermost/platform/model/gitlab.go)0
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/incoming_webhook.go (renamed from vendor/github.com/mattermost/platform/model/incoming_webhook.go)72
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/initial_load.go (renamed from vendor/github.com/mattermost/platform/model/initial_load.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/job.go (renamed from vendor/github.com/mattermost/platform/model/job.go)23
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/ldap.go (renamed from vendor/github.com/mattermost/platform/model/ldap.go)1
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/license.go (renamed from vendor/github.com/mattermost/platform/model/license.go)59
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/manifest.go (renamed from vendor/github.com/mattermost/platform/model/manifest.go)140
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/message_export.go27
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/mfa_secret.go (renamed from vendor/github.com/mattermost/platform/model/mfa_secret.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/migration.go8
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/oauth.go (renamed from vendor/github.com/mattermost/platform/model/oauth.go)59
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/outgoing_webhook.go (renamed from vendor/github.com/mattermost/platform/model/outgoing_webhook.go)88
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/permission.go (renamed from vendor/github.com/mattermost/platform/model/authorization.go)372
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/plugin_key_value.go (renamed from vendor/github.com/mattermost/platform/model/plugin_key_value.go)0
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/plugin_status.go42
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/plugins_response.go (renamed from vendor/github.com/mattermost/platform/model/plugins_response.go)20
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/post.go587
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/post_list.go (renamed from vendor/github.com/mattermost/platform/model/post_list.go)20
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/post_search_results.go40
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/preference.go (renamed from vendor/github.com/mattermost/platform/model/preference.go)24
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/preferences.go (renamed from vendor/github.com/mattermost/platform/model/preferences.go)8
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/push_notification.go (renamed from vendor/github.com/mattermost/platform/model/push_notification.go)21
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/push_response.go (renamed from vendor/github.com/mattermost/platform/model/push_response.go)7
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/reaction.go (renamed from vendor/github.com/mattermost/platform/model/reaction.go)16
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/role.go363
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/saml.go (renamed from vendor/github.com/mattermost/platform/model/saml.go)22
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/scheduled_task.go77
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/scheme.go208
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/search_params.go (renamed from vendor/github.com/mattermost/platform/model/search_params.go)105
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/security_bulletin.go (renamed from vendor/github.com/mattermost/platform/model/security_bulletin.go)28
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/session.go (renamed from vendor/github.com/mattermost/platform/model/session.go)59
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/slack_attachment.go (renamed from vendor/github.com/mattermost/platform/model/slack_attachment.go)25
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/status.go (renamed from vendor/github.com/mattermost/platform/model/status.go)56
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/suggest_command.go (renamed from vendor/github.com/mattermost/platform/model/suggest_command.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/switch_request.go (renamed from vendor/github.com/mattermost/platform/model/switch_request.go)21
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/system.go47
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/team.go (renamed from vendor/github.com/mattermost/platform/model/team.go)120
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/team_member.go (renamed from vendor/github.com/mattermost/platform/model/team_member.go)72
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/team_search.go (renamed from vendor/github.com/mattermost/platform/model/team_search.go)0
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/team_stats.go (renamed from vendor/github.com/mattermost/platform/model/team_stats.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/terms_of_service.go70
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/timezone.go628
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/token.go (renamed from vendor/github.com/mattermost/platform/model/token.go)0
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/user.go (renamed from vendor/github.com/mattermost/platform/model/user.go)226
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/user_access_token.go (renamed from vendor/github.com/mattermost/platform/model/user_access_token.go)36
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/user_access_token_search.go35
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/user_autocomplete.go (renamed from vendor/github.com/mattermost/platform/model/user_autocomplete.go)46
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/user_search.go (renamed from vendor/github.com/mattermost/platform/model/user_search.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/users_stats.go24
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/utils.go (renamed from vendor/github.com/mattermost/platform/model/utils.go)178
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/version.go (renamed from vendor/github.com/mattermost/platform/model/version.go)21
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/webrtc.go (renamed from vendor/github.com/mattermost/platform/model/webrtc.go)30
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/websocket_client.go (renamed from vendor/github.com/mattermost/platform/model/websocket_client.go)102
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/websocket_message.go165
-rw-r--r--vendor/github.com/mattermost/mattermost-server/model/websocket_request.go (renamed from vendor/github.com/mattermost/platform/model/websocket_request.go)19
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/jsonutils/json.go56
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/autolink.go253
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/block_quote.go62
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/blocks.go153
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/document.go22
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/fenced_code.go112
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/html.go192
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/html_entities.go2132
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/indented_code.go98
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go664
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/inspect.go78
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/lines.go27
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/links.go184
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/list.go220
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/markdown.go148
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/paragraph.go71
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/reference_definition.go75
-rw-r--r--vendor/github.com/mattermost/platform/NOTICE.txt2935
-rw-r--r--vendor/github.com/mattermost/platform/model/client.go2379
-rw-r--r--vendor/github.com/mattermost/platform/model/message_export.go18
-rw-r--r--vendor/github.com/mattermost/platform/model/post.go394
-rw-r--r--vendor/github.com/mattermost/platform/model/scheduled_task.go110
-rw-r--r--vendor/github.com/mattermost/platform/model/system.go42
-rw-r--r--vendor/github.com/mattermost/platform/model/websocket_message.go149
122 files changed, 13530 insertions, 7850 deletions
diff --git a/vendor/github.com/mattermost/platform/LICENSE.txt b/vendor/github.com/mattermost/mattermost-server/LICENSE.txt
index ead98cf0..76e4c455 100644
--- a/vendor/github.com/mattermost/platform/LICENSE.txt
+++ b/vendor/github.com/mattermost/mattermost-server/LICENSE.txt
@@ -12,7 +12,7 @@ You may be licensed to use source code to create compiled versions not produced
2. Under a commercial license available from Mattermost, Inc. by contacting commercial@mattermost.com
You are licensed to use the source code in Admin Tools and Configuration Files (templates/, config/, model/,
-webapp/client, webapp/fonts, webapp/i18n, webapp/images and all subdirectories thereof) under the Apache License v2.0.
+plugin/ and all subdirectories thereof) under the Apache License v2.0.
We promise that we will not enforce the copyleft provisions in AGPL v3.0 against you if your application (a) does not
link to the Mattermost Platform directly, but exclusively uses the Mattermost Admin Tools and Configuration Files, and
diff --git a/vendor/github.com/mattermost/mattermost-server/NOTICE.txt b/vendor/github.com/mattermost/mattermost-server/NOTICE.txt
new file mode 100644
index 00000000..55141290
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/NOTICE.txt
@@ -0,0 +1,3721 @@
+Mattermost Server
+© 2015-present Mattermost, Inc. All Rights Reserved. See LICENSE.txt for license information.
+
+NOTICES:
+--------
+
+This document includes a list of open source components used in Mattermost Server, including those that have been modified.
+
+---
+
+## NYTimes/gziphandler
+
+This product contains 'gziphandler' by The New York Times.
+
+Golang middleware to gzip HTTP responses
+
+* HOMEPAGE:
+ * https://github.com/NYTimes/gziphandler
+
+* LICENSE: Apache-2.0
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2016-2017 The New York Times Company
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+---
+
+## avct/uasurfer
+
+This product contains 'uasurfer' by Avocet.
+
+Go package for fast and reliable abstraction of browser user agent strings.
+
+* HOMEPAGE:
+ * https://github.com/avct/uasurfer
+
+* LICENSE: Apache-2.0
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ Copyright 2015 Avocet Systems Ltd.
+ http://avocet.io/opensource
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ Copyright 2015 Avocet Systems Ltd.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+---
+
+## dgryski/dgoogauth
+
+This product contains 'dgoogauth' by Damian Gryski.
+
+Google Authenticator for Go
+
+* HOMEPAGE:
+ * https://github.com/dgryski/dgoogauth
+
+* LICENSE: Apache-2.0
+
+Note: An original license file for this dependency is not available. We determined the type of license based on the official project website. The following text has been prepared using a template from the SPDX Workgroup (https://spdx.org) for this type of license.
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright 2018 Damian Gryski
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+---
+
+## disintegration/imaging
+
+This product contains 'imaging' by Grigory Dryapak.
+
+Imaging is a simple image processing package for Go
+
+* HOMEPAGE:
+ * https://github.com/disintegration/imaging
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2012-2018 Grigory Dryapak
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## dyatlov/go-opengraph
+
+This product contains 'go-opengraph' by Vitaly Dyatlov.
+
+Golang package for parsing OpenGraph data from HTML into regular structures
+
+* HOMEPAGE:
+ * https://github.com/dyatlov/go-opengraph
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Vitaly Dyatlov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## fsnotify/fsnotify
+
+This product contains 'fsnotify' by GitHub user "fsnotify".
+
+Cross-platform file system notifications for Go.
+
+* HOMEPAGE:
+ * https://github.com/fsnotify/fsnotify
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2012 The Go Authors. All rights reserved.
+Copyright (c) 2012 fsnotify Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## go-ldap/ldap
+
+This product contains 'ldap' by GitHub user "go-ldap".
+
+Basic LDAP v3 functionality for the GO programming language.
+
+* HOMEPAGE:
+ * https://github.com/go-ldap/ldap
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2011-2015 Michael Mitton (mmitton@gmail.com)
+Portions copyright (c) 2015-2016 go-ldap Authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## go-redis/redis
+
+This product contains 'redis' by GitHub user "go-redis".
+
+Type-safe Redis client for Golang
+
+* HOMEPAGE:
+ * https://github.com/go-redis/redis
+
+* LICENSE: BSD-2-Clause
+
+Copyright (c) 2013 The github.com/go-redis/redis Authors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## go-sql-driver/mysql
+
+This product contains 'mysql' by Go SQL Drivers.
+
+Go MySQL Driver is a MySQL driver for Go's (golang) database/sql package
+
+* HOMEPAGE:
+ * https://github.com/go-sql-driver/mysql
+
+* LICENSE: MPL-2.0
+
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
+
+---
+
+## golang/freetype
+
+This product contains 'freetype' by Go.
+
+The Freetype font rasterizer in the Go programming language.
+
+* HOMEPAGE:
+ * https://github.com/golang/freetype
+
+* LICENSE: (FTL OR GPL-2.0)
+
+Use of the Freetype-Go software is subject to your choice of exactly one of
+the following two licenses:
+ * The FreeType License, which is similar to the original BSD license with
+ an advertising clause, or
+ * The GNU General Public License (GPL), version 2 or later.
+
+The text of these licenses are available in the licenses/ftl.txt and the
+licenses/gpl.txt files respectively. They are also available at
+http://freetype.sourceforge.net/license.html
+
+The Luxi fonts in the testdata directory are licensed separately. See the
+testdata/COPYING file for details.
+
+---
+
+## gorilla/handlers
+
+This product contains 'handlers' by Gorilla web toolkit.
+
+A collection of useful handlers for Go's net/http package.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/handlers
+
+* LICENSE: BSD-2-Clause
+
+Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## gorilla/mux
+
+This product contains 'mux' by Gorilla web toolkit.
+
+A powerful URL router and dispatcher for golang.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/mux
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## gorilla/schema
+
+This product contains 'schema' by Gorilla web toolkit.
+
+Package gorilla/schema fills a struct with form values.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/schema
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## gorilla/websocket
+
+This product contains 'websocket' by Gorilla web toolkit.
+
+A WebSocket implementation for Go.
+
+* HOMEPAGE:
+ * https://github.com/gorilla/websocket
+
+* LICENSE: BSD-2-Clause
+
+Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## hako/durafmt
+
+This product contains 'durafmt' by Wesley Hill.
+
+:clock8: Better time duration formatting in Go!
+
+* HOMEPAGE:
+ * https://github.com/hako/durafmt
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Wesley Hill
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## hashicorp/go-hclog
+
+This product contains 'go-hclog' by HashiCorp.
+
+A common logging package for HashiCorp tools
+
+* HOMEPAGE:
+ * https://github.com/hashicorp/go-hclog
+
+* LICENSE: MIT
+
+MIT License
+
+Copyright (c) 2017 HashiCorp
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## hashicorp/go-plugin
+
+This product contains 'go-plugin' by HashiCorp.
+
+Golang plugin system over RPC.
+
+* HOMEPAGE:
+ * https://github.com/hashicorp/go-plugin
+
+* LICENSE: MPL-2.0
+
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. “Contributor”
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. “Contributor Version”
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor’s Contribution.
+
+1.3. “Contribution”
+
+ means Covered Software of a particular Contributor.
+
+1.4. “Covered Software”
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. “Incompatible With Secondary Licenses”
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of version
+ 1.1 or earlier of the License, but not also under the terms of a
+ Secondary License.
+
+1.6. “Executable Form”
+
+ means any form of the work other than Source Code Form.
+
+1.7. “Larger Work”
+
+ means a work that combines Covered Software with other material, in a separate
+ file or files, that is not Covered Software.
+
+1.8. “License”
+
+ means this document.
+
+1.9. “Licensable”
+
+ means having the right to grant, to the maximum extent possible, whether at the
+ time of the initial grant or subsequently, any and all of the rights conveyed by
+ this License.
+
+1.10. “Modifications”
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to, deletion
+ from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. “Patent Claims” of a Contributor
+
+ means any patent claim(s), including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by such Contributor that
+ would be infringed, but for the grant of the License, by the making,
+ using, selling, offering for sale, having made, import, or transfer of
+ either its Contributions or its Contributor Version.
+
+1.12. “Secondary License”
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. “Source Code Form”
+
+ means the form of the work preferred for making modifications.
+
+1.14. “You” (or “Your”)
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, “You” includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, “control” means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or as
+ part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its Contributions
+ or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution become
+ effective for each Contribution on the date the Contributor first distributes
+ such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under this
+ License. No additional rights or licenses will be implied from the distribution
+ or licensing of Covered Software under this License. Notwithstanding Section
+ 2.1(b) above, no patent license is granted by a Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party’s
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of its
+ Contributions.
+
+ This License does not grant any rights in the trademarks, service marks, or
+ logos of any Contributor (except as may be necessary to comply with the
+ notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this License
+ (see Section 10.2) or under the terms of a Secondary License (if permitted
+ under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its Contributions
+ are its original creation(s) or it has sufficient rights to grant the
+ rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under applicable
+ copyright doctrines of fair use, fair dealing, or other equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under the
+ terms of this License. You must inform recipients that the Source Code Form
+ of the Covered Software is governed by the terms of this License, and how
+ they can obtain a copy of this License. You may not attempt to alter or
+ restrict the recipients’ rights in the Source Code Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this License,
+ or sublicense it under different terms, provided that the license for
+ the Executable Form does not attempt to limit or alter the recipients’
+ rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for the
+ Covered Software. If the Larger Work is a combination of Covered Software
+ with a work governed by one or more Secondary Licenses, and the Covered
+ Software is not Incompatible With Secondary Licenses, this License permits
+ You to additionally distribute such Covered Software under the terms of
+ such Secondary License(s), so that the recipient of the Larger Work may, at
+ their option, further distribute the Covered Software under the terms of
+ either this License or such Secondary License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices (including
+ copyright notices, patent notices, disclaimers of warranty, or limitations
+ of liability) contained within the Source Code Form of the Covered
+ Software, except that You may alter any license notices to the extent
+ required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on behalf
+ of any Contributor. You must make it absolutely clear that any such
+ warranty, support, indemnity, or liability obligation is offered by You
+ alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute, judicial
+ order, or regulation then You must: (a) comply with the terms of this License
+ to the maximum extent possible; and (b) describe the limitations and the code
+ they affect. Such description must be placed in a text file included with all
+ distributions of the Covered Software under this License. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing basis,
+ if such Contributor fails to notify You of the non-compliance by some
+ reasonable means prior to 60 days after You have come back into compliance.
+ Moreover, Your grants from a particular Contributor are reinstated on an
+ ongoing basis if such Contributor notifies You of the non-compliance by
+ some reasonable means, this is the first time You have received notice of
+ non-compliance with this License from such Contributor, and You become
+ compliant prior to 30 days after Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions, counter-claims,
+ and cross-claims) alleging that a Contributor Version directly or
+ indirectly infringes any patent, then the rights granted to You by any and
+ all Contributors for the Covered Software under Section 2.1 of this License
+ shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an “as is” basis, without
+ warranty of any kind, either expressed, implied, or statutory, including,
+ without limitation, warranties that the Covered Software is free of defects,
+ merchantable, fit for a particular purpose or non-infringing. The entire
+ risk as to the quality and performance of the Covered Software is with You.
+ Should any Covered Software prove defective in any respect, You (not any
+ Contributor) assume the cost of any necessary servicing, repair, or
+ correction. This disclaimer of warranty constitutes an essential part of this
+ License. No use of any Covered Software is authorized under this License
+ except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from such
+ party’s negligence to the extent applicable law prohibits such limitation.
+ Some jurisdictions do not allow the exclusion or limitation of incidental or
+ consequential damages, so this exclusion and limitation may not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts of
+ a jurisdiction where the defendant maintains its principal place of business
+ and such litigation shall be governed by laws of that jurisdiction, without
+ reference to its conflict-of-law provisions. Nothing in this Section shall
+ prevent a party’s ability to bring cross-claims or counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject matter
+ hereof. If any provision of this License is held to be unenforceable, such
+ provision shall be reformed only to the extent necessary to make it
+ enforceable. Any law or regulation which provides that the language of a
+ contract shall be construed against the drafter shall not be used to construe
+ this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version of
+ the License under which You originally received the Covered Software, or
+ under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a modified
+ version of this License if you rename the license and remove any
+ references to the name of the license steward (except to note that such
+ modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
+ If You choose to distribute Source Code Form that is Incompatible With
+ Secondary Licenses under the terms of this version of the License, the
+ notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+
+ This Source Code Form is subject to the
+ terms of the Mozilla Public License, v.
+ 2.0. If a copy of the MPL was not
+ distributed with this file, You can
+ obtain one at
+ http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file, then
+You may include the notice in a location (such as a LICENSE file in a relevant
+directory) where a recipient would be likely to look for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - “Incompatible With Secondary Licenses” Notice
+
+ This Source Code Form is “Incompatible
+ With Secondary Licenses”, as defined by
+ the Mozilla Public License, v. 2.0.
+
+---
+
+## hashicorp/memberlist
+
+This product contains 'memberlist' by HashiCorp.
+
+Golang package for gossip based membership and failure detection
+
+* HOMEPAGE:
+ * https://github.com/hashicorp/memberlist
+
+* LICENSE: MPL-2.0
+
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. “Contributor”
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. “Contributor Version”
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor’s Contribution.
+
+1.3. “Contribution”
+
+ means Covered Software of a particular Contributor.
+
+1.4. “Covered Software”
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. “Incompatible With Secondary Licenses”
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of version
+ 1.1 or earlier of the License, but not also under the terms of a
+ Secondary License.
+
+1.6. “Executable Form”
+
+ means any form of the work other than Source Code Form.
+
+1.7. “Larger Work”
+
+ means a work that combines Covered Software with other material, in a separate
+ file or files, that is not Covered Software.
+
+1.8. “License”
+
+ means this document.
+
+1.9. “Licensable”
+
+ means having the right to grant, to the maximum extent possible, whether at the
+ time of the initial grant or subsequently, any and all of the rights conveyed by
+ this License.
+
+1.10. “Modifications”
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to, deletion
+ from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. “Patent Claims” of a Contributor
+
+ means any patent claim(s), including without limitation, method, process,
+ and apparatus claims, in any patent Licensable by such Contributor that
+ would be infringed, but for the grant of the License, by the making,
+ using, selling, offering for sale, having made, import, or transfer of
+ either its Contributions or its Contributor Version.
+
+1.12. “Secondary License”
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. “Source Code Form”
+
+ means the form of the work preferred for making modifications.
+
+1.14. “You” (or “Your”)
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, “You” includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, “control” means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or as
+ part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its Contributions
+ or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution become
+ effective for each Contribution on the date the Contributor first distributes
+ such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under this
+ License. No additional rights or licenses will be implied from the distribution
+ or licensing of Covered Software under this License. Notwithstanding Section
+ 2.1(b) above, no patent license is granted by a Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party’s
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of its
+ Contributions.
+
+ This License does not grant any rights in the trademarks, service marks, or
+ logos of any Contributor (except as may be necessary to comply with the
+ notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this License
+ (see Section 10.2) or under the terms of a Secondary License (if permitted
+ under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its Contributions
+ are its original creation(s) or it has sufficient rights to grant the
+ rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under applicable
+ copyright doctrines of fair use, fair dealing, or other equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under the
+ terms of this License. You must inform recipients that the Source Code Form
+ of the Covered Software is governed by the terms of this License, and how
+ they can obtain a copy of this License. You may not attempt to alter or
+ restrict the recipients’ rights in the Source Code Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this License,
+ or sublicense it under different terms, provided that the license for
+ the Executable Form does not attempt to limit or alter the recipients’
+ rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for the
+ Covered Software. If the Larger Work is a combination of Covered Software
+ with a work governed by one or more Secondary Licenses, and the Covered
+ Software is not Incompatible With Secondary Licenses, this License permits
+ You to additionally distribute such Covered Software under the terms of
+ such Secondary License(s), so that the recipient of the Larger Work may, at
+ their option, further distribute the Covered Software under the terms of
+ either this License or such Secondary License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices (including
+ copyright notices, patent notices, disclaimers of warranty, or limitations
+ of liability) contained within the Source Code Form of the Covered
+ Software, except that You may alter any license notices to the extent
+ required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on behalf
+ of any Contributor. You must make it absolutely clear that any such
+ warranty, support, indemnity, or liability obligation is offered by You
+ alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute, judicial
+ order, or regulation then You must: (a) comply with the terms of this License
+ to the maximum extent possible; and (b) describe the limitations and the code
+ they affect. Such description must be placed in a text file included with all
+ distributions of the Covered Software under this License. Except to the
+ extent prohibited by statute or regulation, such description must be
+ sufficiently detailed for a recipient of ordinary skill to be able to
+ understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing basis,
+ if such Contributor fails to notify You of the non-compliance by some
+ reasonable means prior to 60 days after You have come back into compliance.
+ Moreover, Your grants from a particular Contributor are reinstated on an
+ ongoing basis if such Contributor notifies You of the non-compliance by
+ some reasonable means, this is the first time You have received notice of
+ non-compliance with this License from such Contributor, and You become
+ compliant prior to 30 days after Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions, counter-claims,
+ and cross-claims) alleging that a Contributor Version directly or
+ indirectly infringes any patent, then the rights granted to You by any and
+ all Contributors for the Covered Software under Section 2.1 of this License
+ shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an “as is” basis, without
+ warranty of any kind, either expressed, implied, or statutory, including,
+ without limitation, warranties that the Covered Software is free of defects,
+ merchantable, fit for a particular purpose or non-infringing. The entire
+ risk as to the quality and performance of the Covered Software is with You.
+ Should any Covered Software prove defective in any respect, You (not any
+ Contributor) assume the cost of any necessary servicing, repair, or
+ correction. This disclaimer of warranty constitutes an essential part of this
+ License. No use of any Covered Software is authorized under this License
+ except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from such
+ party’s negligence to the extent applicable law prohibits such limitation.
+ Some jurisdictions do not allow the exclusion or limitation of incidental or
+ consequential damages, so this exclusion and limitation may not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts of
+ a jurisdiction where the defendant maintains its principal place of business
+ and such litigation shall be governed by laws of that jurisdiction, without
+ reference to its conflict-of-law provisions. Nothing in this Section shall
+ prevent a party’s ability to bring cross-claims or counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject matter
+ hereof. If any provision of this License is held to be unenforceable, such
+ provision shall be reformed only to the extent necessary to make it
+ enforceable. Any law or regulation which provides that the language of a
+ contract shall be construed against the drafter shall not be used to construe
+ this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version of
+ the License under which You originally received the Covered Software, or
+ under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a modified
+ version of this License if you rename the license and remove any
+ references to the name of the license steward (except to note that such
+ modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
+ If You choose to distribute Source Code Form that is Incompatible With
+ Secondary Licenses under the terms of this version of the License, the
+ notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+
+ This Source Code Form is subject to the
+ terms of the Mozilla Public License, v.
+ 2.0. If a copy of the MPL was not
+ distributed with this file, You can
+ obtain one at
+ http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file, then
+You may include the notice in a location (such as a LICENSE file in a relevant
+directory) where a recipient would be likely to look for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - “Incompatible With Secondary Licenses” Notice
+
+ This Source Code Form is “Incompatible
+ With Secondary Licenses”, as defined by
+ the Mozilla Public License, v. 2.0.
+
+---
+
+## icrowley/fake
+
+This product contains 'fake' by GitHub user "icrowley".
+
+Fake data generator for Go (Golang)
+
+* HOMEPAGE:
+ * https://github.com/icrowley/fake
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Dmitry Afanasyev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## jaytaylor/html2text
+
+This product contains 'html2text' by J. Elliot Taylor.
+
+Golang HTML to plaintext conversion library
+
+* HOMEPAGE:
+ * https://github.com/jaytaylor/html2text
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Jay Taylor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## lib/pq
+
+This product contains 'pq' by GitHub user "lib".
+
+Pure Go Postgres driver for database/sql
+
+* HOMEPAGE:
+ * https://github.com/lib/pq
+
+* LICENSE: MIT
+
+Copyright (c) 2011-2013, 'pq' Contributors
+Portions Copyright (C) 2011 Blake Mizerany
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+---
+
+## mattermost/gorp
+
+This product contains 'gorp' by Mattermost (forked from original GitHub repo 'go-gorp/gorp' owned by GitHub user "go-gorp").
+
+Go Relational Persistence - an ORM-ish library for Go
+
+* HOMEPAGE:
+ * https://github.com/mattermost/gorp
+
+* LICENSE: MIT
+
+(The MIT License)
+
+Copyright (c) 2012 James Cooper <james@bitmechanic.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+---
+
+## mattermost/rsc
+
+This product contains 'rsc' by Mattermost (forked from original GitHub repo 'petar/rsc' owned by Petar Maymounkov).
+
+fork of Russ Cox's code.google.com/p/rsc
+
+* HOMEPAGE:
+ * https://github.com/mattermost/rsc
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## mattermost/viper
+
+This product contains 'viper' by Mattermost (forked from original GitHub repo 'spf13/viper' owned by Steve Francia).
+
+Go configuration with fangs
+
+* HOMEPAGE:
+ * https://github.com/mattermost/viper
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Steve Francia
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## minio/minio-go
+
+This product contains 'minio-go' by Minio Cloud Storage.
+
+Minio Client SDK for Go
+
+* HOMEPAGE:
+ * https://github.com/minio/minio-go
+
+* LICENSE: Apache-2.0
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+* This package includes the following NOTICE:
+
+minio-go
+Copyright 2015-2017 Minio, Inc.
+
+---
+
+## nicksnyder/go-i18n
+
+This product contains 'go-i18n' by Nick Snyder.
+
+Translate your Go program into multiple languages.
+
+* HOMEPAGE:
+ * https://github.com/nicksnyder/go-i18n
+
+* LICENSE: MIT
+
+Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+---
+
+## pborman/uuid
+
+This product contains 'uuid' by GitHub user "pborman".
+
+Automatically exported from code.google.com/p/go-uuid
+
+* HOMEPAGE:
+ * https://github.com/pborman/uuid
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2009,2014 Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## pkg/errors
+
+This product contains 'errors' by GitHub user "pkg".
+
+Simple error handling primitives
+
+* HOMEPAGE:
+ * https://github.com/pkg/errors
+
+* LICENSE: BSD-2-Clause
+
+Copyright (c) 2015, Dave Cheney <dave@cheney.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## prometheus/client_golang
+
+This product contains 'client_golang' by Prometheus.
+
+Prometheus instrumentation library for Go applications
+
+* HOMEPAGE:
+ * https://github.com/prometheus/client_golang
+
+* LICENSE: Apache-2.0
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+* This package includes the following NOTICE:
+
+Prometheus instrumentation library for Go applications
+Copyright 2012-2015 The Prometheus Authors
+
+This product includes software developed at
+SoundCloud Ltd. (http://soundcloud.com/).
+
+
+The following components are included in this product:
+
+perks - a fork of https://github.com/bmizerany/perks
+https://github.com/beorn7/perks
+Copyright 2013-2015 Blake Mizerany, Björn Rabenstein
+See https://github.com/beorn7/perks/blob/master/README.md for license details.
+
+Go support for Protocol Buffers - Google's data interchange format
+http://github.com/golang/protobuf/
+Copyright 2010 The Go Authors
+See source code for license details.
+
+Support for streaming Protocol Buffer messages for the Go language (golang).
+https://github.com/matttproud/golang_protobuf_extensions
+Copyright 2013 Matt T. Proud
+Licensed under the Apache License, Version 2.0
+
+---
+
+## rs/cors
+
+This product contains 'cors' by Olivier Poitrey.
+
+Go net/http configurable handler to handle CORS requests
+
+* HOMEPAGE:
+ * https://github.com/rs/cors
+
+* LICENSE: MIT
+
+Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+---
+
+## rwcarlsen/goexif
+
+This product contains 'goexif' by Robert Carlsen.
+
+Decode embedded EXIF meta data from image files.
+
+* HOMEPAGE:
+ * https://github.com/rwcarlsen/goexif
+
+* LICENSE: BSD-2-Clause
+
+
+Copyright (c) 2012, Robert Carlsen & Contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## segmentio/analytics-go
+
+This product contains 'analytics-go' by Segment.
+
+Segment analytics client for Go
+
+* HOMEPAGE:
+ * https://github.com/segmentio/analytics-go
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Segment, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+---
+
+## spf13/cobra
+
+This product contains 'cobra' by Steve Francia.
+
+A Commander for modern Go CLI interactions
+
+* HOMEPAGE:
+ * https://github.com/spf13/cobra
+
+* LICENSE: Apache-2.0
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+---
+
+## stretchr/testify
+
+This product contains 'testify' by Stretchr, Inc..
+
+A toolkit with common assertions and mocks that plays nicely with the standard library
+
+* HOMEPAGE:
+ * https://github.com/stretchr/testify
+
+* LICENSE: MIT
+
+Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
+
+Please consider promoting this project if you find it useful.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+---
+
+## throttled/throttled
+
+This product contains 'throttled' by GitHub user "throttled".
+
+Package throttled implements rate limiting access to resources such as HTTP endpoints.
+
+* HOMEPAGE:
+ * https://github.com/throttled/throttled
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2018, Martin Angers and Contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## tylerb/graceful
+
+This product contains 'graceful' by Tyler Stillwater.
+
+Graceful is a Go package enabling graceful shutdown of an http.Handler server.
+
+* HOMEPAGE:
+ * https://github.com/tylerb/graceful
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Tyler Bunnell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## zap
+
+This product contains 'zap' by Uber Technologies, Inc..
+
+Blazing fast, structured, leveled logging in Go.
+
+* HOMEPAGE:
+ * https://github.com/uber-go/zap
+
+* LICENSE: MIT
+
+Copyright (c) 2016-2017 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+---
+
+## x/crypto
+
+This product contains 'crypto' by The Go Authors.
+
+[mirror] Go supplementary cryptography libraries
+
+* HOMEPAGE:
+ * https://github.com/golang/crypto
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## x/image
+
+This product contains 'image' by The Go Authors.
+
+[mirror] Go supplementary image libraries
+
+* HOMEPAGE:
+ * https://github.com/golang/image
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## x/net
+
+This product contains 'net' by The Go Authors.
+
+[mirror] Go supplementary network libraries
+
+* HOMEPAGE:
+ * https://github.com/golang/net
+
+* LICENSE: BSD-3-Clause
+
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+## go-gomail/gomail
+
+This product contains 'gomail' by Gomail.
+
+The best way to send emails in Go.
+
+* HOMEPAGE:
+ * https://github.com/go-gomail/gomail
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Alexandre Cesaro
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+---
+
+## natefinch/lumberjack
+
+This product contains 'lumberjack' by Nate Finch.
+
+lumberjack is a log rolling package for Go
+
+* HOMEPAGE:
+ * https://github.com/natefinch/lumberjack
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Nate Finch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+---
+
+## olivere/elastic
+
+This product contains 'elastic' by Oliver Eilhard.
+
+Elasticsearch client for Go.
+
+* HOMEPAGE:
+ * https://github.com/olivere/elastic
+
+* LICENSE: MIT
+
+The MIT License (MIT)
+Copyright © 2012-2015 Oliver Eilhard
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
+---
+
+## go-yaml/yaml
+
+This product contains 'yaml' by GitHub user "go-yaml".
+
+YAML support for the Go language.
+
+* HOMEPAGE:
+ * https://github.com/go-yaml/yaml
+
+* LICENSE: Apache-2.0
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+* This package includes the following NOTICE:
+
+Copyright 2011-2016 Canonical Ltd.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/github.com/mattermost/mattermost-server/mlog/default.go b/vendor/github.com/mattermost/mattermost-server/mlog/default.go
new file mode 100644
index 00000000..366d22f8
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/mlog/default.go
@@ -0,0 +1,50 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package mlog
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+// defaultLog manually encodes the log to STDOUT, providing a basic, default logging implementation
+// before mlog is fully configured.
+func defaultLog(level, msg string, fields ...Field) {
+ log := struct {
+ Level string `json:"level"`
+ Message string `json:"msg"`
+ Fields []Field `json:"fields,omitempty"`
+ }{
+ level,
+ msg,
+ fields,
+ }
+
+ if b, err := json.Marshal(log); err != nil {
+ fmt.Printf(`{"level":"error","msg":"failed to encode log message"}%s`, "\n")
+ } else {
+ fmt.Printf("%s\n", b)
+ }
+}
+
+func defaultDebugLog(msg string, fields ...Field) {
+ defaultLog("debug", msg, fields...)
+}
+
+func defaultInfoLog(msg string, fields ...Field) {
+ defaultLog("info", msg, fields...)
+}
+
+func defaultWarnLog(msg string, fields ...Field) {
+ defaultLog("warn", msg, fields...)
+}
+
+func defaultErrorLog(msg string, fields ...Field) {
+ defaultLog("error", msg, fields...)
+}
+
+func defaultCriticalLog(msg string, fields ...Field) {
+ // We map critical to error in zap, so be consistent.
+ defaultLog("error", msg, fields...)
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/mlog/global.go b/vendor/github.com/mattermost/mattermost-server/mlog/global.go
new file mode 100644
index 00000000..ba90ace2
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/mlog/global.go
@@ -0,0 +1,44 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package mlog
+
+import (
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+var globalLogger *Logger
+
+func InitGlobalLogger(logger *Logger) {
+ glob := *logger
+ glob.zap = glob.zap.WithOptions(zap.AddCallerSkip(1))
+ globalLogger = &glob
+ Debug = globalLogger.Debug
+ Info = globalLogger.Info
+ Warn = globalLogger.Warn
+ Error = globalLogger.Error
+ Critical = globalLogger.Critical
+}
+
+func RedirectStdLog(logger *Logger) {
+ zap.RedirectStdLogAt(logger.zap.With(zap.String("source", "stdlog")).WithOptions(zap.AddCallerSkip(-2)), zapcore.ErrorLevel)
+}
+
+type LogFunc func(string, ...Field)
+
+// DON'T USE THIS Modify the level on the app logger
+func GloballyDisableDebugLogForTest() {
+ globalLogger.consoleLevel.SetLevel(zapcore.ErrorLevel)
+}
+
+// DON'T USE THIS Modify the level on the app logger
+func GloballyEnableDebugLogForTest() {
+ globalLogger.consoleLevel.SetLevel(zapcore.DebugLevel)
+}
+
+var Debug LogFunc = defaultDebugLog
+var Info LogFunc = defaultInfoLog
+var Warn LogFunc = defaultWarnLog
+var Error LogFunc = defaultErrorLog
+var Critical LogFunc = defaultCriticalLog
diff --git a/vendor/github.com/mattermost/mattermost-server/mlog/log.go b/vendor/github.com/mattermost/mattermost-server/mlog/log.go
new file mode 100644
index 00000000..e3bc38d8
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/mlog/log.go
@@ -0,0 +1,173 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package mlog
+
+import (
+ "io"
+ "log"
+ "os"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+ "gopkg.in/natefinch/lumberjack.v2"
+)
+
+const (
+ // Very verbose messages for debugging specific issues
+ LevelDebug = "debug"
+ // Default log level, informational
+ LevelInfo = "info"
+ // Warnings are messages about possible issues
+ LevelWarn = "warn"
+ // Errors are messages about things we know are problems
+ LevelError = "error"
+)
+
+// Type and function aliases from zap to limit the libraries scope into MM code
+type Field = zapcore.Field
+
+var Int64 = zap.Int64
+var Int = zap.Int
+var Uint32 = zap.Uint32
+var String = zap.String
+var Any = zap.Any
+var Err = zap.Error
+var Bool = zap.Bool
+
+type LoggerConfiguration struct {
+ EnableConsole bool
+ ConsoleJson bool
+ ConsoleLevel string
+ EnableFile bool
+ FileJson bool
+ FileLevel string
+ FileLocation string
+}
+
+type Logger struct {
+ zap *zap.Logger
+ consoleLevel zap.AtomicLevel
+ fileLevel zap.AtomicLevel
+}
+
+func getZapLevel(level string) zapcore.Level {
+ switch level {
+ case LevelInfo:
+ return zapcore.InfoLevel
+ case LevelWarn:
+ return zapcore.WarnLevel
+ case LevelDebug:
+ return zapcore.DebugLevel
+ case LevelError:
+ return zapcore.ErrorLevel
+ default:
+ return zapcore.InfoLevel
+ }
+}
+
+func makeEncoder(json bool) zapcore.Encoder {
+ encoderConfig := zap.NewProductionEncoderConfig()
+ if json {
+ return zapcore.NewJSONEncoder(encoderConfig)
+ }
+
+ encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
+ return zapcore.NewConsoleEncoder(encoderConfig)
+}
+
+func NewLogger(config *LoggerConfiguration) *Logger {
+ cores := []zapcore.Core{}
+ logger := &Logger{
+ consoleLevel: zap.NewAtomicLevelAt(getZapLevel(config.ConsoleLevel)),
+ fileLevel: zap.NewAtomicLevelAt(getZapLevel(config.FileLevel)),
+ }
+
+ if config.EnableConsole {
+ writer := zapcore.Lock(os.Stdout)
+ core := zapcore.NewCore(makeEncoder(config.ConsoleJson), writer, logger.consoleLevel)
+ cores = append(cores, core)
+ }
+
+ if config.EnableFile {
+ writer := zapcore.AddSync(&lumberjack.Logger{
+ Filename: config.FileLocation,
+ MaxSize: 100,
+ Compress: true,
+ })
+ core := zapcore.NewCore(makeEncoder(config.FileJson), writer, logger.fileLevel)
+ cores = append(cores, core)
+ }
+
+ combinedCore := zapcore.NewTee(cores...)
+
+ logger.zap = zap.New(combinedCore,
+ zap.AddCallerSkip(1),
+ zap.AddCaller(),
+ )
+
+ return logger
+}
+
+func (l *Logger) ChangeLevels(config *LoggerConfiguration) {
+ l.consoleLevel.SetLevel(getZapLevel(config.ConsoleLevel))
+ l.fileLevel.SetLevel(getZapLevel(config.FileLevel))
+}
+
+func (l *Logger) SetConsoleLevel(level string) {
+ l.consoleLevel.SetLevel(getZapLevel(level))
+}
+
+func (l *Logger) With(fields ...Field) *Logger {
+ newlogger := *l
+ newlogger.zap = newlogger.zap.With(fields...)
+ return &newlogger
+}
+
+func (l *Logger) StdLog(fields ...Field) *log.Logger {
+ return zap.NewStdLog(l.With(fields...).zap.WithOptions(getStdLogOption()))
+}
+
+// StdLogWriter returns a writer that can be hooked up to the output of a golang standard logger
+// anything written will be interpreted as log entries accordingly
+func (l *Logger) StdLogWriter() io.Writer {
+ newLogger := *l
+ newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(4), getStdLogOption())
+ f := newLogger.Info
+ return &loggerWriter{f}
+}
+
+func (l *Logger) WithCallerSkip(skip int) *Logger {
+ newlogger := *l
+ newlogger.zap = newlogger.zap.WithOptions(zap.AddCallerSkip(skip))
+ return &newlogger
+}
+
+// Made for the plugin interface, wraps mlog in a simpler interface
+// at the cost of performance
+func (l *Logger) Sugar() *SugarLogger {
+ return &SugarLogger{
+ wrappedLogger: l,
+ zapSugar: l.zap.Sugar(),
+ }
+}
+
+func (l *Logger) Debug(message string, fields ...Field) {
+ l.zap.Debug(message, fields...)
+}
+
+func (l *Logger) Info(message string, fields ...Field) {
+ l.zap.Info(message, fields...)
+}
+
+func (l *Logger) Warn(message string, fields ...Field) {
+ l.zap.Warn(message, fields...)
+}
+
+func (l *Logger) Error(message string, fields ...Field) {
+ l.zap.Error(message, fields...)
+}
+
+func (l *Logger) Critical(message string, fields ...Field) {
+ l.zap.Error(message, fields...)
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/mlog/stdlog.go b/vendor/github.com/mattermost/mattermost-server/mlog/stdlog.go
new file mode 100644
index 00000000..7839ddfa
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/mlog/stdlog.go
@@ -0,0 +1,87 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package mlog
+
+import (
+ "bytes"
+ "strings"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+// Implementation of zapcore.Core to interpret log messages from a standard logger
+// and translate the levels to zapcore levels.
+type stdLogLevelInterpreterCore struct {
+ wrappedCore zapcore.Core
+}
+
+func stdLogInterpretZapEntry(entry zapcore.Entry) zapcore.Entry {
+ message := entry.Message
+ if strings.Index(message, "[DEBUG]") == 0 {
+ entry.Level = zapcore.DebugLevel
+ entry.Message = message[7:]
+ } else if strings.Index(message, "[DEBG]") == 0 {
+ entry.Level = zapcore.DebugLevel
+ entry.Message = message[6:]
+ } else if strings.Index(message, "[WARN]") == 0 {
+ entry.Level = zapcore.WarnLevel
+ entry.Message = message[6:]
+ } else if strings.Index(message, "[ERROR]") == 0 {
+ entry.Level = zapcore.ErrorLevel
+ entry.Message = message[7:]
+ } else if strings.Index(message, "[EROR]") == 0 {
+ entry.Level = zapcore.ErrorLevel
+ entry.Message = message[6:]
+ } else if strings.Index(message, "[ERR]") == 0 {
+ entry.Level = zapcore.ErrorLevel
+ entry.Message = message[5:]
+ } else if strings.Index(message, "[INFO]") == 0 {
+ entry.Level = zapcore.InfoLevel
+ entry.Message = message[6:]
+ }
+ return entry
+}
+
+func (s *stdLogLevelInterpreterCore) Enabled(lvl zapcore.Level) bool {
+ return s.wrappedCore.Enabled(lvl)
+}
+
+func (s *stdLogLevelInterpreterCore) With(fields []zapcore.Field) zapcore.Core {
+ return s.wrappedCore.With(fields)
+}
+
+func (s *stdLogLevelInterpreterCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry {
+ entry = stdLogInterpretZapEntry(entry)
+ return s.wrappedCore.Check(entry, checkedEntry)
+}
+
+func (s *stdLogLevelInterpreterCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
+ entry = stdLogInterpretZapEntry(entry)
+ return s.wrappedCore.Write(entry, fields)
+}
+
+func (s *stdLogLevelInterpreterCore) Sync() error {
+ return s.wrappedCore.Sync()
+}
+
+func getStdLogOption() zap.Option {
+ return zap.WrapCore(
+ func(core zapcore.Core) zapcore.Core {
+ return &stdLogLevelInterpreterCore{core}
+ },
+ )
+}
+
+type loggerWriter struct {
+ logFunc func(msg string, fields ...Field)
+}
+
+func (l *loggerWriter) Write(p []byte) (int, error) {
+ trimmed := string(bytes.TrimSpace(p))
+ for _, line := range strings.Split(trimmed, "\n") {
+ l.logFunc(string(line))
+ }
+ return len(p), nil
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/mlog/sugar.go b/vendor/github.com/mattermost/mattermost-server/mlog/sugar.go
new file mode 100644
index 00000000..c00a8bbf
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/mlog/sugar.go
@@ -0,0 +1,28 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+package mlog
+
+import "go.uber.org/zap"
+
+// Made for the plugin interface, use the regular logger for other uses
+type SugarLogger struct {
+ wrappedLogger *Logger
+ zapSugar *zap.SugaredLogger
+}
+
+func (l *SugarLogger) Debug(msg string, keyValuePairs ...interface{}) {
+ l.zapSugar.Debugw(msg, keyValuePairs...)
+}
+
+func (l *SugarLogger) Info(msg string, keyValuePairs ...interface{}) {
+ l.zapSugar.Infow(msg, keyValuePairs...)
+}
+
+func (l *SugarLogger) Error(msg string, keyValuePairs ...interface{}) {
+ l.zapSugar.Errorw(msg, keyValuePairs...)
+}
+
+func (l *SugarLogger) Warn(msg string, keyValuePairs ...interface{}) {
+ l.zapSugar.Warnw(msg, keyValuePairs...)
+}
diff --git a/vendor/github.com/mattermost/platform/model/access.go b/vendor/github.com/mattermost/mattermost-server/model/access.go
index 551ef930..e9603c78 100644
--- a/vendor/github.com/mattermost/platform/model/access.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/access.go
@@ -74,41 +74,23 @@ func (me *AccessData) IsExpired() bool {
}
func (ad *AccessData) ToJson() string {
- b, err := json.Marshal(ad)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(ad)
+ return string(b)
}
func AccessDataFromJson(data io.Reader) *AccessData {
- decoder := json.NewDecoder(data)
- var ad AccessData
- err := decoder.Decode(&ad)
- if err == nil {
- return &ad
- } else {
- return nil
- }
+ var ad *AccessData
+ json.NewDecoder(data).Decode(&ad)
+ return ad
}
func (ar *AccessResponse) ToJson() string {
- b, err := json.Marshal(ar)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(ar)
+ return string(b)
}
func AccessResponseFromJson(data io.Reader) *AccessResponse {
- decoder := json.NewDecoder(data)
- var ar AccessResponse
- err := decoder.Decode(&ar)
- if err == nil {
- return &ar
- } else {
- return nil
- }
+ var ar *AccessResponse
+ json.NewDecoder(data).Decode(&ar)
+ return ar
}
diff --git a/vendor/github.com/mattermost/platform/model/analytics_row.go b/vendor/github.com/mattermost/mattermost-server/model/analytics_row.go
index 5493163d..4615bb79 100644
--- a/vendor/github.com/mattermost/platform/model/analytics_row.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/analytics_row.go
@@ -16,23 +16,14 @@ type AnalyticsRow struct {
type AnalyticsRows []*AnalyticsRow
func (me *AnalyticsRow) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func AnalyticsRowFromJson(data io.Reader) *AnalyticsRow {
- decoder := json.NewDecoder(data)
- var me AnalyticsRow
- err := decoder.Decode(&me)
- if err == nil {
- return &me
- } else {
- return nil
- }
+ var me *AnalyticsRow
+ json.NewDecoder(data).Decode(&me)
+ return me
}
func (me AnalyticsRows) ToJson() string {
@@ -44,12 +35,7 @@ func (me AnalyticsRows) ToJson() string {
}
func AnalyticsRowsFromJson(data io.Reader) AnalyticsRows {
- decoder := json.NewDecoder(data)
var me AnalyticsRows
- err := decoder.Decode(&me)
- if err == nil {
- return me
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&me)
+ return me
}
diff --git a/vendor/github.com/mattermost/platform/model/audit.go b/vendor/github.com/mattermost/mattermost-server/model/audit.go
index 7699cf93..e3d1bdf9 100644
--- a/vendor/github.com/mattermost/platform/model/audit.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/audit.go
@@ -19,21 +19,12 @@ type Audit struct {
}
func (o *Audit) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func AuditFromJson(data io.Reader) *Audit {
- decoder := json.NewDecoder(data)
- var o Audit
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Audit
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/audits.go b/vendor/github.com/mattermost/mattermost-server/model/audits.go
index e57e2a3a..3673eb61 100644
--- a/vendor/github.com/mattermost/platform/model/audits.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/audits.go
@@ -28,12 +28,7 @@ func (o Audits) ToJson() string {
}
func AuditsFromJson(data io.Reader) Audits {
- decoder := json.NewDecoder(data)
var o Audits
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/authorize.go b/vendor/github.com/mattermost/mattermost-server/model/authorize.go
index df07ff14..922adc07 100644
--- a/vendor/github.com/mattermost/platform/model/authorize.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/authorize.go
@@ -12,6 +12,7 @@ import (
const (
AUTHCODE_EXPIRE_TIME = 60 * 10 // 10 minutes
AUTHCODE_RESPONSE_TYPE = "code"
+ IMPLICIT_RESPONSE_TYPE = "token"
DEFAULT_SCOPE = "user"
)
@@ -58,11 +59,11 @@ func (ad *AuthData) IsValid() *AppError {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.create_at.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest)
}
- if len(ad.RedirectUri) == 0 || len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) {
+ if len(ad.RedirectUri) > 256 || !IsValidHttpUrl(ad.RedirectUri) {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest)
}
- if len(ad.State) > 128 {
+ if len(ad.State) > 1024 {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ad.ClientId, http.StatusBadRequest)
}
@@ -89,7 +90,7 @@ func (ar *AuthorizeRequest) IsValid() *AppError {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest)
}
- if len(ar.State) > 128 {
+ if len(ar.State) > 1024 {
return NewAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ar.ClientId, http.StatusBadRequest)
}
@@ -115,43 +116,25 @@ func (ad *AuthData) PreSave() {
}
func (ad *AuthData) ToJson() string {
- b, err := json.Marshal(ad)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(ad)
+ return string(b)
}
func AuthDataFromJson(data io.Reader) *AuthData {
- decoder := json.NewDecoder(data)
- var ad AuthData
- err := decoder.Decode(&ad)
- if err == nil {
- return &ad
- } else {
- return nil
- }
+ var ad *AuthData
+ json.NewDecoder(data).Decode(&ad)
+ return ad
}
func (ar *AuthorizeRequest) ToJson() string {
- b, err := json.Marshal(ar)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(ar)
+ return string(b)
}
func AuthorizeRequestFromJson(data io.Reader) *AuthorizeRequest {
- decoder := json.NewDecoder(data)
- var ar AuthorizeRequest
- err := decoder.Decode(&ar)
- if err == nil {
- return &ar
- } else {
- return nil
- }
+ var ar *AuthorizeRequest
+ json.NewDecoder(data).Decode(&ar)
+ return ar
}
func (ad *AuthData) IsExpired() bool {
diff --git a/vendor/github.com/mattermost/platform/model/builtin.go b/vendor/github.com/mattermost/mattermost-server/model/builtin.go
index 5dd00a96..5dd00a96 100644
--- a/vendor/github.com/mattermost/platform/model/builtin.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/builtin.go
diff --git a/vendor/github.com/mattermost/platform/model/bundle_info.go b/vendor/github.com/mattermost/mattermost-server/model/bundle_info.go
index 6965159c..7cb06701 100644
--- a/vendor/github.com/mattermost/platform/model/bundle_info.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/bundle_info.go
@@ -3,6 +3,8 @@
package model
+import "github.com/mattermost/mattermost-server/mlog"
+
type BundleInfo struct {
Path string
@@ -11,6 +13,13 @@ type BundleInfo struct {
ManifestError error
}
+func (b *BundleInfo) WrapLogger(logger *mlog.Logger) *mlog.Logger {
+ if b.Manifest != nil {
+ return logger.With(mlog.String("plugin_id", b.Manifest.Id))
+ }
+ return logger.With(mlog.String("plugin_path", b.Path))
+}
+
// Returns bundle info for the given path. The return value is never nil.
func BundleInfoForPath(path string) *BundleInfo {
m, mpath, err := FindManifest(path)
diff --git a/vendor/github.com/mattermost/platform/model/channel.go b/vendor/github.com/mattermost/mattermost-server/model/channel.go
index a4c733c3..529c49d3 100644
--- a/vendor/github.com/mattermost/platform/model/channel.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel.go
@@ -32,20 +32,22 @@ const (
)
type Channel struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- DeleteAt int64 `json:"delete_at"`
- TeamId string `json:"team_id"`
- Type string `json:"type"`
- DisplayName string `json:"display_name"`
- Name string `json:"name"`
- Header string `json:"header"`
- Purpose string `json:"purpose"`
- LastPostAt int64 `json:"last_post_at"`
- TotalMsgCount int64 `json:"total_msg_count"`
- ExtraUpdateAt int64 `json:"extra_update_at"`
- CreatorId string `json:"creator_id"`
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ TeamId string `json:"team_id"`
+ Type string `json:"type"`
+ DisplayName string `json:"display_name"`
+ Name string `json:"name"`
+ Header string `json:"header"`
+ Purpose string `json:"purpose"`
+ LastPostAt int64 `json:"last_post_at"`
+ TotalMsgCount int64 `json:"total_msg_count"`
+ ExtraUpdateAt int64 `json:"extra_update_at"`
+ CreatorId string `json:"creator_id"`
+ SchemeId *string `json:"scheme_id"`
+ Props map[string]interface{} `json:"props" db:"-"`
}
type ChannelPatch struct {
@@ -55,61 +57,47 @@ type ChannelPatch struct {
Purpose *string `json:"purpose"`
}
+type ChannelForExport struct {
+ Channel
+ TeamName string
+ SchemeName *string
+}
+
func (o *Channel) DeepCopy() *Channel {
copy := *o
+ if copy.SchemeId != nil {
+ copy.SchemeId = NewString(*o.SchemeId)
+ }
return &copy
}
func (o *Channel) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func (o *ChannelPatch) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelFromJson(data io.Reader) *Channel {
- decoder := json.NewDecoder(data)
- var o Channel
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Channel
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func ChannelPatchFromJson(data io.Reader) *ChannelPatch {
- decoder := json.NewDecoder(data)
- var o ChannelPatch
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelPatch
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *Channel) Etag() string {
return Etag(o.Id, o.UpdateAt)
}
-func (o *Channel) StatsEtag() string {
- return Etag(o.Id, o.ExtraUpdateAt)
-}
-
func (o *Channel) IsValid() *AppError {
-
if len(o.Id) != 26 {
return NewAppError("Channel.IsValid", "model.channel.is_valid.id.app_error", nil, "", http.StatusBadRequest)
}
@@ -156,17 +144,13 @@ func (o *Channel) PreSave() {
o.CreateAt = GetMillis()
o.UpdateAt = o.CreateAt
- o.ExtraUpdateAt = o.CreateAt
+ o.ExtraUpdateAt = 0
}
func (o *Channel) PreUpdate() {
o.UpdateAt = GetMillis()
}
-func (o *Channel) ExtraUpdated() {
- o.ExtraUpdateAt = GetMillis()
-}
-
func (o *Channel) IsGroupOrDirect() bool {
return o.Type == CHANNEL_DIRECT || o.Type == CHANNEL_GROUP
}
@@ -189,6 +173,18 @@ func (o *Channel) Patch(patch *ChannelPatch) {
}
}
+func (o *Channel) MakeNonNil() {
+ if o.Props == nil {
+ o.Props = make(map[string]interface{})
+ }
+}
+
+func (o *Channel) AddProp(key string, value interface{}) {
+ o.MakeNonNil()
+
+ o.Props[key] = value
+}
+
func GetDMNameFromIds(userId1, userId2 string) string {
if userId1 > userId2 {
return userId2 + "__" + userId1
diff --git a/vendor/github.com/mattermost/platform/model/channel_count.go b/vendor/github.com/mattermost/mattermost-server/model/channel_count.go
index aecb0c71..8c6d8dd0 100644
--- a/vendor/github.com/mattermost/platform/model/channel_count.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_count.go
@@ -43,21 +43,12 @@ func (o *ChannelCounts) Etag() string {
}
func (o *ChannelCounts) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelCountsFromJson(data io.Reader) *ChannelCounts {
- decoder := json.NewDecoder(data)
- var o ChannelCounts
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelCounts
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/channel_data.go b/vendor/github.com/mattermost/mattermost-server/model/channel_data.go
index 41b7eaa6..aae0a149 100644
--- a/vendor/github.com/mattermost/platform/model/channel_data.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_data.go
@@ -23,21 +23,12 @@ func (o *ChannelData) Etag() string {
}
func (o *ChannelData) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelDataFromJson(data io.Reader) *ChannelData {
- decoder := json.NewDecoder(data)
- var o ChannelData
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelData
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/channel_list.go b/vendor/github.com/mattermost/mattermost-server/model/channel_list.go
index d5a4ccb7..1b3bda46 100644
--- a/vendor/github.com/mattermost/platform/model/channel_list.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_list.go
@@ -41,23 +41,13 @@ func (o *ChannelList) Etag() string {
}
func ChannelListFromJson(data io.Reader) *ChannelList {
- decoder := json.NewDecoder(data)
- var o ChannelList
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelList
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func ChannelSliceFromJson(data io.Reader) []*Channel {
- decoder := json.NewDecoder(data)
var o []*Channel
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/channel_member.go b/vendor/github.com/mattermost/mattermost-server/model/channel_member.go
index e7ad8232..941db62f 100644
--- a/vendor/github.com/mattermost/platform/model/channel_member.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_member.go
@@ -28,18 +28,26 @@ type ChannelUnread struct {
}
type ChannelMember struct {
- ChannelId string `json:"channel_id"`
- UserId string `json:"user_id"`
- Roles string `json:"roles"`
- LastViewedAt int64 `json:"last_viewed_at"`
- MsgCount int64 `json:"msg_count"`
- MentionCount int64 `json:"mention_count"`
- NotifyProps StringMap `json:"notify_props"`
- LastUpdateAt int64 `json:"last_update_at"`
+ ChannelId string `json:"channel_id"`
+ UserId string `json:"user_id"`
+ Roles string `json:"roles"`
+ LastViewedAt int64 `json:"last_viewed_at"`
+ MsgCount int64 `json:"msg_count"`
+ MentionCount int64 `json:"mention_count"`
+ NotifyProps StringMap `json:"notify_props"`
+ LastUpdateAt int64 `json:"last_update_at"`
+ SchemeUser bool `json:"scheme_user"`
+ SchemeAdmin bool `json:"scheme_admin"`
+ ExplicitRoles string `json:"explicit_roles"`
}
type ChannelMembers []ChannelMember
+type ChannelMemberForExport struct {
+ ChannelMember
+ ChannelName string
+}
+
func (o *ChannelMembers) ToJson() string {
if b, err := json.Marshal(o); err != nil {
return "[]"
@@ -49,54 +57,31 @@ func (o *ChannelMembers) ToJson() string {
}
func (o *ChannelUnread) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelMembersFromJson(data io.Reader) *ChannelMembers {
- decoder := json.NewDecoder(data)
- var o ChannelMembers
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelMembers
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func ChannelUnreadFromJson(data io.Reader) *ChannelUnread {
- decoder := json.NewDecoder(data)
- var o ChannelUnread
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelUnread
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *ChannelMember) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelMemberFromJson(data io.Reader) *ChannelMember {
- decoder := json.NewDecoder(data)
- var o ChannelMember
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelMember
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *ChannelMember) IsValid() *AppError {
diff --git a/vendor/github.com/mattermost/platform/model/channel_member_history.go b/vendor/github.com/mattermost/mattermost-server/model/channel_member_history.go
index bc71b580..55435c32 100644
--- a/vendor/github.com/mattermost/platform/model/channel_member_history.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_member_history.go
@@ -6,7 +6,6 @@ package model
type ChannelMemberHistory struct {
ChannelId string
UserId string
- UserEmail string `db:"Email"`
JoinTime int64
LeaveTime *int64
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/channel_member_history_result.go b/vendor/github.com/mattermost/mattermost-server/model/channel_member_history_result.go
new file mode 100644
index 00000000..ed3e7963
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_member_history_result.go
@@ -0,0 +1,15 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+type ChannelMemberHistoryResult struct {
+ ChannelId string
+ UserId string
+ JoinTime int64
+ LeaveTime *int64
+
+ // these two fields are never set in the database - when we SELECT, we join on Users to get them
+ UserEmail string `db:"Email"`
+ Username string
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/channel_mentions.go b/vendor/github.com/mattermost/mattermost-server/model/channel_mentions.go
new file mode 100644
index 00000000..795ec379
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_mentions.go
@@ -0,0 +1,28 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "regexp"
+ "strings"
+)
+
+var channelMentionRegexp = regexp.MustCompile(`\B~[a-zA-Z0-9\-_]+`)
+
+func ChannelMentions(message string) []string {
+ var names []string
+
+ if strings.Contains(message, "~") {
+ alreadyMentioned := make(map[string]bool)
+ for _, match := range channelMentionRegexp.FindAllString(message, -1) {
+ name := match[1:]
+ if !alreadyMentioned[name] {
+ names = append(names, name)
+ alreadyMentioned[name] = true
+ }
+ }
+ }
+
+ return names
+}
diff --git a/vendor/github.com/mattermost/platform/model/channel_search.go b/vendor/github.com/mattermost/mattermost-server/model/channel_search.go
index d915c164..593cf669 100644
--- a/vendor/github.com/mattermost/platform/model/channel_search.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_search.go
@@ -14,22 +14,13 @@ type ChannelSearch struct {
// ToJson convert a Channel to a json string
func (c *ChannelSearch) ToJson() string {
- b, err := json.Marshal(c)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(c)
+ return string(b)
}
// ChannelSearchFromJson will decode the input and return a Channel
func ChannelSearchFromJson(data io.Reader) *ChannelSearch {
- decoder := json.NewDecoder(data)
- var cs ChannelSearch
- err := decoder.Decode(&cs)
- if err == nil {
- return &cs
- } else {
- return nil
- }
+ var cs *ChannelSearch
+ json.NewDecoder(data).Decode(&cs)
+ return cs
}
diff --git a/vendor/github.com/mattermost/platform/model/channel_stats.go b/vendor/github.com/mattermost/mattermost-server/model/channel_stats.go
index 758aa2b1..21af920f 100644
--- a/vendor/github.com/mattermost/platform/model/channel_stats.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_stats.go
@@ -14,21 +14,12 @@ type ChannelStats struct {
}
func (o *ChannelStats) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelStatsFromJson(data io.Reader) *ChannelStats {
- decoder := json.NewDecoder(data)
- var o ChannelStats
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelStats
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/channel_view.go b/vendor/github.com/mattermost/mattermost-server/model/channel_view.go
index e7b1de30..650d14ce 100644
--- a/vendor/github.com/mattermost/platform/model/channel_view.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/channel_view.go
@@ -14,23 +14,14 @@ type ChannelView struct {
}
func (o *ChannelView) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelViewFromJson(data io.Reader) *ChannelView {
- decoder := json.NewDecoder(data)
- var o ChannelView
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelView
+ json.NewDecoder(data).Decode(&o)
+ return o
}
type ChannelViewResponse struct {
@@ -39,21 +30,12 @@ type ChannelViewResponse struct {
}
func (o *ChannelViewResponse) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ChannelViewResponseFromJson(data io.Reader) *ChannelViewResponse {
- decoder := json.NewDecoder(data)
- var o ChannelViewResponse
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ChannelViewResponse
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/client4.go b/vendor/github.com/mattermost/mattermost-server/model/client4.go
index e84a23e5..57c4fb7c 100644
--- a/vendor/github.com/mattermost/platform/model/client4.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/client4.go
@@ -13,6 +13,33 @@ import (
"net/url"
"strconv"
"strings"
+ "time"
+)
+
+const (
+ HEADER_REQUEST_ID = "X-Request-ID"
+ HEADER_VERSION_ID = "X-Version-ID"
+ HEADER_CLUSTER_ID = "X-Cluster-ID"
+ HEADER_ETAG_SERVER = "ETag"
+ HEADER_ETAG_CLIENT = "If-None-Match"
+ HEADER_FORWARDED = "X-Forwarded-For"
+ HEADER_REAL_IP = "X-Real-IP"
+ HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
+ HEADER_TOKEN = "token"
+ HEADER_BEARER = "BEARER"
+ HEADER_AUTH = "Authorization"
+ HEADER_REQUESTED_WITH = "X-Requested-With"
+ HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
+ STATUS = "status"
+ STATUS_OK = "OK"
+ STATUS_FAIL = "FAIL"
+ STATUS_REMOVE = "REMOVE"
+
+ CLIENT_DIR = "client"
+
+ API_URL_SUFFIX_V1 = "/api/v1"
+ API_URL_SUFFIX_V4 = "/api/v4"
+ API_URL_SUFFIX = API_URL_SUFFIX_V4
)
type Response struct {
@@ -30,10 +57,29 @@ type Client4 struct {
HttpClient *http.Client // The http client
AuthToken string
AuthType string
+ HttpHeader map[string]string // Headers to be copied over for each request
+}
+
+func closeBody(r *http.Response) {
+ if r.Body != nil {
+ ioutil.ReadAll(r.Body)
+ r.Body.Close()
+ }
+}
+
+// Must is a convenience function used for testing.
+func (c *Client4) Must(result interface{}, resp *Response) interface{} {
+ if resp.Error != nil {
+
+ time.Sleep(time.Second)
+ panic(resp.Error)
+ }
+
+ return result
}
func NewAPIv4Client(url string) *Client4 {
- return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", ""}
+ return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", "", map[string]string{}}
}
func BuildErrorResponse(r *http.Response, err *AppError) *Response {
@@ -64,6 +110,11 @@ func BuildResponse(r *http.Response) *Response {
}
}
+func (c *Client4) MockSession(sessionToken string) {
+ c.AuthToken = sessionToken
+ c.AuthType = HEADER_BEARER
+}
+
func (c *Client4) SetOAuthToken(token string) {
c.AuthToken = token
c.AuthType = HEADER_TOKEN
@@ -82,6 +133,10 @@ func (c *Client4) GetUserRoute(userId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId)
}
+func (c *Client4) GetUserAccessTokensRoute() string {
+ return fmt.Sprintf(c.GetUsersRoute() + "/tokens")
+}
+
func (c *Client4) GetUserAccessTokenRoute(tokenId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/tokens/%v", tokenId)
}
@@ -158,6 +213,10 @@ func (c *Client4) GetPostsRoute() string {
return fmt.Sprintf("/posts")
}
+func (c *Client4) GetPostsEphemeralRoute() string {
+ return fmt.Sprintf("/posts/ephemeral")
+}
+
func (c *Client4) GetConfigRoute() string {
return fmt.Sprintf("/config")
}
@@ -194,6 +253,10 @@ func (c *Client4) GetTestEmailRoute() string {
return fmt.Sprintf("/email/test")
}
+func (c *Client4) GetTestS3Route() string {
+ return fmt.Sprintf("/file/s3_test")
+}
+
func (c *Client4) GetDatabaseRoute() string {
return fmt.Sprintf("/database")
}
@@ -278,6 +341,10 @@ func (c *Client4) GetEmojiRoute(emojiId string) string {
return fmt.Sprintf(c.GetEmojisRoute()+"/%v", emojiId)
}
+func (c *Client4) GetEmojiByNameRoute(name string) string {
+ return fmt.Sprintf(c.GetEmojisRoute()+"/name/%v", name)
+}
+
func (c *Client4) GetReactionsRoute() string {
return fmt.Sprintf("/reactions")
}
@@ -298,10 +365,50 @@ func (c *Client4) GetJobsRoute() string {
return fmt.Sprintf("/jobs")
}
+func (c *Client4) GetRolesRoute() string {
+ return fmt.Sprintf("/roles")
+}
+
+func (c *Client4) GetSchemesRoute() string {
+ return fmt.Sprintf("/schemes")
+}
+
+func (c *Client4) GetSchemeRoute(id string) string {
+ return c.GetSchemesRoute() + fmt.Sprintf("/%v", id)
+}
+
func (c *Client4) GetAnalyticsRoute() string {
return fmt.Sprintf("/analytics")
}
+func (c *Client4) GetTimezonesRoute() string {
+ return fmt.Sprintf(c.GetSystemRoute() + "/timezones")
+}
+
+func (c *Client4) GetChannelSchemeRoute(channelId string) string {
+ return fmt.Sprintf(c.GetChannelsRoute()+"/%v/scheme", channelId)
+}
+
+func (c *Client4) GetTeamSchemeRoute(teamId string) string {
+ return fmt.Sprintf(c.GetTeamsRoute()+"/%v/scheme", teamId)
+}
+
+func (c *Client4) GetTotalUsersStatsRoute() string {
+ return fmt.Sprintf(c.GetUsersRoute() + "/stats")
+}
+
+func (c *Client4) GetRedirectLocationRoute() string {
+ return fmt.Sprintf("/redirect_location")
+}
+
+func (c *Client4) GetRegisterTermsOfServiceRoute(userId string) string {
+ return c.GetUserRoute(userId) + "/terms_of_service"
+}
+
+func (c *Client4) GetTermsOfServiceRoute() string {
+ return "/terms_of_service"
+}
+
func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag)
}
@@ -320,7 +427,6 @@ func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) {
func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) {
rq, _ := http.NewRequest(method, url, strings.NewReader(data))
- rq.Close = true
if len(etag) > 0 {
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
@@ -330,6 +436,13 @@ func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response,
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
}
+ if c.HttpHeader != nil && len(c.HttpHeader) > 0 {
+
+ for k, v := range c.HttpHeader {
+ rq.Header.Set(k, v)
+ }
+ }
+
if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
} else if rp.StatusCode == 304 {
@@ -345,7 +458,6 @@ func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response,
func (c *Client4) DoUploadFile(url string, data []byte, contentType string) (*FileUploadResponse, *Response) {
rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data))
rq.Header.Set("Content-Type", contentType)
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@@ -367,7 +479,6 @@ func (c *Client4) DoUploadFile(url string, data []byte, contentType string) (*Fi
func (c *Client4) DoEmojiUploadFile(url string, data []byte, contentType string) (*Emoji, *Response) {
rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data))
rq.Header.Set("Content-Type", contentType)
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@@ -389,7 +500,6 @@ func (c *Client4) DoEmojiUploadFile(url string, data []byte, contentType string)
func (c *Client4) DoUploadImportTeam(url string, data []byte, contentType string) (map[string]string, *Response) {
rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data))
rq.Header.Set("Content-Type", contentType)
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@@ -506,13 +616,13 @@ func (c *Client4) CreateUser(user *User) (*User, *Response) {
}
}
-// CreateUserWithHash creates a user in the system based on the provided user struct and hash created.
-func (c *Client4) CreateUserWithHash(user *User, hash, data string) (*User, *Response) {
+// CreateUserWithToken creates a user in the system based on the provided tokenId.
+func (c *Client4) CreateUserWithToken(user *User, tokenId string) (*User, *Response) {
var query string
- if hash != "" && data != "" {
- query = fmt.Sprintf("?d=%v&h=%v", url.QueryEscape(data), hash)
+ if tokenId != "" {
+ query = fmt.Sprintf("?t=%v", tokenId)
} else {
- err := NewAppError("MissingHashOrData", "api.user.create_user.missing_hash_or_data.app_error", nil, "", http.StatusBadRequest)
+ err := NewAppError("MissingHashOrData", "api.user.create_user.missing_token.app_error", nil, "", http.StatusBadRequest)
return nil, &Response{StatusCode: err.StatusCode, Error: err}
}
if r, err := c.DoApiPost(c.GetUsersRoute()+query, user.ToJson()); err != nil {
@@ -683,7 +793,7 @@ func (c *Client4) GetUsersNotInTeam(teamId string, page int, perPage int, etag s
}
}
-// GetUsersInChannel returns a page of users on a team. Page counting starts at 0.
+// GetUsersInChannel returns a page of users in a channel. Page counting starts at 0.
func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, etag string) ([]*User, *Response) {
query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v", channelId, page, perPage)
if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
@@ -694,7 +804,18 @@ func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, eta
}
}
-// GetUsersNotInChannel returns a page of users on a team. Page counting starts at 0.
+// GetUsersInChannelStatus returns a page of users in a channel. Page counting starts at 0. Sorted by Status
+func (c *Client4) GetUsersInChannelByStatus(channelId string, page int, perPage int, etag string) ([]*User, *Response) {
+ query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v&sort=status", channelId, page, perPage)
+ if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return UserListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// GetUsersNotInChannel returns a page of users not in a channel. Page counting starts at 0.
func (c *Client4) GetUsersNotInChannel(teamId, channelId string, page int, perPage int, etag string) ([]*User, *Response) {
query := fmt.Sprintf("?in_team=%v&not_in_channel=%v&page=%v&per_page=%v", teamId, channelId, page, perPage)
if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
@@ -1001,7 +1122,6 @@ func (c *Client4) SetProfileImage(userId string, data []byte) (bool, *Response)
rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetUserRoute(userId)+"/image", bytes.NewReader(body.Bytes()))
rq.Header.Set("Content-Type", writer.FormDataContentType())
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@@ -1035,10 +1155,23 @@ func (c *Client4) CreateUserAccessToken(userId, description string) (*UserAccess
}
}
-// GetUserAccessToken will get a user access token's id, description and the user_id
-// of the user it is for. The actual token will not be returned. Must have the
-// 'read_user_access_token' permission and if getting for another user, must have the
-// 'edit_other_users' permission.
+// GetUserAccessTokens will get a page of access tokens' id, description, is_active
+// and the user_id in the system. The actual token will not be returned. Must have
+// the 'manage_system' permission.
+func (c *Client4) GetUserAccessTokens(page int, perPage int) ([]*UserAccessToken, *Response) {
+ query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
+ if r, err := c.DoApiGet(c.GetUserAccessTokensRoute()+query, ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return UserAccessTokenListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// GetUserAccessToken will get a user access tokens' id, description, is_active
+// and the user_id of the user it is for. The actual token will not be returned.
+// Must have the 'read_user_access_token' permission and if getting for another
+// user, must have the 'edit_other_users' permission.
func (c *Client4) GetUserAccessToken(tokenId string) (*UserAccessToken, *Response) {
if r, err := c.DoApiGet(c.GetUserAccessTokenRoute(tokenId), ""); err != nil {
return nil, BuildErrorResponse(r, err)
@@ -1075,6 +1208,16 @@ func (c *Client4) RevokeUserAccessToken(tokenId string) (bool, *Response) {
}
}
+// SearchUserAccessTokens returns user access tokens matching the provided search term.
+func (c *Client4) SearchUserAccessTokens(search *UserAccessTokenSearch) ([]*UserAccessToken, *Response) {
+ if r, err := c.DoApiPost(c.GetUsersRoute()+"/tokens/search", search.ToJson()); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return UserAccessTokenListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// DisableUserAccessToken will disable a user access token by id. Must have the
// 'revoke_user_access_token' permission and if disabling for another user, must have the
// 'edit_other_users' permission.
@@ -1196,6 +1339,16 @@ func (c *Client4) UpdateTeamMemberRoles(teamId, userId, newRoles string) (bool,
}
}
+// UpdateTeamMemberSchemeRoles will update the scheme-derived roles on a team for a user.
+func (c *Client4) UpdateTeamMemberSchemeRoles(teamId string, userId string, schemeRoles *SchemeRoles) (bool, *Response) {
+ if r, err := c.DoApiPut(c.GetTeamMemberRoute(teamId, userId)+"/schemeRoles", schemeRoles.ToJson()); err != nil {
+ return false, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return CheckStatusOK(r), BuildResponse(r)
+ }
+}
+
// UpdateTeam will update a team.
func (c *Client4) UpdateTeam(team *Team) (*Team, *Response) {
if r, err := c.DoApiPut(c.GetTeamRoute(team.Id), team.ToJson()); err != nil {
@@ -1282,16 +1435,16 @@ func (c *Client4) AddTeamMember(teamId, userId string) (*TeamMember, *Response)
}
// AddTeamMemberFromInvite adds a user to a team and return a team member using an invite id
-// or an invite hash/data pair.
-func (c *Client4) AddTeamMemberFromInvite(hash, dataToHash, inviteId string) (*TeamMember, *Response) {
+// or an invite token/data pair.
+func (c *Client4) AddTeamMemberFromInvite(token, inviteId string) (*TeamMember, *Response) {
var query string
if inviteId != "" {
query += fmt.Sprintf("?invite_id=%v", inviteId)
}
- if hash != "" && dataToHash != "" {
- query += fmt.Sprintf("?hash=%v&data=%v", hash, dataToHash)
+ if token != "" {
+ query += fmt.Sprintf("?token=%v", token)
}
if r, err := c.DoApiPost(c.GetTeamsRoute()+"/members/invite"+query, ""); err != nil {
@@ -1339,6 +1492,17 @@ func (c *Client4) GetTeamStats(teamId, etag string) (*TeamStats, *Response) {
}
}
+// GetTotalUsersStats returns a total system user stats.
+// Must be authenticated.
+func (c *Client4) GetTotalUsersStats(etag string) (*UsersStats, *Response) {
+ if r, err := c.DoApiGet(c.GetTotalUsersStatsRoute(), etag); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return UsersStatsFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// GetTeamUnread will return a TeamUnread object that contains the amount of
// unread messages and mentions the user has for the specified team.
// Must be authenticated.
@@ -1401,6 +1565,68 @@ func (c *Client4) GetTeamInviteInfo(inviteId string) (*Team, *Response) {
}
}
+// SetTeamIcon sets team icon of the team
+func (c *Client4) SetTeamIcon(teamId string, data []byte) (bool, *Response) {
+
+ body := &bytes.Buffer{}
+ writer := multipart.NewWriter(body)
+
+ if part, err := writer.CreateFormFile("image", "teamIcon.png"); err != nil {
+ return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
+ } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
+ return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.no_file.app_error", nil, err.Error(), http.StatusBadRequest)}
+ }
+
+ if err := writer.Close(); err != nil {
+ return false, &Response{Error: NewAppError("SetTeamIcon", "model.client.set_team_icon.writer.app_error", nil, err.Error(), http.StatusBadRequest)}
+ }
+
+ rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetTeamRoute(teamId)+"/image", bytes.NewReader(body.Bytes()))
+ rq.Header.Set("Content-Type", writer.FormDataContentType())
+
+ if len(c.AuthToken) > 0 {
+ rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
+ }
+
+ if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
+ // set to http.StatusForbidden(403)
+ return false, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.GetTeamRoute(teamId)+"/image", "model.client.connecting.app_error", nil, err.Error(), 403)}
+ } else {
+ defer closeBody(rp)
+
+ if rp.StatusCode >= 300 {
+ return false, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
+ } else {
+ return CheckStatusOK(rp), BuildResponse(rp)
+ }
+ }
+}
+
+// GetTeamIcon gets the team icon of the team
+func (c *Client4) GetTeamIcon(teamId, etag string) ([]byte, *Response) {
+ if r, err := c.DoApiGet(c.GetTeamRoute(teamId)+"/image", etag); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+
+ if data, err := ioutil.ReadAll(r.Body); err != nil {
+ return nil, BuildErrorResponse(r, NewAppError("GetTeamIcon", "model.client.get_team_icon.app_error", nil, err.Error(), r.StatusCode))
+ } else {
+ return data, BuildResponse(r)
+ }
+ }
+}
+
+// RemoveTeamIcon updates LastTeamIconUpdate to 0 which indicates team icon is removed.
+func (c *Client4) RemoveTeamIcon(teamId string) (bool, *Response) {
+ if r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "/image"); err != nil {
+ return false, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return CheckStatusOK(r), BuildResponse(r)
+ }
+}
+
// Channel Section
// CreateChannel creates a channel based on the provided channel struct.
@@ -1433,6 +1659,16 @@ func (c *Client4) PatchChannel(channelId string, patch *ChannelPatch) (*Channel,
}
}
+// ConvertChannelToPrivate converts public to private channel.
+func (c *Client4) ConvertChannelToPrivate(channelId string) (*Channel, *Response) {
+ if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/convert", ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return ChannelFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// RestoreChannel restores a previously deleted channel. Any missing fields are not updated.
func (c *Client4) RestoreChannel(channelId string) (*Channel, *Response) {
if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/restore", ""); err != nil {
@@ -1567,6 +1803,15 @@ func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Ch
}
}
+func (c *Client4) GetChannelByNameIncludeDeleted(channelName, teamId string, etag string) (*Channel, *Response) {
+ if r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted=true", etag); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return ChannelFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// GetChannelByNameForTeamName returns a channel based on the provided channel name and team name strings.
func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag string) (*Channel, *Response) {
if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName), etag); err != nil {
@@ -1577,6 +1822,15 @@ func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag
}
}
+func (c *Client4) GetChannelByNameForTeamNameIncludeDeleted(channelName, teamName string, etag string) (*Channel, *Response) {
+ if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted=true", etag); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return ChannelFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// GetChannelMembers gets a page of channel members.
func (c *Client4) GetChannelMembers(channelId string, page, perPage int, etag string) (*ChannelMembers, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
@@ -1652,6 +1906,16 @@ func (c *Client4) UpdateChannelRoles(channelId, userId, roles string) (bool, *Re
}
}
+// UpdateChannelMemberSchemeRoles will update the scheme-derived roles on a channel for a user.
+func (c *Client4) UpdateChannelMemberSchemeRoles(channelId string, userId string, schemeRoles *SchemeRoles) (bool, *Response) {
+ if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/schemeRoles", schemeRoles.ToJson()); err != nil {
+ return false, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return CheckStatusOK(r), BuildResponse(r)
+ }
+}
+
// UpdateChannelNotifyProps will update the notification properties on a channel for a user.
func (c *Client4) UpdateChannelNotifyProps(channelId, userId string, props map[string]string) (bool, *Response) {
if r, err := c.DoApiPut(c.GetChannelMemberRoute(channelId, userId)+"/notify_props", MapToJson(props)); err != nil {
@@ -1694,11 +1958,43 @@ func (c *Client4) RemoveUserFromChannel(channelId, userId string) (bool, *Respon
}
}
+// AutocompleteChannelsForTeam will return an ordered list of channels autocomplete suggestions
+func (c *Client4) AutocompleteChannelsForTeam(teamId, name string) (*ChannelList, *Response) {
+ query := fmt.Sprintf("?name=%v", name)
+ if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+"/autocomplete"+query, ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return ChannelListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// AutocompleteChannelsForTeamForSearch will return an ordered list of your channels autocomplete suggestions
+func (c *Client4) AutocompleteChannelsForTeamForSearch(teamId, name string) (*ChannelList, *Response) {
+ query := fmt.Sprintf("?name=%v", name)
+ if r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+"/search_autocomplete"+query, ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return ChannelListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// Post Section
// CreatePost creates a post based on the provided post struct.
func (c *Client4) CreatePost(post *Post) (*Post, *Response) {
- if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToJson()); err != nil {
+ if r, err := c.DoApiPost(c.GetPostsRoute(), post.ToUnsanitizedJson()); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return PostFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// CreatePostEphemeral creates a ephemeral post based on the provided post struct which is send to the given user id
+func (c *Client4) CreatePostEphemeral(post *PostEphemeral) (*Post, *Response) {
+ if r, err := c.DoApiPost(c.GetPostsEphemeralRoute(), post.ToUnsanitizedJson()); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
@@ -1708,7 +2004,7 @@ func (c *Client4) CreatePost(post *Post) (*Post, *Response) {
// UpdatePost updates a post based on the provided post struct.
func (c *Client4) UpdatePost(postId string, post *Post) (*Post, *Response) {
- if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToJson()); err != nil {
+ if r, err := c.DoApiPut(c.GetPostRoute(postId), post.ToUnsanitizedJson()); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
@@ -1863,12 +2159,31 @@ func (c *Client4) GetPostsBefore(channelId, postId string, page, perPage int, et
// SearchPosts returns any posts with matching terms string.
func (c *Client4) SearchPosts(teamId string, terms string, isOrSearch bool) (*PostList, *Response) {
+ params := SearchParameter{
+ Terms: &terms,
+ IsOrSearch: &isOrSearch,
+ }
+ return c.SearchPostsWithParams(teamId, &params)
+}
+
+// SearchPosts returns any posts with matching terms string.
+func (c *Client4) SearchPostsWithParams(teamId string, params *SearchParameter) (*PostList, *Response) {
+ if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", params.SearchParameterToJson()); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return PostListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// SearchPosts returns any posts with matching terms string, including .
+func (c *Client4) SearchPostsWithMatches(teamId string, terms string, isOrSearch bool) (*PostSearchResults, *Response) {
requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch}
if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search", StringInterfaceToJson(requestBody)); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
- return PostListFromJson(r.Body), BuildResponse(r)
+ return PostSearchResultsFromJson(r.Body), BuildResponse(r)
}
}
@@ -1884,7 +2199,8 @@ func (c *Client4) DoPostAction(postId, actionId string) (bool, *Response) {
// File Section
-// UploadFile will upload a file to a channel, to be later attached to a post.
+// UploadFile will upload a file to a channel using a multipart request, to be later attached to a post.
+// This method is functionally equivalent to Client4.UploadFileAsRequestBody.
func (c *Client4) UploadFile(data []byte, channelId string, filename string) (*FileUploadResponse, *Response) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
@@ -1908,6 +2224,12 @@ func (c *Client4) UploadFile(data []byte, channelId string, filename string) (*F
return c.DoUploadFile(c.GetFilesRoute(), body.Bytes(), writer.FormDataContentType())
}
+// UploadFileAsRequestBody will upload a file to a channel as the body of a request, to be later attached
+// to a post. This method is functionally equivalent to Client4.UploadFile.
+func (c *Client4) UploadFileAsRequestBody(data []byte, channelId string, filename string) (*FileUploadResponse, *Response) {
+ return c.DoUploadFile(c.GetFilesRoute()+fmt.Sprintf("?channel_id=%v&filename=%v", url.QueryEscape(channelId), url.QueryEscape(filename)), data, http.DetectContentType(data))
+}
+
// GetFile gets the bytes for a file by id.
func (c *Client4) GetFile(fileId string) ([]byte, *Response) {
if r, err := c.DoApiGet(c.GetFileRoute(fileId), ""); err != nil {
@@ -2045,8 +2367,18 @@ func (c *Client4) GetPing() (string, *Response) {
}
// TestEmail will attempt to connect to the configured SMTP server.
-func (c *Client4) TestEmail() (bool, *Response) {
- if r, err := c.DoApiPost(c.GetTestEmailRoute(), ""); err != nil {
+func (c *Client4) TestEmail(config *Config) (bool, *Response) {
+ if r, err := c.DoApiPost(c.GetTestEmailRoute(), config.ToJson()); err != nil {
+ return false, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return CheckStatusOK(r), BuildResponse(r)
+ }
+}
+
+// TestS3Connection will attempt to connect to the AWS S3.
+func (c *Client4) TestS3Connection(config *Config) (bool, *Response) {
+ if r, err := c.DoApiPost(c.GetTestS3Route(), config.ToJson()); err != nil {
return false, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
@@ -2085,6 +2417,18 @@ func (c *Client4) GetOldClientConfig(etag string) (map[string]string, *Response)
}
}
+// GetEnvironmentConfig will retrieve a map mirroring the server configuration where fields
+// are set to true if the corresponding config setting is set through an environment variable.
+// Settings that haven't been set through environment variables will be missing from the map.
+func (c *Client4) GetEnvironmentConfig() (map[string]interface{}, *Response) {
+ if r, err := c.DoApiGet(c.GetConfigRoute()+"/environment", ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return StringInterfaceFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// GetOldClientLicense will retrieve the parts of the server license needed by the
// client, formatted in the old format.
func (c *Client4) GetOldClientLicense(etag string) (map[string]string, *Response) {
@@ -2143,7 +2487,6 @@ func (c *Client4) UploadLicenseFile(data []byte) (bool, *Response) {
rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetLicenseRoute(), bytes.NewReader(body.Bytes()))
rq.Header.Set("Content-Type", writer.FormDataContentType())
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@@ -2178,7 +2521,7 @@ func (c *Client4) RemoveLicenseFile() (bool, *Response) {
// and defaults to "standard". The "teamId" argument is optional and will limit results
// to a specific team.
func (c *Client4) GetAnalyticsOld(name, teamId string) (AnalyticsRows, *Response) {
- query := fmt.Sprintf("?name=%v&teamId=%v", name, teamId)
+ query := fmt.Sprintf("?name=%v&team_id=%v", name, teamId)
if r, err := c.DoApiGet(c.GetAnalyticsRoute()+"/old"+query, ""); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
@@ -2531,7 +2874,6 @@ func (c *Client4) GetComplianceReport(reportId string) (*Compliance, *Response)
func (c *Client4) DownloadComplianceReport(reportId string) ([]byte, *Response) {
var rq *http.Request
rq, _ = http.NewRequest("GET", c.ApiUrl+c.GetComplianceReportRoute(reportId), nil)
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
@@ -2636,7 +2978,6 @@ func (c *Client4) UploadBrandImage(data []byte) (bool, *Response) {
rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetBrandRoute()+"/image", bytes.NewReader(body.Bytes()))
rq.Header.Set("Content-Type", writer.FormDataContentType())
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@@ -2785,6 +3126,27 @@ func (c *Client4) DeauthorizeOAuthApp(appId string) (bool, *Response) {
}
}
+// GetOAuthAccessToken is a test helper function for the OAuth access token endpoint.
+func (c *Client4) GetOAuthAccessToken(data url.Values) (*AccessResponse, *Response) {
+ rq, _ := http.NewRequest(http.MethodPost, c.Url+"/oauth/access_token", strings.NewReader(data.Encode()))
+ rq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ if len(c.AuthToken) > 0 {
+ rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
+ }
+
+ if rp, err := c.HttpClient.Do(rq); err != nil || rp == nil {
+ return nil, &Response{StatusCode: http.StatusForbidden, Error: NewAppError(c.Url+"/oauth/access_token", "model.client.connecting.app_error", nil, err.Error(), 403)}
+ } else {
+ defer closeBody(rp)
+ if rp.StatusCode >= 300 {
+ return nil, BuildErrorResponse(rp, AppErrorFromJson(rp.Body))
+ } else {
+ return AccessResponseFromJson(rp.Body), BuildResponse(rp)
+ }
+ }
+}
+
// Elasticsearch Section
// TestElasticsearch will attempt to connect to the configured Elasticsearch server and return OK if configured
@@ -2873,7 +3235,9 @@ func (c *Client4) ExecuteCommand(channelId, command string) (*CommandResponse, *
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
- return CommandResponseFromJson(r.Body), BuildResponse(r)
+
+ response, _ := CommandResponseFromJson(r.Body)
+ return response, BuildResponse(r)
}
}
@@ -2889,7 +3253,9 @@ func (c *Client4) ExecuteCommandWithTeam(channelId, teamId, command string) (*Co
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
- return CommandResponseFromJson(r.Body), BuildResponse(r)
+
+ response, _ := CommandResponseFromJson(r.Body)
+ return response, BuildResponse(r)
}
}
@@ -2996,6 +3362,18 @@ func (c *Client4) GetEmojiList(page, perPage int) ([]*Emoji, *Response) {
}
}
+// GetSortedEmojiList returns a page of custom emoji on the system sorted based on the sort
+// parameter, blank for no sorting and "name" to sort by emoji names.
+func (c *Client4) GetSortedEmojiList(page, perPage int, sort string) ([]*Emoji, *Response) {
+ query := fmt.Sprintf("?page=%v&per_page=%v&sort=%v", page, perPage, sort)
+ if r, err := c.DoApiGet(c.GetEmojisRoute()+query, ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return EmojiListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// DeleteEmoji delete an custom emoji on the provided emoji id string.
func (c *Client4) DeleteEmoji(emojiId string) (bool, *Response) {
if r, err := c.DoApiDelete(c.GetEmojiRoute(emojiId)); err != nil {
@@ -3006,7 +3384,7 @@ func (c *Client4) DeleteEmoji(emojiId string) (bool, *Response) {
}
}
-// GetEmoji returns a custom emoji in the system on the provided emoji id string.
+// GetEmoji returns a custom emoji based on the emojiId string.
func (c *Client4) GetEmoji(emojiId string) (*Emoji, *Response) {
if r, err := c.DoApiGet(c.GetEmojiRoute(emojiId), ""); err != nil {
return nil, BuildErrorResponse(r, err)
@@ -3016,6 +3394,16 @@ func (c *Client4) GetEmoji(emojiId string) (*Emoji, *Response) {
}
}
+// GetEmojiByName returns a custom emoji based on the name string.
+func (c *Client4) GetEmojiByName(name string) (*Emoji, *Response) {
+ if r, err := c.DoApiGet(c.GetEmojiByNameRoute(name), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return EmojiFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// GetEmojiImage returns the emoji image.
func (c *Client4) GetEmojiImage(emojiId string) ([]byte, *Response) {
if r, err := c.DoApiGet(c.GetEmojiRoute(emojiId)+"/image", ""); err != nil {
@@ -3031,6 +3419,27 @@ func (c *Client4) GetEmojiImage(emojiId string) ([]byte, *Response) {
}
}
+// SearchEmoji returns a list of emoji matching some search criteria.
+func (c *Client4) SearchEmoji(search *EmojiSearch) ([]*Emoji, *Response) {
+ if r, err := c.DoApiPost(c.GetEmojisRoute()+"/search", search.ToJson()); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return EmojiListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// AutocompleteEmoji returns a list of emoji starting with or matching name.
+func (c *Client4) AutocompleteEmoji(name string, etag string) ([]*Emoji, *Response) {
+ query := fmt.Sprintf("?name=%v", name)
+ if r, err := c.DoApiGet(c.GetEmojisRoute()+"/autocomplete"+query, ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return EmojiListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// Reaction Section
// SaveReaction saves an emoji reaction for a post. Returns the saved reaction if successful, otherwise an error will be returned.
@@ -3063,6 +3472,18 @@ func (c *Client4) DeleteReaction(reaction *Reaction) (bool, *Response) {
}
}
+// Timezone Section
+
+// GetSupportedTimezone returns a page of supported timezones on the system.
+func (c *Client4) GetSupportedTimezone() (SupportedTimezones, *Response) {
+ if r, err := c.DoApiGet(c.GetTimezonesRoute(), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return TimezonesFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// Open Graph Metadata Section
// OpenGraph return the open graph metadata for a particular url if the site have the metadata
@@ -3130,6 +3551,120 @@ func (c *Client4) CancelJob(jobId string) (bool, *Response) {
}
}
+// Roles Section
+
+// GetRole gets a single role by ID.
+func (c *Client4) GetRole(id string) (*Role, *Response) {
+ if r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/%v", id), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return RoleFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// GetRoleByName gets a single role by Name.
+func (c *Client4) GetRoleByName(name string) (*Role, *Response) {
+ if r, err := c.DoApiGet(c.GetRolesRoute()+fmt.Sprintf("/name/%v", name), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return RoleFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// GetRolesByNames returns a list of roles based on the provided role names.
+func (c *Client4) GetRolesByNames(roleNames []string) ([]*Role, *Response) {
+ if r, err := c.DoApiPost(c.GetRolesRoute()+"/names", ArrayToJson(roleNames)); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return RoleListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// PatchRole partially updates a role in the system. Any missing fields are not updated.
+func (c *Client4) PatchRole(roleId string, patch *RolePatch) (*Role, *Response) {
+ if r, err := c.DoApiPut(c.GetRolesRoute()+fmt.Sprintf("/%v/patch", roleId), patch.ToJson()); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return RoleFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// Schemes Section
+
+// CreateScheme creates a new Scheme.
+func (c *Client4) CreateScheme(scheme *Scheme) (*Scheme, *Response) {
+ if r, err := c.DoApiPost(c.GetSchemesRoute(), scheme.ToJson()); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return SchemeFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// GetScheme gets a single scheme by ID.
+func (c *Client4) GetScheme(id string) (*Scheme, *Response) {
+ if r, err := c.DoApiGet(c.GetSchemeRoute(id), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return SchemeFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// Get all schemes, sorted with the most recently created first, optionally filtered by scope.
+func (c *Client4) GetSchemes(scope string, page int, perPage int) ([]*Scheme, *Response) {
+ if r, err := c.DoApiGet(c.GetSchemesRoute()+fmt.Sprintf("?scope=%v&page=%v&per_page=%v", scope, page, perPage), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return SchemesFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// DeleteScheme deletes a single scheme by ID.
+func (c *Client4) DeleteScheme(id string) (bool, *Response) {
+ if r, err := c.DoApiDelete(c.GetSchemeRoute(id)); err != nil {
+ return false, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return CheckStatusOK(r), BuildResponse(r)
+ }
+}
+
+// PatchScheme partially updates a scheme in the system. Any missing fields are not updated.
+func (c *Client4) PatchScheme(id string, patch *SchemePatch) (*Scheme, *Response) {
+ if r, err := c.DoApiPut(c.GetSchemeRoute(id)+"/patch", patch.ToJson()); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return SchemeFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// Get the teams using this scheme, sorted alphabetically by display name.
+func (c *Client4) GetTeamsForScheme(schemeId string, page int, perPage int) ([]*Team, *Response) {
+ if r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/teams?page=%v&per_page=%v", page, perPage), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return TeamListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+// Get the channels using this scheme, sorted alphabetically by display name.
+func (c *Client4) GetChannelsForScheme(schemeId string, page int, perPage int) (ChannelList, *Response) {
+ if r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/channels?page=%v&per_page=%v", page, perPage), ""); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return *ChannelListFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// Plugin Section
// UploadPlugin takes an io.Reader stream pointing to the contents of a .tar.gz plugin.
@@ -3150,7 +3685,6 @@ func (c *Client4) UploadPlugin(file io.Reader) (*Manifest, *Response) {
rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetPluginsRoute(), body)
rq.Header.Set("Content-Type", writer.FormDataContentType())
- rq.Close = true
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@@ -3180,6 +3714,18 @@ func (c *Client4) GetPlugins() (*PluginsResponse, *Response) {
}
}
+// GetPluginStatuses will return the plugins installed on any server in the cluster, for reporting
+// to the administrator via the system console.
+// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
+func (c *Client4) GetPluginStatuses() (PluginStatuses, *Response) {
+ if r, err := c.DoApiGet(c.GetPluginsRoute(), "/statuses"); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return PluginStatusesFromJson(r.Body), BuildResponse(r)
+ }
+}
+
// RemovePlugin will deactivate and delete a plugin.
// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
func (c *Client4) RemovePlugin(id string) (bool, *Response) {
@@ -3204,8 +3750,8 @@ func (c *Client4) GetWebappPlugins() ([]*Manifest, *Response) {
// ActivatePlugin will activate an plugin installed.
// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
-func (c *Client4) ActivatePlugin(id string) (bool, *Response) {
- if r, err := c.DoApiPost(c.GetPluginRoute(id)+"/activate", ""); err != nil {
+func (c *Client4) EnablePlugin(id string) (bool, *Response) {
+ if r, err := c.DoApiPost(c.GetPluginRoute(id)+"/enable", ""); err != nil {
return false, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
@@ -3215,11 +3761,79 @@ func (c *Client4) ActivatePlugin(id string) (bool, *Response) {
// DeactivatePlugin will deactivate an active plugin.
// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
-func (c *Client4) DeactivatePlugin(id string) (bool, *Response) {
- if r, err := c.DoApiPost(c.GetPluginRoute(id)+"/deactivate", ""); err != nil {
+func (c *Client4) DisablePlugin(id string) (bool, *Response) {
+ if r, err := c.DoApiPost(c.GetPluginRoute(id)+"/disable", ""); err != nil {
+ return false, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return CheckStatusOK(r), BuildResponse(r)
+ }
+}
+
+// UpdateChannelScheme will update a channel's scheme.
+func (c *Client4) UpdateChannelScheme(channelId, schemeId string) (bool, *Response) {
+ sip := &SchemeIDPatch{SchemeID: &schemeId}
+ if r, err := c.DoApiPut(c.GetChannelSchemeRoute(channelId), sip.ToJson()); err != nil {
return false, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
}
+
+// UpdateTeamScheme will update a team's scheme.
+func (c *Client4) UpdateTeamScheme(teamId, schemeId string) (bool, *Response) {
+ sip := &SchemeIDPatch{SchemeID: &schemeId}
+ if r, err := c.DoApiPut(c.GetTeamSchemeRoute(teamId), sip.ToJson()); err != nil {
+ return false, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return CheckStatusOK(r), BuildResponse(r)
+ }
+}
+
+// GetRedirectLocation retrieves the value of the 'Location' header of an HTTP response for a given URL.
+func (c *Client4) GetRedirectLocation(urlParam, etag string) (string, *Response) {
+ url := fmt.Sprintf("%s?url=%s", c.GetRedirectLocationRoute(), url.QueryEscape(urlParam))
+ if r, err := c.DoApiGet(url, etag); err != nil {
+ return "", BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return MapFromJson(r.Body)["location"], BuildResponse(r)
+ }
+}
+
+func (c *Client4) RegisteTermsOfServiceAction(userId, termsOfServiceId string, accepted bool) (*bool, *Response) {
+ url := c.GetRegisterTermsOfServiceRoute(userId)
+ data := map[string]interface{}{"termsOfServiceId": termsOfServiceId, "accepted": accepted}
+
+ if r, err := c.DoApiPost(url, StringInterfaceToJson(data)); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return NewBool(CheckStatusOK(r)), BuildResponse(r)
+ }
+}
+
+func (c *Client4) GetTermsOfService(etag string) (*TermsOfService, *Response) {
+ url := c.GetTermsOfServiceRoute()
+
+ if r, err := c.DoApiGet(url, etag); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return TermsOfServiceFromJson(r.Body), BuildResponse(r)
+ }
+}
+
+func (c *Client4) CreateTermsOfService(text, userId string) (*TermsOfService, *Response) {
+ url := c.GetTermsOfServiceRoute()
+
+ data := map[string]string{"text": text}
+ if r, err := c.DoApiPost(url, MapToJson(data)); err != nil {
+ return nil, BuildErrorResponse(r, err)
+ } else {
+ defer closeBody(r)
+ return TermsOfServiceFromJson(r.Body), BuildResponse(r)
+ }
+}
diff --git a/vendor/github.com/mattermost/platform/model/cluster_discovery.go b/vendor/github.com/mattermost/mattermost-server/model/cluster_discovery.go
index 89e5fc95..796d3dda 100644
--- a/vendor/github.com/mattermost/platform/model/cluster_discovery.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/cluster_discovery.go
@@ -86,27 +86,27 @@ func FilterClusterDiscovery(vs []*ClusterDiscovery, f func(*ClusterDiscovery) bo
func (o *ClusterDiscovery) IsValid() *AppError {
if len(o.Id) != 26 {
- return NewAppError("Channel.IsValid", "model.channel.is_valid.id.app_error", nil, "", http.StatusBadRequest)
+ return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.ClusterName) == 0 {
- return NewAppError("ClusterDiscovery.IsValid", "ClusterName must be set", nil, "", http.StatusBadRequest)
+ return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.name.app_error", nil, "", http.StatusBadRequest)
}
if len(o.Type) == 0 {
- return NewAppError("ClusterDiscovery.IsValid", "Type must be set", nil, "", http.StatusBadRequest)
+ return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.type.app_error", nil, "", http.StatusBadRequest)
}
if len(o.Hostname) == 0 {
- return NewAppError("ClusterDiscovery.IsValid", "Hostname must be set", nil, "", http.StatusBadRequest)
+ return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.hostname.app_error", nil, "", http.StatusBadRequest)
}
if o.CreateAt == 0 {
- return NewAppError("ClusterDiscovery.IsValid", "CreateAt must be set", nil, "", http.StatusBadRequest)
+ return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.create_at.app_error", nil, "", http.StatusBadRequest)
}
if o.LastPingAt == 0 {
- return NewAppError("ClusterDiscovery.IsValid", "LastPingAt must be set", nil, "", http.StatusBadRequest)
+ return NewAppError("ClusterDiscovery.IsValid", "model.cluster.is_valid.last_ping_at.app_error", nil, "", http.StatusBadRequest)
}
return nil
diff --git a/vendor/github.com/mattermost/platform/model/cluster_info.go b/vendor/github.com/mattermost/mattermost-server/model/cluster_info.go
index c4f7e89a..46a3487a 100644
--- a/vendor/github.com/mattermost/platform/model/cluster_info.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/cluster_info.go
@@ -6,7 +6,6 @@ package model
import (
"encoding/json"
"io"
- "strings"
)
type ClusterInfo struct {
@@ -18,36 +17,19 @@ type ClusterInfo struct {
}
func (me *ClusterInfo) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func (me *ClusterInfo) Copy() *ClusterInfo {
- json := me.ToJson()
- return ClusterInfoFromJson(strings.NewReader(json))
+ b, _ := json.Marshal(me)
+ return string(b)
}
func ClusterInfoFromJson(data io.Reader) *ClusterInfo {
- decoder := json.NewDecoder(data)
- var me ClusterInfo
- err := decoder.Decode(&me)
- if err == nil {
- return &me
- } else {
- return nil
- }
+ var me *ClusterInfo
+ json.NewDecoder(data).Decode(&me)
+ return me
}
func ClusterInfosToJson(objmap []*ClusterInfo) string {
- if b, err := json.Marshal(objmap); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(objmap)
+ return string(b)
}
func ClusterInfosFromJson(data io.Reader) []*ClusterInfo {
diff --git a/vendor/github.com/mattermost/platform/model/cluster_message.go b/vendor/github.com/mattermost/mattermost-server/model/cluster_message.go
index a6dec2e7..d02da3ee 100644
--- a/vendor/github.com/mattermost/platform/model/cluster_message.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/cluster_message.go
@@ -21,6 +21,8 @@ const (
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL = "inv_channel"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER = "inv_user"
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user"
+ CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles"
+ CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES = "inv_schemes"
CLUSTER_SEND_BEST_EFFORT = "best_effort"
CLUSTER_SEND_RELIABLE = "reliable"
@@ -35,21 +37,12 @@ type ClusterMessage struct {
}
func (o *ClusterMessage) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ClusterMessageFromJson(data io.Reader) *ClusterMessage {
- decoder := json.NewDecoder(data)
- var o ClusterMessage
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *ClusterMessage
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/cluster_stats.go b/vendor/github.com/mattermost/mattermost-server/model/cluster_stats.go
index 879487c6..064f7b81 100644
--- a/vendor/github.com/mattermost/platform/model/cluster_stats.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/cluster_stats.go
@@ -16,21 +16,12 @@ type ClusterStats struct {
}
func (me *ClusterStats) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func ClusterStatsFromJson(data io.Reader) *ClusterStats {
- decoder := json.NewDecoder(data)
- var me ClusterStats
- err := decoder.Decode(&me)
- if err == nil {
- return &me
- } else {
- return nil
- }
+ var me *ClusterStats
+ json.NewDecoder(data).Decode(&me)
+ return me
}
diff --git a/vendor/github.com/mattermost/platform/model/command.go b/vendor/github.com/mattermost/mattermost-server/model/command.go
index 69da41c1..b23e5020 100644
--- a/vendor/github.com/mattermost/platform/model/command.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/command.go
@@ -38,43 +38,25 @@ type Command struct {
}
func (o *Command) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func CommandFromJson(data io.Reader) *Command {
- decoder := json.NewDecoder(data)
- var o Command
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Command
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func CommandListToJson(l []*Command) string {
- b, err := json.Marshal(l)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(l)
+ return string(b)
}
func CommandListFromJson(data io.Reader) []*Command {
- decoder := json.NewDecoder(data)
var o []*Command
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *Command) IsValid() *AppError {
diff --git a/vendor/github.com/mattermost/platform/model/command_args.go b/vendor/github.com/mattermost/mattermost-server/model/command_args.go
index 76b03a2f..4a635a1a 100644
--- a/vendor/github.com/mattermost/platform/model/command_args.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/command_args.go
@@ -23,21 +23,12 @@ type CommandArgs struct {
}
func (o *CommandArgs) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func CommandArgsFromJson(data io.Reader) *CommandArgs {
- decoder := json.NewDecoder(data)
- var o CommandArgs
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *CommandArgs
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/command_response.go b/vendor/github.com/mattermost/mattermost-server/model/command_response.go
index a3a171ce..1ed5286d 100644
--- a/vendor/github.com/mattermost/platform/model/command_response.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/command_response.go
@@ -8,6 +8,8 @@ import (
"io"
"io/ioutil"
"strings"
+
+ "github.com/mattermost/mattermost-server/utils/jsonutils"
)
const (
@@ -27,22 +29,18 @@ type CommandResponse struct {
}
func (o *CommandResponse) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
-func CommandResponseFromHTTPBody(contentType string, body io.Reader) *CommandResponse {
+func CommandResponseFromHTTPBody(contentType string, body io.Reader) (*CommandResponse, error) {
if strings.TrimSpace(strings.Split(contentType, ";")[0]) == "application/json" {
return CommandResponseFromJson(body)
}
if b, err := ioutil.ReadAll(body); err == nil {
- return CommandResponseFromPlainText(string(b))
+ return CommandResponseFromPlainText(string(b)), nil
}
- return nil
+ return nil, nil
}
func CommandResponseFromPlainText(text string) *CommandResponse {
@@ -51,15 +49,19 @@ func CommandResponseFromPlainText(text string) *CommandResponse {
}
}
-func CommandResponseFromJson(data io.Reader) *CommandResponse {
- decoder := json.NewDecoder(data)
- var o CommandResponse
+func CommandResponseFromJson(data io.Reader) (*CommandResponse, error) {
+ b, err := ioutil.ReadAll(data)
+ if err != nil {
+ return nil, err
+ }
- if err := decoder.Decode(&o); err != nil {
- return nil
+ var o CommandResponse
+ err = json.Unmarshal(b, &o)
+ if err != nil {
+ return nil, jsonutils.HumanizeJsonError(err, b)
}
o.Attachments = StringifySlackFieldValue(o.Attachments)
- return &o
+ return &o, nil
}
diff --git a/vendor/github.com/mattermost/platform/model/command_webhook.go b/vendor/github.com/mattermost/mattermost-server/model/command_webhook.go
index 0b00e00b..0b00e00b 100644
--- a/vendor/github.com/mattermost/platform/model/command_webhook.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/command_webhook.go
diff --git a/vendor/github.com/mattermost/platform/model/compliance.go b/vendor/github.com/mattermost/mattermost-server/model/compliance.go
index 3134ddba..5546b783 100644
--- a/vendor/github.com/mattermost/platform/model/compliance.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/compliance.go
@@ -38,12 +38,8 @@ type Compliance struct {
type Compliances []Compliance
func (o *Compliance) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func (me *Compliance) PreSave() {
@@ -56,7 +52,7 @@ func (me *Compliance) PreSave() {
}
me.Count = 0
- me.Emails = strings.ToLower(me.Emails)
+ me.Emails = NormalizeEmail(me.Emails)
me.Keywords = strings.ToLower(me.Keywords)
me.CreateAt = GetMillis()
@@ -103,14 +99,9 @@ func (me *Compliance) IsValid() *AppError {
}
func ComplianceFromJson(data io.Reader) *Compliance {
- decoder := json.NewDecoder(data)
- var o Compliance
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Compliance
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o Compliances) ToJson() string {
@@ -122,12 +113,7 @@ func (o Compliances) ToJson() string {
}
func CompliancesFromJson(data io.Reader) Compliances {
- decoder := json.NewDecoder(data)
var o Compliances
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/compliance_post.go b/vendor/github.com/mattermost/mattermost-server/model/compliance_post.go
index 3751c586..75e8de1f 100644
--- a/vendor/github.com/mattermost/platform/model/compliance_post.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/compliance_post.go
@@ -17,6 +17,7 @@ type CompliancePost struct {
// From Channel
ChannelName string
ChannelDisplayName string
+ ChannelType string
// From User
UserUsername string
@@ -45,6 +46,7 @@ func CompliancePostHeader() []string {
"ChannelName",
"ChannelDisplayName",
+ "ChannelType",
"UserUsername",
"UserEmail",
@@ -92,6 +94,7 @@ func (me *CompliancePost) Row() []string {
cleanComplianceStrings(me.ChannelName),
cleanComplianceStrings(me.ChannelDisplayName),
+ cleanComplianceStrings(me.ChannelType),
cleanComplianceStrings(me.UserUsername),
cleanComplianceStrings(me.UserEmail),
diff --git a/vendor/github.com/mattermost/platform/model/config.go b/vendor/github.com/mattermost/mattermost-server/model/config.go
index fb34d1a0..d59b8d6d 100644
--- a/vendor/github.com/mattermost/platform/model/config.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/config.go
@@ -6,8 +6,12 @@ package model
import (
"encoding/json"
"io"
+ "math"
+ "net"
"net/http"
"net/url"
+ "regexp"
+ "strconv"
"strings"
"time"
)
@@ -21,6 +25,7 @@ const (
IMAGE_DRIVER_LOCAL = "local"
IMAGE_DRIVER_S3 = "amazons3"
+ DATABASE_DRIVER_SQLITE = "sqlite3"
DATABASE_DRIVER_MYSQL = "mysql"
DATABASE_DRIVER_POSTGRES = "postgres"
@@ -35,10 +40,6 @@ const (
SERVICE_GOOGLE = "google"
SERVICE_OFFICE365 = "office365"
- WEBSERVER_MODE_REGULAR = "regular"
- WEBSERVER_MODE_GZIP = "gzip"
- WEBSERVER_MODE_DISABLED = "disabled"
-
GENERIC_NO_CHANNEL_NOTIFICATION = "generic_no_channel"
GENERIC_NOTIFICATION = "generic"
FULL_NOTIFICATION = "full"
@@ -69,6 +70,10 @@ const (
ALLOW_EDIT_POST_NEVER = "never"
ALLOW_EDIT_POST_TIME_LIMIT = "time_limit"
+ GROUP_UNREAD_CHANNELS_DISABLED = "disabled"
+ GROUP_UNREAD_CHANNELS_DEFAULT_ON = "default_on"
+ GROUP_UNREAD_CHANNELS_DEFAULT_OFF = "default_off"
+
EMAIL_BATCHING_BUFFER_SIZE = 256
EMAIL_BATCHING_INTERVAL = 30
@@ -85,6 +90,8 @@ const (
SERVICE_SETTINGS_DEFAULT_MAX_LOGIN_ATTEMPTS = 10
SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM = ""
SERVICE_SETTINGS_DEFAULT_LISTEN_AND_ADDRESS = ":8065"
+ SERVICE_SETTINGS_DEFAULT_GFYCAT_API_KEY = "2_KtH_W5"
+ SERVICE_SETTINGS_DEFAULT_GFYCAT_API_SECRET = "3wLVZPiswc3DnaiaFoLkDvB4X0IV6CpMkj4tf2inJRsBY6-FnkT08zGmppWFgeof"
TEAM_SETTINGS_DEFAULT_MAX_USERS_PER_TEAM = 50
TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT = ""
@@ -93,17 +100,16 @@ const (
SQL_SETTINGS_DEFAULT_DATA_SOURCE = "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8&readTimeout=30s&writeTimeout=30s"
+ FILE_SETTINGS_DEFAULT_DIRECTORY = "./data/"
+
EMAIL_SETTINGS_DEFAULT_FEEDBACK_ORGANIZATION = ""
- SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/"
- SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/"
- SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/"
- SUPPORT_SETTINGS_DEFAULT_HELP_LINK = "https://about.mattermost.com/default-help/"
- SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/"
- SUPPORT_SETTINGS_DEFAULT_ADMINISTRATORS_GUIDE_LINK = "https://about.mattermost.com/administrators-guide/"
- SUPPORT_SETTINGS_DEFAULT_TROUBLESHOOTING_FORUM_LINK = "https://about.mattermost.com/troubleshooting-forum/"
- SUPPORT_SETTINGS_DEFAULT_COMMERCIAL_SUPPORT_LINK = "https://about.mattermost.com/commercial-support/"
- SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
+ SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK = "https://about.mattermost.com/default-terms/"
+ SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK = "https://about.mattermost.com/default-privacy-policy/"
+ SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK = "https://about.mattermost.com/default-about/"
+ SUPPORT_SETTINGS_DEFAULT_HELP_LINK = "https://about.mattermost.com/default-help/"
+ SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK = "https://about.mattermost.com/default-report-a-problem/"
+ SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL = "feedback@mattermost.com"
LDAP_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
LDAP_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
@@ -114,6 +120,7 @@ const (
LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE = ""
LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME = ""
+ SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE = ""
SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE = ""
SAML_SETTINGS_DEFAULT_LAST_NAME_ATTRIBUTE = ""
SAML_SETTINGS_DEFAULT_EMAIL_ATTRIBUTE = ""
@@ -154,72 +161,113 @@ const (
PLUGIN_SETTINGS_DEFAULT_DIRECTORY = "./plugins"
PLUGIN_SETTINGS_DEFAULT_CLIENT_DIRECTORY = "./client/plugins"
+
+ TIMEZONE_SETTINGS_DEFAULT_SUPPORTED_TIMEZONES_PATH = "timezones.json"
+
+ COMPLIANCE_EXPORT_TYPE_CSV = "csv"
+ COMPLIANCE_EXPORT_TYPE_ACTIANCE = "actiance"
+ COMPLIANCE_EXPORT_TYPE_GLOBALRELAY = "globalrelay"
+ GLOBALRELAY_CUSTOMER_TYPE_A9 = "A9"
+ GLOBALRELAY_CUSTOMER_TYPE_A10 = "A10"
+
+ CLIENT_SIDE_CERT_CHECK_PRIMARY_AUTH = "primary"
+ CLIENT_SIDE_CERT_CHECK_SECONDARY_AUTH = "secondary"
)
type ServiceSettings struct {
- SiteURL *string
- LicenseFileLocation *string
- ListenAddress *string
- ConnectionSecurity *string
- TLSCertFile *string
- TLSKeyFile *string
- UseLetsEncrypt *bool
- LetsEncryptCertificateCacheFile *string
- Forward80To443 *bool
- ReadTimeout *int
- WriteTimeout *int
- MaximumLoginAttempts *int
- GoroutineHealthThreshold *int
- GoogleDeveloperKey string
- EnableOAuthServiceProvider bool
- EnableIncomingWebhooks bool
- EnableOutgoingWebhooks bool
- EnableCommands *bool
- EnableOnlyAdminIntegrations *bool
- EnablePostUsernameOverride bool
- EnablePostIconOverride bool
- EnableAPIv3 *bool
- EnableLinkPreviews *bool
- EnableTesting bool
- EnableDeveloper *bool
- EnableSecurityFixAlert *bool
- EnableInsecureOutgoingConnections *bool
- AllowedUntrustedInternalConnections *string
- EnableMultifactorAuthentication *bool
- EnforceMultifactorAuthentication *bool
- EnableUserAccessTokens *bool
- AllowCorsFrom *string
- SessionLengthWebInDays *int
- SessionLengthMobileInDays *int
- SessionLengthSSOInDays *int
- SessionCacheInMinutes *int
- SessionIdleTimeoutInMinutes *int
- WebsocketSecurePort *int
- WebsocketPort *int
- WebserverMode *string
- EnableCustomEmoji *bool
- EnableEmojiPicker *bool
- RestrictCustomEmojiCreation *string
- RestrictPostDelete *string
- AllowEditPost *string
- PostEditTimeLimit *int
- TimeBetweenUserTypingUpdatesMilliseconds *int64
- EnablePostSearch *bool
- EnableUserTypingMessages *bool
- EnableChannelViewedMessages *bool
- EnableUserStatuses *bool
- ExperimentalEnableAuthenticationTransfer *bool
- ClusterLogTimeoutMilliseconds *int
- CloseUnusedDirectMessages *bool
- EnablePreviewFeatures *bool
- EnableTutorial *bool
+ SiteURL *string
+ WebsocketURL *string
+ LicenseFileLocation *string
+ ListenAddress *string
+ ConnectionSecurity *string
+ TLSCertFile *string
+ TLSKeyFile *string
+ UseLetsEncrypt *bool
+ LetsEncryptCertificateCacheFile *string
+ Forward80To443 *bool
+ ReadTimeout *int
+ WriteTimeout *int
+ MaximumLoginAttempts *int
+ GoroutineHealthThreshold *int
+ GoogleDeveloperKey string
+ EnableOAuthServiceProvider bool
+ EnableIncomingWebhooks bool
+ EnableOutgoingWebhooks bool
+ EnableCommands *bool
+ EnableOnlyAdminIntegrations *bool
+ EnablePostUsernameOverride bool
+ EnablePostIconOverride bool
+ EnableLinkPreviews *bool
+ EnableTesting bool
+ EnableDeveloper *bool
+ EnableSecurityFixAlert *bool
+ EnableInsecureOutgoingConnections *bool
+ AllowedUntrustedInternalConnections *string
+ EnableMultifactorAuthentication *bool
+ EnforceMultifactorAuthentication *bool
+ EnableUserAccessTokens *bool
+ AllowCorsFrom *string
+ CorsExposedHeaders *string
+ CorsAllowCredentials *bool
+ CorsDebug *bool
+ AllowCookiesForSubdomains *bool
+ SessionLengthWebInDays *int
+ SessionLengthMobileInDays *int
+ SessionLengthSSOInDays *int
+ SessionCacheInMinutes *int
+ SessionIdleTimeoutInMinutes *int
+ WebsocketSecurePort *int
+ WebsocketPort *int
+ WebserverMode *string
+ EnableCustomEmoji *bool
+ EnableEmojiPicker *bool
+ EnableGifPicker *bool
+ GfycatApiKey *string
+ GfycatApiSecret *string
+ RestrictCustomEmojiCreation *string
+ RestrictPostDelete *string
+ AllowEditPost *string
+ PostEditTimeLimit *int
+ TimeBetweenUserTypingUpdatesMilliseconds *int64
+ EnablePostSearch *bool
+ EnableUserTypingMessages *bool
+ EnableChannelViewedMessages *bool
+ EnableUserStatuses *bool
+ ExperimentalEnableAuthenticationTransfer *bool
+ ClusterLogTimeoutMilliseconds *int
+ CloseUnusedDirectMessages *bool
+ EnablePreviewFeatures *bool
+ EnableTutorial *bool
+ ExperimentalEnableDefaultChannelLeaveJoinMessages *bool
+ ExperimentalGroupUnreadChannels *string
+ ExperimentalChannelOrganization *bool
+ ImageProxyType *string
+ ImageProxyURL *string
+ ImageProxyOptions *string
+ EnableAPITeamDeletion *bool
+ ExperimentalEnableHardenedMode *bool
+ ExperimentalLimitClientConfig *bool
+ EnableEmailInvitations *bool
}
func (s *ServiceSettings) SetDefaults() {
+ if s.EnableEmailInvitations == nil {
+ // If the site URL is also not present then assume this is a clean install
+ if s.SiteURL == nil {
+ s.EnableEmailInvitations = NewBool(false)
+ } else {
+ s.EnableEmailInvitations = NewBool(true)
+ }
+ }
+
if s.SiteURL == nil {
s.SiteURL = NewString(SERVICE_SETTINGS_DEFAULT_SITE_URL)
}
+ if s.WebsocketURL == nil {
+ s.WebsocketURL = NewString("")
+ }
+
if s.LicenseFileLocation == nil {
s.LicenseFileLocation = NewString("")
}
@@ -228,10 +276,6 @@ func (s *ServiceSettings) SetDefaults() {
s.ListenAddress = NewString(SERVICE_SETTINGS_DEFAULT_LISTEN_AND_ADDRESS)
}
- if s.EnableAPIv3 == nil {
- s.EnableAPIv3 = NewBool(true)
- }
-
if s.EnableLinkPreviews == nil {
s.EnableLinkPreviews = NewBool(false)
}
@@ -249,7 +293,7 @@ func (s *ServiceSettings) SetDefaults() {
}
if s.AllowedUntrustedInternalConnections == nil {
- s.AllowedUntrustedInternalConnections = new(string)
+ s.AllowedUntrustedInternalConnections = NewString("")
}
if s.EnableMultifactorAuthentication == nil {
@@ -376,6 +420,22 @@ func (s *ServiceSettings) SetDefaults() {
s.AllowCorsFrom = NewString(SERVICE_SETTINGS_DEFAULT_ALLOW_CORS_FROM)
}
+ if s.CorsExposedHeaders == nil {
+ s.CorsExposedHeaders = NewString("")
+ }
+
+ if s.CorsAllowCredentials == nil {
+ s.CorsAllowCredentials = NewBool(false)
+ }
+
+ if s.CorsDebug == nil {
+ s.CorsDebug = NewBool(false)
+ }
+
+ if s.AllowCookiesForSubdomains == nil {
+ s.AllowCookiesForSubdomains = NewBool(false)
+ }
+
if s.WebserverMode == nil {
s.WebserverMode = NewString("gzip")
} else if *s.WebserverMode == "regular" {
@@ -390,6 +450,18 @@ func (s *ServiceSettings) SetDefaults() {
s.EnableEmojiPicker = NewBool(true)
}
+ if s.EnableGifPicker == nil {
+ s.EnableGifPicker = NewBool(false)
+ }
+
+ if s.GfycatApiKey == nil || *s.GfycatApiKey == "" {
+ s.GfycatApiKey = NewString(SERVICE_SETTINGS_DEFAULT_GFYCAT_API_KEY)
+ }
+
+ if s.GfycatApiSecret == nil || *s.GfycatApiSecret == "" {
+ s.GfycatApiSecret = NewString(SERVICE_SETTINGS_DEFAULT_GFYCAT_API_SECRET)
+ }
+
if s.RestrictCustomEmojiCreation == nil {
s.RestrictCustomEmojiCreation = NewString(RESTRICT_EMOJI_CREATION_ALL)
}
@@ -407,23 +479,67 @@ func (s *ServiceSettings) SetDefaults() {
}
if s.PostEditTimeLimit == nil {
- s.PostEditTimeLimit = NewInt(300)
+ s.PostEditTimeLimit = NewInt(-1)
}
if s.EnablePreviewFeatures == nil {
s.EnablePreviewFeatures = NewBool(true)
}
+
+ if s.ExperimentalEnableDefaultChannelLeaveJoinMessages == nil {
+ s.ExperimentalEnableDefaultChannelLeaveJoinMessages = NewBool(true)
+ }
+
+ if s.ExperimentalGroupUnreadChannels == nil {
+ s.ExperimentalGroupUnreadChannels = NewString(GROUP_UNREAD_CHANNELS_DISABLED)
+ } else if *s.ExperimentalGroupUnreadChannels == "0" {
+ s.ExperimentalGroupUnreadChannels = NewString(GROUP_UNREAD_CHANNELS_DISABLED)
+ } else if *s.ExperimentalGroupUnreadChannels == "1" {
+ s.ExperimentalGroupUnreadChannels = NewString(GROUP_UNREAD_CHANNELS_DEFAULT_ON)
+ }
+
+ if s.ExperimentalChannelOrganization == nil {
+ experimentalUnreadEnabled := *s.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DISABLED
+ s.ExperimentalChannelOrganization = NewBool(experimentalUnreadEnabled)
+ }
+
+ if s.ImageProxyType == nil {
+ s.ImageProxyType = NewString("")
+ }
+
+ if s.ImageProxyURL == nil {
+ s.ImageProxyURL = NewString("")
+ }
+
+ if s.ImageProxyOptions == nil {
+ s.ImageProxyOptions = NewString("")
+ }
+
+ if s.EnableAPITeamDeletion == nil {
+ s.EnableAPITeamDeletion = NewBool(false)
+ }
+
+ if s.ExperimentalEnableHardenedMode == nil {
+ s.ExperimentalEnableHardenedMode = NewBool(false)
+ }
+
+ if s.ExperimentalLimitClientConfig == nil {
+ s.ExperimentalLimitClientConfig = NewBool(false)
+ }
}
type ClusterSettings struct {
- Enable *bool
- ClusterName *string
- OverrideHostname *string
- UseIpAddress *bool
- UseExperimentalGossip *bool
- ReadOnlyConfig *bool
- GossipPort *int
- StreamingPort *int
+ Enable *bool
+ ClusterName *string
+ OverrideHostname *string
+ UseIpAddress *bool
+ UseExperimentalGossip *bool
+ ReadOnlyConfig *bool
+ GossipPort *int
+ StreamingPort *int
+ MaxIdleConns *int
+ MaxIdleConnsPerHost *int
+ IdleConnTimeoutMilliseconds *int
}
func (s *ClusterSettings) SetDefaults() {
@@ -458,6 +574,18 @@ func (s *ClusterSettings) SetDefaults() {
if s.StreamingPort == nil {
s.StreamingPort = NewInt(8075)
}
+
+ if s.MaxIdleConns == nil {
+ s.MaxIdleConns = NewInt(100)
+ }
+
+ if s.MaxIdleConnsPerHost == nil {
+ s.MaxIdleConnsPerHost = NewInt(128)
+ }
+
+ if s.IdleConnTimeoutMilliseconds == nil {
+ s.IdleConnTimeoutMilliseconds = NewInt(90000)
+ }
}
type MetricsSettings struct {
@@ -480,6 +608,21 @@ func (s *MetricsSettings) SetDefaults() {
}
}
+type ExperimentalSettings struct {
+ ClientSideCertEnable *bool
+ ClientSideCertCheck *string
+}
+
+func (s *ExperimentalSettings) SetDefaults() {
+ if s.ClientSideCertEnable == nil {
+ s.ClientSideCertEnable = NewBool(false)
+ }
+
+ if s.ClientSideCertCheck == nil {
+ s.ClientSideCertCheck = NewString(CLIENT_SIDE_CERT_CHECK_SECONDARY_AUTH)
+ }
+}
+
type AnalyticsSettings struct {
MaxUsersForStatistics *int
}
@@ -501,15 +644,17 @@ type SSOSettings struct {
}
type SqlSettings struct {
- DriverName *string
- DataSource *string
- DataSourceReplicas []string
- DataSourceSearchReplicas []string
- MaxIdleConns *int
- MaxOpenConns *int
- Trace bool
- AtRestEncryptKey string
- QueryTimeout *int
+ DriverName *string
+ DataSource *string
+ DataSourceReplicas []string
+ DataSourceSearchReplicas []string
+ MaxIdleConns *int
+ ConnMaxLifetimeMilliseconds *int
+ MaxOpenConns *int
+ Trace bool
+ AtRestEncryptKey string
+ QueryTimeout *int
+ EnablePublicChannelsMaterialization *bool
}
func (s *SqlSettings) SetDefaults() {
@@ -533,17 +678,26 @@ func (s *SqlSettings) SetDefaults() {
s.MaxOpenConns = NewInt(300)
}
+ if s.ConnMaxLifetimeMilliseconds == nil {
+ s.ConnMaxLifetimeMilliseconds = NewInt(3600000)
+ }
+
if s.QueryTimeout == nil {
s.QueryTimeout = NewInt(30)
}
+
+ if s.EnablePublicChannelsMaterialization == nil {
+ s.EnablePublicChannelsMaterialization = NewBool(true)
+ }
}
type LogSettings struct {
EnableConsole bool
ConsoleLevel string
+ ConsoleJson *bool
EnableFile bool
FileLevel string
- FileFormat string
+ FileJson *bool
FileLocation string
EnableWebhookDebugging bool
EnableDiagnostics *bool
@@ -553,6 +707,14 @@ func (s *LogSettings) SetDefaults() {
if s.EnableDiagnostics == nil {
s.EnableDiagnostics = NewBool(true)
}
+
+ if s.ConsoleJson == nil {
+ s.ConsoleJson = NewBool(true)
+ }
+
+ if s.FileJson == nil {
+ s.FileJson = NewBool(true)
+ }
}
type PasswordSettings struct {
@@ -654,12 +816,12 @@ func (s *FileSettings) SetDefaults() {
}
if s.InitialFont == "" {
- // Defaults to "luximbi.ttf"
- s.InitialFont = "luximbi.ttf"
+ // Defaults to "nunito-bold.ttf"
+ s.InitialFont = "nunito-bold.ttf"
}
if s.Directory == "" {
- s.Directory = "./data/"
+ s.Directory = FILE_SETTINGS_DEFAULT_DIRECTORY
}
}
@@ -686,6 +848,7 @@ type EmailSettings struct {
EnableEmailBatching *bool
EmailBatchingBufferSize *int
EmailBatchingInterval *int
+ EnablePreviewModeBanner *bool
SkipServerCertificateVerification *bool
EmailNotificationContentsType *string
LoginButtonColor *string
@@ -738,6 +901,10 @@ func (s *EmailSettings) SetDefaults() {
s.EmailBatchingInterval = NewInt(EMAIL_BATCHING_INTERVAL)
}
+ if s.EnablePreviewModeBanner == nil {
+ s.EnablePreviewModeBanner = NewBool(true)
+ }
+
if s.EnableSMTPAuth == nil {
s.EnableSMTPAuth = new(bool)
if s.ConnectionSecurity == CONN_SECURITY_NONE {
@@ -772,12 +939,28 @@ func (s *EmailSettings) SetDefaults() {
}
}
+type ExtensionSettings struct {
+ EnableExperimentalExtensions *bool
+ AllowedExtensionsIDs []string
+}
+
+func (s *ExtensionSettings) SetDefaults() {
+ if s.EnableExperimentalExtensions == nil {
+ s.EnableExperimentalExtensions = NewBool(false)
+ }
+
+ if s.AllowedExtensionsIDs == nil {
+ s.AllowedExtensionsIDs = []string{}
+ }
+}
+
type RateLimitSettings struct {
Enable *bool
PerSec *int
MaxBurst *int
MemoryStoreSize *int
- VaryByRemoteAddr bool
+ VaryByRemoteAddr *bool
+ VaryByUser *bool
VaryByHeader string
}
@@ -797,6 +980,14 @@ func (s *RateLimitSettings) SetDefaults() {
if s.MemoryStoreSize == nil {
s.MemoryStoreSize = NewInt(10000)
}
+
+ if s.VaryByRemoteAddr == nil {
+ s.VaryByRemoteAddr = NewBool(true)
+ }
+
+ if s.VaryByUser == nil {
+ s.VaryByUser = NewBool(false)
+ }
}
type PrivacySettings struct {
@@ -805,12 +996,13 @@ type PrivacySettings struct {
}
type SupportSettings struct {
- TermsOfServiceLink *string
- PrivacyPolicyLink *string
- AboutLink *string
- HelpLink *string
- ReportAProblemLink *string
- SupportEmail *string
+ TermsOfServiceLink *string
+ PrivacyPolicyLink *string
+ AboutLink *string
+ HelpLink *string
+ ReportAProblemLink *string
+ SupportEmail *string
+ CustomTermsOfServiceEnabled *bool
}
func (s *SupportSettings) SetDefaults() {
@@ -857,6 +1049,10 @@ func (s *SupportSettings) SetDefaults() {
if s.SupportEmail == nil {
s.SupportEmail = NewString(SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL)
}
+
+ if s.CustomTermsOfServiceEnabled == nil {
+ s.CustomTermsOfServiceEnabled = NewBool(false)
+ }
}
type AnnouncementSettings struct {
@@ -917,9 +1113,10 @@ func (s *ThemeSettings) SetDefaults() {
type TeamSettings struct {
SiteName string
MaxUsersPerTeam *int
- EnableTeamCreation bool
- EnableUserCreation bool
+ EnableTeamCreation *bool
+ EnableUserCreation *bool
EnableOpenServer *bool
+ EnableUserDeactivation *bool
RestrictCreationToDomains string
EnableCustomBrand *bool
CustomBrandText *string
@@ -939,8 +1136,12 @@ type TeamSettings struct {
MaxNotificationsPerChannel *int64
EnableConfirmNotificationsToChannel *bool
TeammateNameDisplay *string
+ ExperimentalViewArchivedChannels *bool
+ ExperimentalEnableAutomaticReplies *bool
+ ExperimentalHideTownSquareinLHS *bool
ExperimentalTownSquareIsReadOnly *bool
ExperimentalPrimaryTeam *string
+ ExperimentalDefaultChannels []string
}
func (s *TeamSettings) SetDefaults() {
@@ -952,6 +1153,10 @@ func (s *TeamSettings) SetDefaults() {
s.EnableCustomBrand = NewBool(false)
}
+ if s.EnableUserDeactivation == nil {
+ s.EnableUserDeactivation = NewBool(false)
+ }
+
if s.CustomBrandText == nil {
s.CustomBrandText = NewString(TEAM_SETTINGS_DEFAULT_CUSTOM_BRAND_TEXT)
}
@@ -1033,6 +1238,14 @@ func (s *TeamSettings) SetDefaults() {
s.EnableConfirmNotificationsToChannel = NewBool(true)
}
+ if s.ExperimentalEnableAutomaticReplies == nil {
+ s.ExperimentalEnableAutomaticReplies = NewBool(false)
+ }
+
+ if s.ExperimentalHideTownSquareinLHS == nil {
+ s.ExperimentalHideTownSquareinLHS = NewBool(false)
+ }
+
if s.ExperimentalTownSquareIsReadOnly == nil {
s.ExperimentalTownSquareIsReadOnly = NewBool(false)
}
@@ -1040,6 +1253,22 @@ func (s *TeamSettings) SetDefaults() {
if s.ExperimentalPrimaryTeam == nil {
s.ExperimentalPrimaryTeam = NewString("")
}
+
+ if s.ExperimentalDefaultChannels == nil {
+ s.ExperimentalDefaultChannels = []string{}
+ }
+
+ if s.EnableTeamCreation == nil {
+ s.EnableTeamCreation = NewBool(true)
+ }
+
+ if s.EnableUserCreation == nil {
+ s.EnableUserCreation = NewBool(true)
+ }
+
+ if s.ExperimentalViewArchivedChannels == nil {
+ s.ExperimentalViewArchivedChannels = NewBool(false)
+ }
}
type ClientRequirements struct {
@@ -1073,8 +1302,9 @@ type LdapSettings struct {
NicknameAttribute *string
IdAttribute *string
PositionAttribute *string
+ LoginIdAttribute *string
- // Syncronization
+ // Synchronization
SyncIntervalMinutes *int
// Advanced
@@ -1156,6 +1386,12 @@ func (s *LdapSettings) SetDefaults() {
s.PositionAttribute = NewString(LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE)
}
+ // For those upgrading to the version when LoginIdAttribute was added
+ // they need IdAttribute == LoginIdAttribute not to break
+ if s.LoginIdAttribute == nil {
+ s.LoginIdAttribute = s.IdAttribute
+ }
+
if s.SyncIntervalMinutes == nil {
s.SyncIntervalMinutes = NewInt(60)
}
@@ -1231,8 +1467,9 @@ func (s *LocalizationSettings) SetDefaults() {
type SamlSettings struct {
// Basic
- Enable *bool
- EnableSyncWithLdap *bool
+ Enable *bool
+ EnableSyncWithLdap *bool
+ EnableSyncWithLdapIncludeAuth *bool
Verify *bool
Encrypt *bool
@@ -1241,11 +1478,15 @@ type SamlSettings struct {
IdpDescriptorUrl *string
AssertionConsumerServiceURL *string
+ ScopingIDPProviderId *string
+ ScopingIDPName *string
+
IdpCertificateFile *string
PublicCertificateFile *string
PrivateKeyFile *string
// User Mapping
+ IdAttribute *string
FirstNameAttribute *string
LastNameAttribute *string
EmailAttribute *string
@@ -1270,6 +1511,10 @@ func (s *SamlSettings) SetDefaults() {
s.EnableSyncWithLdap = NewBool(false)
}
+ if s.EnableSyncWithLdapIncludeAuth == nil {
+ s.EnableSyncWithLdapIncludeAuth = NewBool(false)
+ }
+
if s.Verify == nil {
s.Verify = NewBool(true)
}
@@ -1302,10 +1547,22 @@ func (s *SamlSettings) SetDefaults() {
s.AssertionConsumerServiceURL = NewString("")
}
+ if s.ScopingIDPProviderId == nil {
+ s.ScopingIDPProviderId = NewString("")
+ }
+
+ if s.ScopingIDPName == nil {
+ s.ScopingIDPName = NewString("")
+ }
+
if s.LoginButtonText == nil || *s.LoginButtonText == "" {
s.LoginButtonText = NewString(USER_AUTH_SERVICE_SAML_TEXT)
}
+ if s.IdAttribute == nil {
+ s.IdAttribute = NewString(SAML_SETTINGS_DEFAULT_ID_ATTRIBUTE)
+ }
+
if s.FirstNameAttribute == nil {
s.FirstNameAttribute = NewString(SAML_SETTINGS_DEFAULT_FIRST_NAME_ATTRIBUTE)
}
@@ -1541,7 +1798,7 @@ type PluginSettings struct {
EnableUploads *bool
Directory *string
ClientDirectory *string
- Plugins map[string]interface{}
+ Plugins map[string]map[string]interface{}
PluginStates map[string]*PluginState
}
@@ -1571,7 +1828,7 @@ func (s *PluginSettings) SetDefaults() {
}
if s.Plugins == nil {
- s.Plugins = make(map[string]interface{})
+ s.Plugins = make(map[string]map[string]interface{})
}
if s.PluginStates == nil {
@@ -1579,11 +1836,37 @@ func (s *PluginSettings) SetDefaults() {
}
}
+type GlobalRelayMessageExportSettings struct {
+ CustomerType *string // must be either A9 or A10, dictates SMTP server url
+ SmtpUsername *string
+ SmtpPassword *string
+ EmailAddress *string // the address to send messages to
+}
+
+func (s *GlobalRelayMessageExportSettings) SetDefaults() {
+ if s.CustomerType == nil {
+ s.CustomerType = NewString(GLOBALRELAY_CUSTOMER_TYPE_A9)
+ }
+ if s.SmtpUsername == nil {
+ s.SmtpUsername = NewString("")
+ }
+ if s.SmtpPassword == nil {
+ s.SmtpPassword = NewString("")
+ }
+ if s.EmailAddress == nil {
+ s.EmailAddress = NewString("")
+ }
+}
+
type MessageExportSettings struct {
EnableExport *bool
+ ExportFormat *string
DailyRunTime *string
ExportFromTimestamp *int64
BatchSize *int
+
+ // formatter-specific settings - these are only expected to be non-nil if ExportFormat is set to the associated format
+ GlobalRelaySettings *GlobalRelayMessageExportSettings
}
func (s *MessageExportSettings) SetDefaults() {
@@ -1591,6 +1874,10 @@ func (s *MessageExportSettings) SetDefaults() {
s.EnableExport = NewBool(false)
}
+ if s.ExportFormat == nil {
+ s.ExportFormat = NewString(COMPLIANCE_EXPORT_TYPE_ACTIANCE)
+ }
+
if s.DailyRunTime == nil {
s.DailyRunTime = NewString("01:00")
}
@@ -1610,6 +1897,37 @@ func (s *MessageExportSettings) SetDefaults() {
if s.BatchSize == nil {
s.BatchSize = NewInt(10000)
}
+
+ if s.GlobalRelaySettings == nil {
+ s.GlobalRelaySettings = &GlobalRelayMessageExportSettings{}
+ }
+ s.GlobalRelaySettings.SetDefaults()
+}
+
+type DisplaySettings struct {
+ CustomUrlSchemes *[]string
+ ExperimentalTimezone *bool
+}
+
+func (s *DisplaySettings) SetDefaults() {
+ if s.CustomUrlSchemes == nil {
+ customUrlSchemes := []string{}
+ s.CustomUrlSchemes = &customUrlSchemes
+ }
+
+ if s.ExperimentalTimezone == nil {
+ s.ExperimentalTimezone = NewBool(false)
+ }
+}
+
+type TimezoneSettings struct {
+ SupportedTimezonesPath *string
+}
+
+func (s *TimezoneSettings) SetDefaults() {
+ if s.SupportedTimezonesPath == nil {
+ s.SupportedTimezonesPath = NewString(TIMEZONE_SETTINGS_DEFAULT_SUPPORTED_TIMEZONES_PATH)
+ }
}
type ConfigFunc func() *Config
@@ -1623,6 +1941,7 @@ type Config struct {
PasswordSettings PasswordSettings
FileSettings FileSettings
EmailSettings EmailSettings
+ ExtensionSettings ExtensionSettings
RateLimitSettings RateLimitSettings
PrivacySettings PrivacySettings
SupportSettings SupportSettings
@@ -1638,6 +1957,7 @@ type Config struct {
NativeAppSettings NativeAppSettings
ClusterSettings ClusterSettings
MetricsSettings MetricsSettings
+ ExperimentalSettings ExperimentalSettings
AnalyticsSettings AnalyticsSettings
WebrtcSettings WebrtcSettings
ElasticsearchSettings ElasticsearchSettings
@@ -1645,6 +1965,8 @@ type Config struct {
MessageExportSettings MessageExportSettings
JobSettings JobSettings
PluginSettings PluginSettings
+ DisplaySettings DisplaySettings
+ TimezoneSettings TimezoneSettings
}
func (o *Config) Clone() *Config {
@@ -1656,12 +1978,8 @@ func (o *Config) Clone() *Config {
}
func (o *Config) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func (o *Config) GetSSOService(service string) *SSOSettings {
@@ -1678,14 +1996,9 @@ func (o *Config) GetSSOService(service string) *SSOSettings {
}
func ConfigFromJson(data io.Reader) *Config {
- decoder := json.NewDecoder(data)
- var o Config
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Config
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *Config) SetDefaults() {
@@ -1707,6 +2020,7 @@ func (o *Config) SetDefaults() {
o.PasswordSettings.SetDefaults()
o.TeamSettings.SetDefaults()
o.MetricsSettings.SetDefaults()
+ o.ExperimentalSettings.SetDefaults()
o.SupportSettings.SetDefaults()
o.AnnouncementSettings.SetDefaults()
o.ThemeSettings.SetDefaults()
@@ -1723,6 +2037,9 @@ func (o *Config) SetDefaults() {
o.JobSettings.SetDefaults()
o.WebrtcSettings.SetDefaults()
o.MessageExportSettings.SetDefaults()
+ o.TimezoneSettings.SetDefaults()
+ o.DisplaySettings.SetDefaults()
+ o.ExtensionSettings.SetDefaults()
}
func (o *Config) IsValid() *AppError {
@@ -1734,6 +2051,10 @@ func (o *Config) IsValid() *AppError {
return NewAppError("Config.IsValid", "model.config.is_valid.cluster_email_batching.app_error", nil, "", http.StatusBadRequest)
}
+ if len(*o.ServiceSettings.SiteURL) == 0 && *o.ServiceSettings.AllowCookiesForSubdomains {
+ return NewAppError("Config.IsValid", "model.config.is_valid.allow_cookies_for_subdomains.app_error", nil, "", http.StatusBadRequest)
+ }
+
if err := o.TeamSettings.isValid(); err != nil {
return err
}
@@ -1790,6 +2111,10 @@ func (o *Config) IsValid() *AppError {
return err
}
+ if err := o.DisplaySettings.isValid(); err != nil {
+ return err
+ }
+
return nil
}
@@ -1834,6 +2159,10 @@ func (ss *SqlSettings) isValid() *AppError {
return NewAppError("Config.IsValid", "model.config.is_valid.sql_idle.app_error", nil, "", http.StatusBadRequest)
}
+ if *ss.ConnMaxLifetimeMilliseconds < 0 {
+ return NewAppError("Config.IsValid", "model.config.is_valid.sql_conn_max_lifetime_milliseconds.app_error", nil, "", http.StatusBadRequest)
+ }
+
if *ss.QueryTimeout <= 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.sql_query_timeout.app_error", nil, "", http.StatusBadRequest)
}
@@ -1938,6 +2267,10 @@ func (ls *LdapSettings) isValid() *AppError {
if *ls.IdAttribute == "" {
return NewAppError("Config.IsValid", "model.config.is_valid.ldap_id", nil, "", http.StatusBadRequest)
}
+
+ if *ls.LoginIdAttribute == "" {
+ return NewAppError("Config.IsValid", "model.config.is_valid.ldap_login_id", nil, "", http.StatusBadRequest)
+ }
}
return nil
@@ -2041,10 +2374,40 @@ func (ss *ServiceSettings) isValid() *AppError {
}
}
- if len(*ss.ListenAddress) == 0 {
+ if len(*ss.WebsocketURL) != 0 {
+ if _, err := url.ParseRequestURI(*ss.WebsocketURL); err != nil {
+ return NewAppError("Config.IsValid", "model.config.is_valid.websocket_url.app_error", nil, "", http.StatusBadRequest)
+ }
+ }
+
+ host, port, _ := net.SplitHostPort(*ss.ListenAddress)
+ var isValidHost bool
+ if host == "" {
+ isValidHost = true
+ } else {
+ isValidHost = (net.ParseIP(host) != nil) || IsDomainName(host)
+ }
+ portInt, err := strconv.Atoi(port)
+ if err != nil || !isValidHost || portInt < 0 || portInt > math.MaxUint16 {
return NewAppError("Config.IsValid", "model.config.is_valid.listen_address.app_error", nil, "", http.StatusBadRequest)
}
+ if *ss.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DISABLED &&
+ *ss.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DEFAULT_ON &&
+ *ss.ExperimentalGroupUnreadChannels != GROUP_UNREAD_CHANNELS_DEFAULT_OFF {
+ return NewAppError("Config.IsValid", "model.config.is_valid.group_unread_channels.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ switch *ss.ImageProxyType {
+ case "":
+ case "atmos/camo":
+ if *ss.ImageProxyOptions == "" {
+ return NewAppError("Config.IsValid", "model.config.is_valid.atmos_camo_image_proxy_options.app_error", nil, "", http.StatusBadRequest)
+ }
+ default:
+ return NewAppError("Config.IsValid", "model.config.is_valid.image_proxy_type.app_error", nil, "", http.StatusBadRequest)
+ }
+
return nil
}
@@ -2121,8 +2484,46 @@ func (mes *MessageExportSettings) isValid(fs FileSettings) *AppError {
return NewAppError("Config.IsValid", "model.config.is_valid.message_export.daily_runtime.app_error", nil, err.Error(), http.StatusBadRequest)
} else if mes.BatchSize == nil || *mes.BatchSize < 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.message_export.batch_size.app_error", nil, "", http.StatusBadRequest)
+ } else if mes.ExportFormat == nil || (*mes.ExportFormat != COMPLIANCE_EXPORT_TYPE_ACTIANCE && *mes.ExportFormat != COMPLIANCE_EXPORT_TYPE_GLOBALRELAY && *mes.ExportFormat != COMPLIANCE_EXPORT_TYPE_CSV) {
+ return NewAppError("Config.IsValid", "model.config.is_valid.message_export.export_type.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if *mes.ExportFormat == COMPLIANCE_EXPORT_TYPE_GLOBALRELAY {
+ if mes.GlobalRelaySettings == nil {
+ return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.config_missing.app_error", nil, "", http.StatusBadRequest)
+ } else if mes.GlobalRelaySettings.CustomerType == nil || (*mes.GlobalRelaySettings.CustomerType != GLOBALRELAY_CUSTOMER_TYPE_A9 && *mes.GlobalRelaySettings.CustomerType != GLOBALRELAY_CUSTOMER_TYPE_A10) {
+ return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.customer_type.app_error", nil, "", http.StatusBadRequest)
+ } else if mes.GlobalRelaySettings.EmailAddress == nil || !strings.Contains(*mes.GlobalRelaySettings.EmailAddress, "@") {
+ // validating email addresses is hard - just make sure it contains an '@' sign
+ // see https://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address
+ return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.email_address.app_error", nil, "", http.StatusBadRequest)
+ } else if mes.GlobalRelaySettings.SmtpUsername == nil || *mes.GlobalRelaySettings.SmtpUsername == "" {
+ return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.smtp_username.app_error", nil, "", http.StatusBadRequest)
+ } else if mes.GlobalRelaySettings.SmtpPassword == nil || *mes.GlobalRelaySettings.SmtpPassword == "" {
+ return NewAppError("Config.IsValid", "model.config.is_valid.message_export.global_relay.smtp_password.app_error", nil, "", http.StatusBadRequest)
+ }
+ }
+ }
+ return nil
+}
+
+func (ds *DisplaySettings) isValid() *AppError {
+ if len(*ds.CustomUrlSchemes) != 0 {
+ validProtocolPattern := regexp.MustCompile(`(?i)^\s*[a-z][a-z0-9-]*\s*$`)
+
+ for _, scheme := range *ds.CustomUrlSchemes {
+ if !validProtocolPattern.MatchString(scheme) {
+ return NewAppError(
+ "Config.IsValid",
+ "model.config.is_valid.display.custom_url_schemes.app_error",
+ map[string]interface{}{"Scheme": scheme},
+ "",
+ http.StatusBadRequest,
+ )
+ }
}
}
+
return nil
}
diff --git a/vendor/github.com/mattermost/platform/model/data_retention_policy.go b/vendor/github.com/mattermost/mattermost-server/model/data_retention_policy.go
index 7284477e..dbb13374 100644
--- a/vendor/github.com/mattermost/platform/model/data_retention_policy.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/data_retention_policy.go
@@ -16,21 +16,12 @@ type DataRetentionPolicy struct {
}
func (me *DataRetentionPolicy) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func DataRetentionPolicyFromJson(data io.Reader) *DataRetentionPolicy {
- decoder := json.NewDecoder(data)
- var me DataRetentionPolicy
- err := decoder.Decode(&me)
- if err == nil {
- return &me
- } else {
- return nil
- }
+ var me *DataRetentionPolicy
+ json.NewDecoder(data).Decode(&me)
+ return me
}
diff --git a/vendor/github.com/mattermost/platform/model/emoji.go b/vendor/github.com/mattermost/mattermost-server/model/emoji.go
index 272616d9..f14af89d 100644
--- a/vendor/github.com/mattermost/platform/model/emoji.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/emoji.go
@@ -9,6 +9,11 @@ import (
"net/http"
)
+const (
+ EMOJI_NAME_MAX_LENGTH = 64
+ EMOJI_SORT_BY_NAME = "name"
+)
+
type Emoji struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
@@ -18,6 +23,11 @@ type Emoji struct {
Name string `json:"name"`
}
+func inSystemEmoji(emojiName string) bool {
+ _, ok := SystemEmojis[emojiName]
+ return ok
+}
+
func (emoji *Emoji) IsValid() *AppError {
if len(emoji.Id) != 26 {
return NewAppError("Emoji.IsValid", "model.emoji.id.app_error", nil, "", http.StatusBadRequest)
@@ -31,11 +41,15 @@ func (emoji *Emoji) IsValid() *AppError {
return NewAppError("Emoji.IsValid", "model.emoji.update_at.app_error", nil, "id="+emoji.Id, http.StatusBadRequest)
}
- if len(emoji.CreatorId) != 26 {
+ if len(emoji.CreatorId) > 26 {
return NewAppError("Emoji.IsValid", "model.emoji.user_id.app_error", nil, "", http.StatusBadRequest)
}
- if len(emoji.Name) == 0 || len(emoji.Name) > 64 || !IsValidAlphaNumHyphenUnderscore(emoji.Name, false) {
+ return IsValidEmojiName(emoji.Name)
+}
+
+func IsValidEmojiName(name string) *AppError {
+ if len(name) == 0 || len(name) > EMOJI_NAME_MAX_LENGTH || !IsValidAlphaNumHyphenUnderscore(name, false) || inSystemEmoji(name) {
return NewAppError("Emoji.IsValid", "model.emoji.name.app_error", nil, "", http.StatusBadRequest)
}
@@ -51,46 +65,24 @@ func (emoji *Emoji) PreSave() {
emoji.UpdateAt = emoji.CreateAt
}
-func (emoji *Emoji) PreUpdate() {
- emoji.UpdateAt = GetMillis()
-}
-
func (emoji *Emoji) ToJson() string {
- b, err := json.Marshal(emoji)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(emoji)
+ return string(b)
}
func EmojiFromJson(data io.Reader) *Emoji {
- decoder := json.NewDecoder(data)
- var emoji Emoji
- err := decoder.Decode(&emoji)
- if err == nil {
- return &emoji
- } else {
- return nil
- }
+ var emoji *Emoji
+ json.NewDecoder(data).Decode(&emoji)
+ return emoji
}
func EmojiListToJson(emojiList []*Emoji) string {
- b, err := json.Marshal(emojiList)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(emojiList)
+ return string(b)
}
func EmojiListFromJson(data io.Reader) []*Emoji {
- decoder := json.NewDecoder(data)
var emojiList []*Emoji
- err := decoder.Decode(&emojiList)
- if err == nil {
- return emojiList
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&emojiList)
+ return emojiList
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/emoji_data.go b/vendor/github.com/mattermost/mattermost-server/model/emoji_data.go
new file mode 100644
index 00000000..f6e62e68
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/emoji_data.go
@@ -0,0 +1,6 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+var SystemEmojis = map[string]string{"policewoman": "1f46e-200d-2640-fe0f", "family_man_girl_medium_skin_tone": "1f468-1f3fd", "man_technologist": "1f468-200d-1f4bb", "family_woman_girl_medium_light_skin_tone": "1f469-1f3fc", "massage_woman_medium_light_skin_tone": "1f486-1f3fc-200d-2640-fe0f", "family_woman_woman_boy": "1f469-200d-1f469-200d-1f466", "rice_scene": "1f391", "notes": "1f3b6", "burundi": "1f1e7-1f1ee", "woman_medium_skin_tone": "1f469-1f3fd", "tipping_hand_man_medium_dark_skin_tone": "1f481-1f3fe-200d-2642-fe0f", "new_moon": "1f311", "belize": "1f1e7-1f1ff", "bhutan": "1f1e7-1f1f9", "eu": "1f1ea-1f1fa", "point_up_dark_skin_tone": "261d-1f3ff", "older_man_medium_light_skin_tone": "1f474-1f3fc", "prince": "1f934", "walking_man": "1f6b6", "telephone_receiver": "1f4de", "arrow_upper_right": "2197-fe0f", "taiwan": "1f1f9-1f1fc", "-1_light_skin_tone": "1f44e-1f3fb", "bear": "1f43b", "derelict_house": "1f3da", "blue_book": "1f4d8", "ok": "1f197", "woman_farmer_medium_light_skin_tone": "1f469-1f3fc", "man_shrugging_light_skin_tone": "1f937-1f3fb-200d-2642-fe0f", "dancing_women": "1f46f", "cd": "1f4bf", "tada": "1f389", "virgo": "264d-fe0f", "white_flower": "1f4ae", "guardswoman_medium_dark_skin_tone": "1f482-1f3fe-200d-2640-fe0f", "performing_arts": "1f3ad", "prayer_beads": "1f4ff", "congo_brazzaville": "1f1e8-1f1ec", "point_down_medium_skin_tone": "1f447-1f3fd", "raised_hand_with_fingers_splayed_light_skin_tone": "1f590-1f3fb", "man_playing_water_polo_medium_skin_tone": "1f93d-1f3fd-200d-2642-fe0f", "four_leaf_clover": "1f340", "microphone": "1f3a4", "heartpulse": "1f497", "north_korea": "1f1f0-1f1f5", "neutral_face": "1f610", "volleyball": "1f3d0", "man_playing_water_polo": "1f93d-200d-2642-fe0f", "uk": "1f1ec-1f1e7", "wallis_futuna": "1f1fc-1f1eb", "earth_africa": "1f30d", "droplet": "1f4a7", "construction_worker_man_medium_dark_skin_tone": "1f477-1f3fe-200d-2640-fe0f", "family_woman_woman_girl_boy_medium_light_skin_tone": "1f469-1f3fc", "mountain_biking_man_medium_dark_skin_tone": "1f6b5-1f3fe-200d-2640-fe0f", "vulcan_salute_light_skin_tone": "1f596-1f3fb", "woman_shrugging_dark_skin_tone": "1f937-1f3ff-200d-2640-fe0f", "walking_man_medium_light_skin_tone": "1f6b6-1f3fc-200d-2640-fe0f", "wave": "1f44b", "framed_picture": "1f5bc", "mag": "1f50d", "fist_left": "1f91b", "building_construction": "1f3d7", "clock9": "1f558", "cayman_islands": "1f1f0-1f1fe", "laos": "1f1f1-1f1e6", "woman_playing_handball_dark_skin_tone": "1f93e-1f3ff-200d-2640-fe0f", "man_office_worker": "1f468-200d-1f4bc", "family_man_woman_girl": "1f468-200d-1f469-200d-1f467", "wilted_flower": "1f940", "books": "1f4da", "rage": "1f621", "rice_ball": "1f359", "desert": "1f3dc", "malta": "1f1f2-1f1f9", "haircut_woman_dark_skin_tone": "1f487-1f3ff-200d-2640-fe0f", "symbols": "1f523", "marshall_islands": "1f1f2-1f1ed", "sierra_leone": "1f1f8-1f1f1", "crossed_fingers_medium_dark_skin_tone": "1f91e-1f3fe", "man_judge_medium_skin_tone": "1f468-1f3fd", "bamboo": "1f38d", "keyboard": "2328-fe0f", "clock10": "1f559", "massage_man_medium_skin_tone": "1f486-1f3fd-200d-2642-fe0f", "tipping_hand_man_dark_skin_tone": "1f481-1f3ff-200d-2642-fe0f", "man_facepalming_light_skin_tone": "1f926-1f3fb-200d-2642-fe0f", "train": "1f68b", "traffic_light": "1f6a5", "vietnam": "1f1fb-1f1f3", "boy_medium_light_skin_tone": "1f466-1f3fc", "man_farmer_light_skin_tone": "1f468-1f3fb", "man_singer_medium_dark_skin_tone": "1f468-1f3fe", "woman_cartwheeling_medium_light_skin_tone": "1f938-1f3fc-200d-2640-fe0f", "top": "1f51d", "gb": "1f1ec-1f1e7", "mouse2": "1f401", "do_not_litter": "1f6af", "south_sudan": "1f1f8-1f1f8", "bowing_woman_light_skin_tone": "1f647-1f3fb-200d-2640-fe0f", "family_man_man_girl_girl_medium_light_skin_tone": "1f468-1f3fc", "japanese_goblin": "1f47a", "camel": "1f42b", "taurus": "2649-fe0f", "mute": "1f507", "woman_mechanic_medium_dark_skin_tone": "1f469-1f3fe", "surfer": "1f3c4", "tipping_hand_man": "1f481-200d-2642-fe0f", "family_woman_woman_boy_boy": "1f469-200d-1f469-200d-1f466-200d-1f466", "floppy_disk": "1f4be", "atm": "1f3e7", "clock230": "1f55d", "prince_light_skin_tone": "1f934-1f3fb", "name_badge": "1f4db", "octocat": "octocat", "family_woman_woman_girl_girl_dark_skin_tone": "1f469-1f3ff", "christmas_tree": "1f384", "waxing_gibbous_moon": "1f314", "mountain_cableway": "1f6a0", "woman_scientist_medium_dark_skin_tone": "1f469-1f3fe", "haircut_man_medium_dark_skin_tone": "1f487-1f3fe-200d-2642-fe0f", "basketball_woman_medium_dark_skin_tone": "26f9-1f3fe-200d-2640-fe0f", "family_man_man_boy_medium_light_skin_tone": "1f468-1f3fc", "rowing_woman_medium_dark_skin_tone": "1f6a3-1f3fe-200d-2640-fe0f", "bowling": "1f3b3", "shinto_shrine": "26e9", "round_pushpin": "1f4cd", "cyprus": "1f1e8-1f1fe", "open_hands_dark_skin_tone": "1f450-1f3ff", "clap_medium_skin_tone": "1f44f-1f3fd", "bath_medium_dark_skin_tone": "1f6c0-1f3fe", "briefcase": "1f4bc", "tiger": "1f42f", "morocco": "1f1f2-1f1e6", "open_hands_light_skin_tone": "1f450-1f3fb", "hand_light_skin_tone": "270b-1f3fb", "weight_lifting_man_medium_dark_skin_tone": "1f3cb-1f3fe-200d-2640-fe0f", "mans_shoe": "1f45e", "poland": "1f1f5-1f1f1", "raised_hands_medium_skin_tone": "1f64c-1f3fd", "family_man_woman_girl_boy_light_skin_tone": "1f468-1f3fb", "woman_playing_handball_medium_skin_tone": "1f93e-1f3fd-200d-2640-fe0f", "office": "1f3e2", "woman_singer_medium_dark_skin_tone": "1f469-1f3fe", "family_woman_woman_boy_boy_medium_light_skin_tone": "1f469-1f3fc", "scorpion": "1f982", "tomato": "1f345", "goal_net": "1f945", "chad": "1f1f9-1f1e9", "family_man_woman_girl_boy_medium_skin_tone": "1f468-1f3fd", "mountain_biking_man_light_skin_tone": "1f6b5-1f3fb-200d-2640-fe0f", "weight_lifting_man_dark_skin_tone": "1f3cb-1f3ff-200d-2640-fe0f", "eyeglasses": "1f453", "golfing_woman": "1f3cc-fe0f-200d-2640-fe0f", "dvd": "1f4c0", "clipboard": "1f4cb", "ireland": "1f1ee-1f1ea", "woman_student_dark_skin_tone": "1f469-1f3ff", "angry": "1f620", "baby": "1f476", "women_wrestling": "1f93c-200d-2640-fe0f", "black_square_button": "1f532", "male_detective_medium_light_skin_tone": "1f575-1f3fc-200d-2640-fe0f", "dancer_dark_skin_tone": "1f483-1f3ff", "id": "1f194", "vibration_mode": "1f4f3", "handshake": "1f91d", "tiger2": "1f405", "leaves": "1f343", "baseball": "26be-fe0f", "golf": "26f3-fe0f", "toilet": "1f6bd", "male_detective_dark_skin_tone": "1f575-1f3ff-200d-2640-fe0f", "family_woman_boy": "1f469-200d-1f466", "duck": "1f986", "writing_hand_medium_dark_skin_tone": "270d-1f3fe", "woman_singer_medium_light_skin_tone": "1f469-1f3fc", "man_teacher_medium_skin_tone": "1f468-1f3fd", "lips": "1f444", "octopus": "1f419", "policeman_medium_dark_skin_tone": "1f46e-1f3fe-200d-2640-fe0f", "man_factory_worker_dark_skin_tone": "1f468-1f3ff", "man_astronaut_light_skin_tone": "1f468-1f3fb", "ok_man_dark_skin_tone": "1f646-1f3ff-200d-2642-fe0f", "couple_with_heart": "1f491", "pray_medium_skin_tone": "1f64f-1f3fd", "woman_health_worker_light_skin_tone": "1f469-1f3fb", "tipping_hand_woman_medium_light_skin_tone": "1f481-1f3fc-200d-2640-fe0f", "no_good_woman_dark_skin_tone": "1f645-1f3ff-200d-2640-fe0f", "no_good_woman_medium_dark_skin_tone": "1f645-1f3fe-200d-2640-fe0f", "thumbsdown": "1f44e", "fist": "270a", "camera_flash": "1f4f8", "azerbaijan": "1f1e6-1f1ff", "woman_with_turban_medium_light_skin_tone": "1f473-1f3fc-200d-2640-fe0f", "man_singer_medium_skin_tone": "1f468-1f3fd", "mali": "1f1f2-1f1f1", "blonde_woman_medium_dark_skin_tone": "1f471-1f3fe-200d-2640-fe0f", "family_man_man_girl_light_skin_tone": "1f468-1f3fb", "biking_woman_medium_skin_tone": "1f6b4-1f3fd-200d-2640-fe0f", "crab": "1f980", "green_salad": "1f957", "men_wrestling": "1f93c-200d-2642-fe0f", "-1_medium_dark_skin_tone": "1f44e-1f3fe", "baby_dark_skin_tone": "1f476-1f3ff", "surfing_woman_light_skin_tone": "1f3c4-1f3fb-200d-2640-fe0f", "stuck_out_tongue_winking_eye": "1f61c", "plate_with_cutlery": "1f37d", "swimmer": "1f3ca", "blonde_woman_medium_light_skin_tone": "1f471-1f3fc-200d-2640-fe0f", "curly_loop": "27b0", "india": "1f1ee-1f1f3", "norfolk_island": "1f1f3-1f1eb", "mountain_biking_woman_dark_skin_tone": "1f6b5-1f3ff-200d-2640-fe0f", "ghost": "1f47b", "boar": "1f417", "railway_track": "1f6e4", "100": "1f4af", "metal_medium_dark_skin_tone": "1f918-1f3fe", "woman_playing_handball_light_skin_tone": "1f93e-1f3fb-200d-2640-fe0f", "barber": "1f488", "clock730": "1f562", "equatorial_guinea": "1f1ec-1f1f6", "maldives": "1f1f2-1f1fb", "weight_lifting_woman_medium_dark_skin_tone": "1f3cb-1f3fe-200d-2640-fe0f", "eagle": "1f985", "tea": "1f375", "tanabata_tree": "1f38b", "night_with_stars": "1f303", "balloon": "1f388", "on": "1f51b", "lizard": "1f98e", "beer": "1f37a", "part_alternation_mark": "303d-fe0f", "white_square_button": "1f533", "clock430": "1f55f", "gibraltar": "1f1ec-1f1ee", "massage_woman_medium_skin_tone": "1f486-1f3fd-200d-2640-fe0f", "sweat": "1f613", "athletic_shoe": "1f45f", "joystick": "1f579", "biohazard": "2623-fe0f", "muscle_medium_light_skin_tone": "1f4aa-1f3fc", "bride_with_veil_medium_light_skin_tone": "1f470-1f3fc", "parasol_on_ground": "26f1", "costa_rica": "1f1e8-1f1f7", "woman_student_light_skin_tone": "1f469-1f3fb", "massage_woman_medium_dark_skin_tone": "1f486-1f3fe-200d-2640-fe0f", "surfing_woman_medium_dark_skin_tone": "1f3c4-1f3fe-200d-2640-fe0f", "rowing_woman": "1f6a3-200d-2640-fe0f", "guardsman_light_skin_tone": "1f482-1f3fb-200d-2640-fe0f", "construction_worker_man_light_skin_tone": "1f477-1f3fb-200d-2640-fe0f", "family_woman_boy_boy": "1f469-200d-1f466-200d-1f466", "small_airplane": "1f6e9", "baggage_claim": "1f6c4", "bosnia_herzegovina": "1f1e7-1f1e6", "falkland_islands": "1f1eb-1f1f0", "crossed_fingers_medium_light_skin_tone": "1f91e-1f3fc", "man_scientist_medium_light_skin_tone": "1f468-1f3fc", "family_woman_girl_boy_medium_skin_tone": "1f469-1f3fd", "satisfied": "1f606", "u5408": "1f234", "cn": "1f1e8-1f1f3", "isle_of_man": "1f1ee-1f1f2", "fist_raised_medium_light_skin_tone": "270a-1f3fc", "family_man_woman_girl_dark_skin_tone": "1f468-1f3ff", "family_woman_girl_dark_skin_tone": "1f469-1f3ff", "family_man_boy_medium_skin_tone": "1f468-1f3fd", "money_mouth_face": "1f911", "syringe": "1f489", "hand_medium_light_skin_tone": "270b-1f3fc", "writing_hand_medium_skin_tone": "270d-1f3fd", "man_farmer_medium_light_skin_tone": "1f468-1f3fc", "woman_artist_dark_skin_tone": "1f469-1f3ff", "tickets": "1f39f", "man_cartwheeling_light_skin_tone": "1f938-1f3fb-200d-2642-fe0f", "squid": "1f991", "fish": "1f41f", "memo": "1f4dd", "eye_speech_bubble": "1f441-200d-1f5e8", "+1_light_skin_tone": "1f44d-1f3fb", "tulip": "1f337", "blossom": "1f33c", "family_woman_woman_girl_medium_dark_skin_tone": "1f469-1f3fe", "triumph": "1f624", "rooster": "1f413", "ng": "1f196", "blonde_man_medium_light_skin_tone": "1f471-1f3fc-200d-2640-fe0f", "policeman_light_skin_tone": "1f46e-1f3fb-200d-2640-fe0f", "woman_cook_dark_skin_tone": "1f469-1f3ff", "pray_dark_skin_tone": "1f64f-1f3ff", "point_up_2_medium_skin_tone": "1f446-1f3fd", "busts_in_silhouette": "1f465", "tornado": "1f32a", "woman_juggling": "1f939-200d-2640-fe0f", "cupid": "1f498", "white_check_mark": "2705", "aruba": "1f1e6-1f1fc", "family_man_boy_boy_medium_dark_skin_tone": "1f468-1f3fe", "woman_cartwheeling_dark_skin_tone": "1f938-1f3ff-200d-2640-fe0f", "woman_playing_water_polo_dark_skin_tone": "1f93d-1f3ff-200d-2640-fe0f", "relaxed": "263a-fe0f", "birthday": "1f382", "high_brightness": "1f506", "couple_with_heart_woman_woman_medium_light_skin_tone": "1f469-1f3fc", "biking_man_dark_skin_tone": "1f6b4-1f3ff-200d-2640-fe0f", "disappointed_relieved": "1f625", "canary_islands": "1f1ee-1f1e8", "st_pierre_miquelon": "1f1f5-1f1f2", "trinidad_tobago": "1f1f9-1f1f9", "turkmenistan": "1f1f9-1f1f2", "pouting_woman_medium_skin_tone": "1f64e-1f3fd-200d-2640-fe0f", "man_student_dark_skin_tone": "1f468-1f3ff", "princess_dark_skin_tone": "1f478-1f3ff", "family_woman_boy_boy_medium_skin_tone": "1f469-1f3fd", "old_key": "1f5dd", "muscle_medium_skin_tone": "1f4aa-1f3fd", "ear_medium_dark_skin_tone": "1f442-1f3fe", "girl_medium_dark_skin_tone": "1f467-1f3fe", "man_pilot_dark_skin_tone": "1f468-1f3ff", "wolf": "1f43a", "gem": "1f48e", "arrow_double_up": "23eb", "woman_factory_worker_light_skin_tone": "1f469-1f3fb", "woman_mechanic_medium_skin_tone": "1f469-1f3fd", "woman_firefighter_light_skin_tone": "1f469-1f3fb", "sunglasses": "1f60e", "snake": "1f40d", "pen": "1f58a", "nose_medium_skin_tone": "1f443-1f3fd", "weight_lifting_man": "1f3cb-fe0f", "alarm_clock": "23f0", "golfing_woman_medium_light_skin_tone": "1f3cc-1f3fc-200d-2640-fe0f", "vulcan_salute": "1f596", "earth_asia": "1f30f", "+1_dark_skin_tone": "1f44d-1f3ff", "family_woman_boy_medium_dark_skin_tone": "1f469-1f3fe", "family_woman_girl_girl_light_skin_tone": "1f469-1f3fb", "weight_lifting_man_medium_light_skin_tone": "1f3cb-1f3fc-200d-2640-fe0f", "angel": "1f47c", "peach": "1f351", "truck": "1f69a", "tajikistan": "1f1f9-1f1ef", "tr": "1f1f9-1f1f7", "running_woman_medium_skin_tone": "1f3c3-1f3fd-200d-2640-fe0f", "wrench": "1f527", "black_flag": "1f3f4", "cape_verde": "1f1e8-1f1fb", "man_technologist_medium_light_skin_tone": "1f468-1f3fc", "mountain_biking_woman_medium_light_skin_tone": "1f6b5-1f3fc-200d-2640-fe0f", "man_astronaut_medium_skin_tone": "1f468-1f3fd", "man_in_tuxedo_medium_light_skin_tone": "1f935-1f3fc", "see_no_evil": "1f648", "egg": "1f95a", "1234": "1f522", "lesotho": "1f1f1-1f1f8", "middle_finger_medium_skin_tone": "1f595-1f3fd", "woman_health_worker_medium_light_skin_tone": "1f469-1f3fc", "sneezing_face": "1f927", "man_cook": "1f468-200d-1f373", "mortar_board": "1f393", "candle": "1f56f", "basketball_man_medium_skin_tone": "26f9-1f3fd-200d-2640-fe0f", "ferris_wheel": "1f3a1", "martinique": "1f1f2-1f1f6", "st_vincent_grenadines": "1f1fb-1f1e8", "yemen": "1f1fe-1f1ea", "pray_light_skin_tone": "1f64f-1f3fb", "man_in_tuxedo_medium_skin_tone": "1f935-1f3fd", "woman_pilot_medium_dark_skin_tone": "1f469-1f3fe", "pregnant_woman_dark_skin_tone": "1f930-1f3ff", "fu": "1f595", "haircut": "1f487", "boxing_glove": "1f94a", "page_with_curl": "1f4c3", "muscle_light_skin_tone": "1f4aa-1f3fb", "woman_firefighter_dark_skin_tone": "1f469-1f3ff", "ok_woman_light_skin_tone": "1f646-1f3fb-200d-2640-fe0f", "family_man_woman_boy_boy_medium_light_skin_tone": "1f468-1f3fc", "family_woman_boy_dark_skin_tone": "1f469-1f3ff", "weight_lifting_man_light_skin_tone": "1f3cb-1f3fb-200d-2640-fe0f", "blonde_man": "1f471", "woman_technologist": "1f469-200d-1f4bb", "boom": "1f4a5", "1st_place_medal": "1f947", "nine": "0039-fe0f-20e3", "czech_republic": "1f1e8-1f1ff", "meat_on_bone": "1f356", "hamburger": "1f354", "video_game": "1f3ae", "clock2": "1f551", "woman_facepalming_dark_skin_tone": "1f926-1f3ff-200d-2640-fe0f", "couplekiss_man_man_medium_dark_skin_tone": "1f468-1f3fe", "arrow_lower_right": "2198-fe0f", "haircut_woman_light_skin_tone": "1f487-1f3fb-200d-2640-fe0f", "woman_dark_skin_tone": "1f469-1f3ff", "older_man_light_skin_tone": "1f474-1f3fb", "first_quarter_moon_with_face": "1f31b", "fries": "1f35f", "restroom": "1f6bb", "zero": "0030-fe0f-20e3", "fr": "1f1eb-1f1f7", "kuwait": "1f1f0-1f1fc", "man_health_worker_medium_skin_tone": "1f468-1f3fd", "woman_judge_light_skin_tone": "1f469-1f3fb", "man_judge_dark_skin_tone": "1f468-1f3ff", "ok_woman_medium_skin_tone": "1f646-1f3fd-200d-2640-fe0f", "bike": "1f6b2", "registered": "00ae-fe0f", "blonde_woman_medium_skin_tone": "1f471-1f3fd-200d-2640-fe0f", "stuck_out_tongue_closed_eyes": "1f61d", "collision": "1f4a5", "wheelchair": "267f-fe0f", "black_circle": "26ab-fe0f", "point_up_2_light_skin_tone": "1f446-1f3fb", "older_man": "1f474", "suspension_railway": "1f69f", "libra": "264e-fe0f", "crossed_flags": "1f38c", "man_cartwheeling_medium_dark_skin_tone": "1f938-1f3fe-200d-2642-fe0f", "man_playing_water_polo_dark_skin_tone": "1f93d-1f3ff-200d-2642-fe0f", "scream": "1f631", "no_good_man": "1f645-200d-2642-fe0f", "timer_clock": "23f2", "venezuela": "1f1fb-1f1ea", "raised_back_of_hand_light_skin_tone": "1f91a-1f3fb", "woman_technologist_medium_skin_tone": "1f469-1f3fd", "popcorn": "1f37f", "romania": "1f1f7-1f1f4", "togo": "1f1f9-1f1ec", "writing_hand_dark_skin_tone": "270d-1f3ff", "woman_singer_dark_skin_tone": "1f469-1f3ff", "pouting_woman_medium_dark_skin_tone": "1f64e-1f3fe-200d-2640-fe0f", "man_health_worker_light_skin_tone": "1f468-1f3fb", "dancer_medium_light_skin_tone": "1f483-1f3fc", "phone": "260e-fe0f", "chart": "1f4b9", "repeat": "1f501", "mahjong": "1f004-fe0f", "liberia": "1f1f1-1f1f7", "rage3": "rage3", "person_frowning": "1f64d", "open_hands_medium_skin_tone": "1f450-1f3fd", "man_dark_skin_tone": "1f468-1f3ff", "man_factory_worker_medium_light_skin_tone": "1f468-1f3fc", "man_astronaut_medium_light_skin_tone": "1f468-1f3fc", "ring": "1f48d", "ok_hand_medium_skin_tone": "1f44c-1f3fd", "santa": "1f385", "beach_umbrella": "1f3d6", "finland": "1f1eb-1f1ee", "woman_facepalming_medium_skin_tone": "1f926-1f3fd-200d-2640-fe0f", "woman_shrugging_medium_light_skin_tone": "1f937-1f3fc-200d-2640-fe0f", "sunflower": "1f33b", "ok_hand_dark_skin_tone": "1f44c-1f3ff", "santa_medium_skin_tone": "1f385-1f3fd", "call_me_hand_medium_skin_tone": "1f919-1f3fd", "man_firefighter_light_skin_tone": "1f468-1f3fb", "kiss": "1f48b", "mandarin": "1f34a", "dollar": "1f4b5", "clock3": "1f552", "argentina": "1f1e6-1f1f7", "fist_left_light_skin_tone": "1f91b-1f3fb", "santa_light_skin_tone": "1f385-1f3fb", "family_man_girl_boy_medium_light_skin_tone": "1f468-1f3fc", "sushi": "1f363", "rice": "1f35a", "mailbox_with_mail": "1f4ec", "woman_cook_medium_dark_skin_tone": "1f469-1f3fe", "family_man_woman_girl_girl_medium_dark_skin_tone": "1f468-1f3fe", "straight_ruler": "1f4cf", "blue_heart": "1f499", "slightly_frowning_face": "1f641", "crossed_fingers": "1f91e", "seedling": "1f331", "herb": "1f33f", "medal_military": "1f396", "camping": "1f3d5", "arrow_backward": "25c0-fe0f", "heavy_multiplication_x": "2716-fe0f", "icecream": "1f366", "heavy_dollar_sign": "1f4b2", "frowning_woman_dark_skin_tone": "1f64d-1f3ff-200d-2640-fe0f", "family_man_woman_boy_boy_medium_dark_skin_tone": "1f468-1f3fe", "brazil": "1f1e7-1f1f7", "fist_right_medium_light_skin_tone": "1f91c-1f3fc", "man_scientist_medium_dark_skin_tone": "1f468-1f3fe", "family_woman_girl_light_skin_tone": "1f469-1f3fb", "swimming_man_medium_dark_skin_tone": "1f3ca-1f3fe-200d-2640-fe0f", "man": "1f468", "lemon": "1f34b", "japanese_castle": "1f3ef", "cinema": "1f3a6", "wave_light_skin_tone": "1f44b-1f3fb", "middle_finger_medium_light_skin_tone": "1f595-1f3fc", "five": "0035-fe0f-20e3", "boy_medium_dark_skin_tone": "1f466-1f3fe", "woman_technologist_dark_skin_tone": "1f469-1f3ff", "man_playing_handball_light_skin_tone": "1f93e-1f3fb-200d-2642-fe0f", "construction_worker_man": "1f477", "stadium": "1f3df", "biking_woman_dark_skin_tone": "1f6b4-1f3ff-200d-2640-fe0f", "trophy": "1f3c6", "arrow_left": "2b05-fe0f", "boy_dark_skin_tone": "1f466-1f3ff", "no_good_woman_light_skin_tone": "1f645-1f3fb-200d-2640-fe0f", "skull_and_crossbones": "2620-fe0f", "couple_with_heart_woman_woman": "1f469-200d-2764-fe0f-200d-1f469", "no_bicycles": "1f6b3", "bell": "1f514", "feelsgood": "feelsgood", "bowing_man_dark_skin_tone": "1f647-1f3ff-200d-2640-fe0f", "mouse": "1f42d", "anchor": "2693-fe0f", "cyclone": "1f300", "solomon_islands": "1f1f8-1f1e7", "basketball": "1f3c0", "notebook_with_decorative_cover": "1f4d4", "family_man_girl_girl_dark_skin_tone": "1f468-1f3ff", "singapore": "1f1f8-1f1ec", "golfing_man_medium_dark_skin_tone": "1f3cc-1f3fe-200d-2640-fe0f", "woman": "1f469", "fried_shrimp": "1f364", "construction": "1f6a7", "eight_pointed_black_star": "2734-fe0f", "black_joker": "1f0cf", "cambodia": "1f1f0-1f1ed", "mount_fuji": "1f5fb", "link": "1f517", "womens": "1f6ba", "family_man_man_boy_boy_medium_skin_tone": "1f468-1f3fd", "joy": "1f602", "crying_cat_face": "1f63f", "parking": "1f17f-fe0f", "barbados": "1f1e7-1f1e7", "bowing_woman_dark_skin_tone": "1f647-1f3ff-200d-2640-fe0f", "angel_medium_light_skin_tone": "1f47c-1f3fc", "waning_gibbous_moon": "1f316", "synagogue": "1f54d", "american_samoa": "1f1e6-1f1f8", "basketball_woman_medium_light_skin_tone": "26f9-1f3fc-200d-2640-fe0f", "bug": "1f41b", "woman_farmer_light_skin_tone": "1f469-1f3fb", "door": "1f6aa", "place_of_worship": "1f6d0", "eight_spoked_asterisk": "2733-fe0f", "mrs_claus_dark_skin_tone": "1f936-1f3ff", "u7a7a": "1f233", "man_astronaut_medium_dark_skin_tone": "1f468-1f3fe", "jack_o_lantern": "1f383", "lock_with_ink_pen": "1f50f", "male_detective_medium_skin_tone": "1f575-1f3fd-200d-2640-fe0f", "woman_firefighter_medium_dark_skin_tone": "1f469-1f3fe", "smiling_imp": "1f608", "tv": "1f4fa", "pouting_man": "1f64e-200d-2642-fe0f", "e-mail": "1f4e7", "package": "1f4e6", "clock130": "1f55c", "family_man_boy_light_skin_tone": "1f468-1f3fb", "cat2": "1f408", "mountain_biking_woman_medium_dark_skin_tone": "1f6b5-1f3fe-200d-2640-fe0f", "nauseated_face": "1f922", "fountain": "26f2-fe0f", "middle_finger": "1f595", "dancers": "1f46f", "cactus": "1f335", "man_student_light_skin_tone": "1f468-1f3fb", "family_man_man_girl_dark_skin_tone": "1f468-1f3ff", "family_man_girl_girl_medium_skin_tone": "1f468-1f3fd", "us_virgin_islands": "1f1fb-1f1ee", "woman_astronaut_medium_dark_skin_tone": "1f469-1f3fe", "honeybee": "1f41d", "bouquet": "1f490", "golfing_man": "1f3cc-fe0f", "u7981": "1f232", "french_guiana": "1f1ec-1f1eb", "kenya": "1f1f0-1f1ea", "melon": "1f348", "nicaragua": "1f1f3-1f1ee", "raised_hand_with_fingers_splayed_medium_light_skin_tone": "1f590-1f3fc", "bath_light_skin_tone": "1f6c0-1f3fb", "man_pilot_medium_light_skin_tone": "1f468-1f3fc", "european_post_office": "1f3e4", "mobile_phone_off": "1f4f4", "no_smoking": "1f6ad", "family_woman_girl_girl_medium_light_skin_tone": "1f469-1f3fc", "family_woman_girl_medium_dark_skin_tone": "1f469-1f3fe", "man_juggling_medium_dark_skin_tone": "1f939-1f3fe-200d-2642-fe0f", "expressionless": "1f611", "school_satchel": "1f392", "film_strip": "1f39e", "running_man_light_skin_tone": "1f3c3-1f3fb-200d-2640-fe0f", "family_man_woman_girl_medium_dark_skin_tone": "1f468-1f3fe", "family_woman_woman_boy_boy_light_skin_tone": "1f469-1f3fb", "smiley_cat": "1f63a", "chestnut": "1f330", "girl_dark_skin_tone": "1f467-1f3ff", "bride_with_veil_medium_dark_skin_tone": "1f470-1f3fe", "family_man_man_boy_boy_light_skin_tone": "1f468-1f3fb", "family_man_boy_boy_medium_light_skin_tone": "1f468-1f3fc", "wink": "1f609", "carrot": "1f955", "credit_card": "1f4b3", "triangular_ruler": "1f4d0", "question": "2753", "+1_medium_dark_skin_tone": "1f44d-1f3fe", "man_teacher_medium_dark_skin_tone": "1f468-1f3fe", "family_man_girl_girl_medium_light_skin_tone": "1f468-1f3fc", "kimono": "1f458", "bellhop_bell": "1f6ce", "red_circle": "1f534", "call_me_hand_light_skin_tone": "1f919-1f3fb", "nail_care_medium_skin_tone": "1f485-1f3fd", "woman_teacher_medium_skin_tone": "1f469-1f3fd", "woman_juggling_dark_skin_tone": "1f939-1f3ff-200d-2640-fe0f", "runner": "1f3c3", "heavy_check_mark": "2714-fe0f", "family_man_girl_girl_medium_dark_skin_tone": "1f468-1f3fe", "tent": "26fa-fe0f", "card_index_dividers": "1f5c2", "man_singer_dark_skin_tone": "1f468-1f3ff", "man_firefighter_medium_dark_skin_tone": "1f468-1f3fe", "man_playing_handball_medium_dark_skin_tone": "1f93e-1f3fe-200d-2642-fe0f", "female_detective_medium_dark_skin_tone": "1f575-1f3fe-200d-2640-fe0f", "metal": "1f918", "dark_sunglasses": "1f576", "vertical_traffic_light": "1f6a6", "four": "0034-fe0f-20e3", "wavy_dash": "3030-fe0f", "ear_medium_light_skin_tone": "1f442-1f3fc", "man_juggling_dark_skin_tone": "1f939-1f3ff-200d-2642-fe0f", "kissing_heart": "1f618", "sweet_potato": "1f360", "gift_heart": "1f49d", "man_technologist_light_skin_tone": "1f468-1f3fb", "prince_medium_dark_skin_tone": "1f934-1f3fe", "ok_man_medium_skin_tone": "1f646-1f3fd-200d-2642-fe0f", "womans_clothes": "1f45a", "roller_coaster": "1f3a2", "woman_student_medium_light_skin_tone": "1f469-1f3fc", "zipper_mouth_face": "1f910", "person_with_blond_hair": "1f471", "leftwards_arrow_with_hook": "21a9-fe0f", "white_circle": "26aa-fe0f", "afghanistan": "1f1e6-1f1eb", "face_with_thermometer": "1f912", "bow": "1f647", "kr": "1f1f0-1f1f7", "finnadie": "finnadie", "girl_light_skin_tone": "1f467-1f3fb", "woman_farmer_medium_skin_tone": "1f469-1f3fd", "umbrella": "2614-fe0f", "ice_cream": "1f368", "point_down_medium_light_skin_tone": "1f447-1f3fc", "woman_health_worker_dark_skin_tone": "1f469-1f3ff", "rowing_woman_medium_light_skin_tone": "1f6a3-1f3fc-200d-2640-fe0f", "money_with_wings": "1f4b8", "dolls": "1f38e", "surfing_man_medium_dark_skin_tone": "1f3c4-1f3fe-200d-2640-fe0f", "mrs_claus_medium_skin_tone": "1f936-1f3fd", "basketball_man_dark_skin_tone": "26f9-1f3ff-200d-2640-fe0f", "dragon_face": "1f432", "woman_cartwheeling": "1f938-200d-2640-fe0f", "aquarius": "2652-fe0f", "sos": "1f198", "clock1230": "1f567", "haiti": "1f1ed-1f1f9", "woman_facepalming": "1f926-200d-2640-fe0f", "clinking_glasses": "1f942", "trollface": "trollface", "mrs_claus_light_skin_tone": "1f936-1f3fb", "clap": "1f44f", "couplekiss_man_man": "1f468-200d-2764-fe0f-200d-1f48b-200d-1f468", "page_facing_up": "1f4c4", "belgium": "1f1e7-1f1ea", "curacao": "1f1e8-1f1fc", "family_woman_woman_boy_dark_skin_tone": "1f469-1f3ff", "two_men_holding_hands": "1f46c", "mountain_snow": "1f3d4", "wind_chime": "1f390", "person_with_pouting_face": "1f64e", "cityscape": "1f3d9", "bride_with_veil_dark_skin_tone": "1f470-1f3ff", "frowning_woman_light_skin_tone": "1f64d-1f3fb-200d-2640-fe0f", "bath_medium_light_skin_tone": "1f6c0-1f3fc", "sheep": "1f411", "sparkler": "1f387", "frowning_woman": "1f64d", "rat": "1f400", "custard": "1f36e", "video_camera": "1f4f9", "open_umbrella": "2602-fe0f", "man_with_turban_medium_light_skin_tone": "1f473-1f3fc-200d-2640-fe0f", "woman_student_medium_dark_skin_tone": "1f469-1f3fe", "ok_woman_medium_light_skin_tone": "1f646-1f3fc-200d-2640-fe0f", "swimming_man_dark_skin_tone": "1f3ca-1f3ff-200d-2640-fe0f", "man_cook_light_skin_tone": "1f468-1f3fb", "running_woman_light_skin_tone": "1f3c3-1f3fb-200d-2640-fe0f", "rabbit": "1f430", "ox": "1f402", "corn": "1f33d", "mozambique": "1f1f2-1f1ff", "point_right_light_skin_tone": "1f449-1f3fb", "nail_care_light_skin_tone": "1f485-1f3fb", "smiley": "1f603", "new_moon_with_face": "1f31a", "croatia": "1f1ed-1f1f7", "man_judge_medium_dark_skin_tone": "1f468-1f3fe", "fist_raised": "270a", "man_astronaut": "1f468-200d-1f680", "clock1130": "1f566", "st_lucia": "1f1f1-1f1e8", "princess": "1f478", "fist_left_medium_light_skin_tone": "1f91b-1f3fc", "point_left_medium_dark_skin_tone": "1f448-1f3fe", "woman_factory_worker_medium_skin_tone": "1f469-1f3fd", "angel_dark_skin_tone": "1f47c-1f3ff", "woman_cook": "1f469-200d-1f373", "koala": "1f428", "satellite": "1f4e1", "book": "1f4d6", "large_orange_diamond": "1f536", "monaco": "1f1f2-1f1e8", "spiral_notepad": "1f5d2", "capricorn": "2651-fe0f", "bacon": "1f953", "blonde_man_medium_dark_skin_tone": "1f471-1f3fe-200d-2640-fe0f", "business_suit_levitating_dark_skin_tone": "1f574-1f3ff", "call_me_hand_medium_light_skin_tone": "1f919-1f3fc", "female_detective_medium_light_skin_tone": "1f575-1f3fc-200d-2640-fe0f", "haircut_woman_medium_light_skin_tone": "1f487-1f3fc-200d-2640-fe0f", "alien": "1f47d", "baguette_bread": "1f956", "northern_mariana_islands": "1f1f2-1f1f5", "ukraine": "1f1fa-1f1e6", "flushed": "1f633", "man_scientist": "1f468-200d-1f52c", "trident": "1f531", "family_woman_woman_girl_boy_medium_skin_tone": "1f469-1f3fd", "family_man_boy_boy": "1f468-200d-1f466-200d-1f466", "tennis": "1f3be", "fire_engine": "1f692", "pushpin": "1f4cc", "man_health_worker_medium_dark_skin_tone": "1f468-1f3fe", "boy": "1f466", "headphones": "1f3a7", "fuelpump": "26fd-fe0f", "u6709": "1f236", "man_cook_medium_skin_tone": "1f468-1f3fd", "bride_with_veil_medium_skin_tone": "1f470-1f3fd", "point_up": "261d-fe0f", "necktie": "1f454", "control_knobs": "1f39b", "austria": "1f1e6-1f1f9", "papua_new_guinea": "1f1f5-1f1ec", "alembic": "2697-fe0f", "cook_islands": "1f1e8-1f1f0", "iceland": "1f1ee-1f1f8", "car": "1f697", "potable_water": "1f6b0", "haircut_man_medium_light_skin_tone": "1f487-1f3fc-200d-2642-fe0f", "couplekiss_woman_woman_medium_light_skin_tone": "1f469-1f3fc", "couplekiss_man_man_medium_skin_tone": "1f468-1f3fd", "cookie": "1f36a", "flight_departure": "1f6eb", "muscle_dark_skin_tone": "1f4aa-1f3ff", "construction_worker_man_medium_skin_tone": "1f477-1f3fd-200d-2640-fe0f", "black_medium_small_square": "25fe-fe0f", "guyana": "1f1ec-1f1fe", "file_folder": "1f4c1", "fountain_pen": "1f58b", "construction_worker_woman_medium_skin_tone": "1f477-1f3fd-200d-2640-fe0f", "family_man_woman_girl_boy_medium_light_skin_tone": "1f468-1f3fc", "poultry_leg": "1f357", "ski": "1f3bf", "guardswoman_medium_skin_tone": "1f482-1f3fd-200d-2640-fe0f", "family_man_man_girl_medium_dark_skin_tone": "1f468-1f3fe", "family_woman_boy_light_skin_tone": "1f469-1f3fb", "trumpet": "1f3ba", "no_pedestrians": "1f6b7", "heavy_minus_sign": "2796", "fist_oncoming": "1f44a", "ambulance": "1f691", "man_artist_dark_skin_tone": "1f468-1f3ff", "drum": "1f941", "train2": "1f686", "u7121": "1f21a-fe0f", "burkina_faso": "1f1e7-1f1eb", "nose_light_skin_tone": "1f443-1f3fb", "policewoman_medium_dark_skin_tone": "1f46e-1f3fe-200d-2640-fe0f", "lantern": "1f3ee", "metal_light_skin_tone": "1f918-1f3fb", "male_detective_light_skin_tone": "1f575-1f3fb-200d-2640-fe0f", "woman_juggling_medium_dark_skin_tone": "1f939-1f3fe-200d-2640-fe0f", "rowing_man_light_skin_tone": "1f6a3-1f3fb-200d-2640-fe0f", "lying_face": "1f925", "point_left": "1f448", "rosette": "1f3f5", "houses": "1f3d8", "repeat_one": "1f502", "liechtenstein": "1f1f1-1f1ee", "cloud_with_lightning": "1f329", "man_cartwheeling": "1f938-200d-2642-fe0f", "pause_button": "23f8", "arrows_clockwise": "1f503", "raised_hand_with_fingers_splayed_dark_skin_tone": "1f590-1f3ff", "clap_dark_skin_tone": "1f44f-1f3ff", "raising_hand_man_medium_skin_tone": "1f64b-1f3fd-200d-2642-fe0f", "family_woman_woman_girl_girl_medium_dark_skin_tone": "1f469-1f3fe", "dog": "1f436", "pouting_man_medium_dark_skin_tone": "1f64e-1f3fe-200d-2642-fe0f", "surfing_woman_medium_skin_tone": "1f3c4-1f3fd-200d-2640-fe0f", "confused": "1f615", "detective": "1f575-fe0f", "studio_microphone": "1f399", "fist_oncoming_medium_light_skin_tone": "1f44a-1f3fc", "man_firefighter_medium_skin_tone": "1f468-1f3fd", "tshirt": "1f455", "trolleybus": "1f68e", "norway": "1f1f3-1f1f4", "neckbeard": "neckbeard", "three": "0033-fe0f-20e3", "point_right_medium_skin_tone": "1f449-1f3fd", "man_medium_dark_skin_tone": "1f468-1f3fe", "pouting_man_medium_light_skin_tone": "1f64e-1f3fc-200d-2642-fe0f", "clock630": "1f561", "fist_raised_medium_skin_tone": "270a-1f3fd", "anguished": "1f627", "eye": "1f441", "bride_with_veil": "1f470", "hear_no_evil": "1f649", "wine_glass": "1f377", "soon": "1f51c", "family_man_boy_boy_light_skin_tone": "1f468-1f3fb", "family_woman_boy_boy_medium_light_skin_tone": "1f469-1f3fc", "dromedary_camel": "1f42a", "chipmunk": "1f43f", "soccer": "26bd-fe0f", "man_with_gua_pi_mao_medium_light_skin_tone": "1f472-1f3fc", "business_suit_levitating_medium_light_skin_tone": "1f574-1f3fc", "running_woman_medium_dark_skin_tone": "1f3c3-1f3fe-200d-2640-fe0f", "speaking_head": "1f5e3", "menorah": "1f54e", "non-potable_water": "1f6b1", "woman_with_turban_dark_skin_tone": "1f473-1f3ff-200d-2640-fe0f", "woman_shrugging_medium_skin_tone": "1f937-1f3fd-200d-2640-fe0f", "frowning_woman_medium_light_skin_tone": "1f64d-1f3fc-200d-2640-fe0f", "biking_man_medium_light_skin_tone": "1f6b4-1f3fc-200d-2640-fe0f", "woman_shrugging": "1f937-200d-2640-fe0f", "arrow_upper_left": "2196-fe0f", "metal_medium_skin_tone": "1f918-1f3fd", "woman_factory_worker_dark_skin_tone": "1f469-1f3ff", "hotsprings": "2668-fe0f", "ear_dark_skin_tone": "1f442-1f3ff", "girl_medium_skin_tone": "1f467-1f3fd", "woman_farmer_dark_skin_tone": "1f469-1f3ff", "man_student_medium_skin_tone": "1f468-1f3fd", "biking_man_medium_skin_tone": "1f6b4-1f3fd-200d-2640-fe0f", "woman_scientist": "1f469-200d-1f52c", "vs": "1f19a", "weight_lifting_man_medium_skin_tone": "1f3cb-1f3fd-200d-2640-fe0f", "arrow_right": "27a1-fe0f", "woman_juggling_medium_light_skin_tone": "1f939-1f3fc-200d-2640-fe0f", "racehorse": "1f40e", "sun_behind_large_cloud": "1f325", "convenience_store": "1f3ea", "namibia": "1f1f3-1f1e6", "raised_hands_medium_dark_skin_tone": "1f64c-1f3fe", "man_judge_medium_light_skin_tone": "1f468-1f3fc", "dancer_medium_skin_tone": "1f483-1f3fd", "fearful": "1f628", "frog": "1f438", "shopping_cart": "1f6d2", "family_man_boy_medium_light_skin_tone": "1f468-1f3fc", "basketball_man_medium_dark_skin_tone": "26f9-1f3fe-200d-2640-fe0f", "oman": "1f1f4-1f1f2", "paraguay": "1f1f5-1f1fe", "horse": "1f434", "tram": "1f68a", "wastebasket": "1f5d1", "yen": "1f4b4", "heavy_exclamation_mark": "2757-fe0f", "arrow_double_down": "23ec", "walking_woman_dark_skin_tone": "1f6b6-1f3ff-200d-2640-fe0f", "shoe": "1f45e", "ear_of_rice": "1f33e", "mountain": "26f0", "uzbekistan": "1f1fa-1f1ff", "baby_light_skin_tone": "1f476-1f3fb", "haircut_woman_medium_dark_skin_tone": "1f487-1f3fe-200d-2640-fe0f", "golfing_woman_medium_skin_tone": "1f3cc-1f3fd-200d-2640-fe0f", "earth_americas": "1f30e", "woman_playing_water_polo_medium_light_skin_tone": "1f93d-1f3fc-200d-2640-fe0f", "walking_woman": "1f6b6-200d-2640-fe0f", "fried_egg": "1f373", "rocket": "1f680", "artificial_satellite": "1f6f0", "man_shrugging_medium_skin_tone": "1f937-1f3fd-200d-2642-fe0f", "golfing_man_dark_skin_tone": "1f3cc-1f3ff-200d-2640-fe0f", "older_woman_medium_skin_tone": "1f475-1f3fd", "man_with_gua_pi_mao_medium_dark_skin_tone": "1f472-1f3fe", "persevere": "1f623", "raising_hand_woman": "1f64b", "pig": "1f437", "european_castle": "1f3f0", "department_store": "1f3ec", "fist_right_light_skin_tone": "1f91c-1f3fb", "raising_hand_woman_dark_skin_tone": "1f64b-1f3ff-200d-2640-fe0f", "paw_prints": "1f43e", "moon": "1f314", "man_medium_skin_tone": "1f468-1f3fd", "rowing_man_dark_skin_tone": "1f6a3-1f3ff-200d-2640-fe0f", "sleepy": "1f62a", "light_rail": "1f688", "peace_symbol": "262e-fe0f", "m": "24c2-fe0f", "woman_pilot_medium_skin_tone": "1f469-1f3fd", "dango": "1f361", "minibus": "1f690", "family_man_man_girl_girl_medium_dark_skin_tone": "1f468-1f3fe", "dizzy_face": "1f635", "bowing_woman": "1f647-200d-2640-fe0f", "pig2": "1f416", "factory": "1f3ed", "small_red_triangle": "1f53a", "ok_man_light_skin_tone": "1f646-1f3fb-200d-2642-fe0f", "two_women_holding_hands": "1f46d", "funeral_urn": "26b1-fe0f", "cocos_islands": "1f1e8-1f1e8", "lipstick": "1f484", "fleur_de_lis": "269c-fe0f", "man_with_gua_pi_mao_dark_skin_tone": "1f472-1f3ff", "woman_factory_worker_medium_dark_skin_tone": "1f469-1f3fe", "no_good_man_medium_light_skin_tone": "1f645-1f3fc-200d-2642-fe0f", "horse_racing_medium_dark_skin_tone": "1f3c7-1f3fe", "clock1030": "1f565", "couplekiss_man_man_dark_skin_tone": "1f468-1f3ff", "frowning_man": "1f64d-200d-2642-fe0f", "family_woman_boy_boy_dark_skin_tone": "1f469-1f3ff", "family_man_girl_boy_light_skin_tone": "1f468-1f3fb", "smile": "1f604", "clock7": "1f556", "massage_man": "1f486-200d-2642-fe0f", "guardswoman_dark_skin_tone": "1f482-1f3ff-200d-2640-fe0f", "raising_hand_man_dark_skin_tone": "1f64b-1f3ff-200d-2642-fe0f", "woman_with_turban_medium_dark_skin_tone": "1f473-1f3fe-200d-2640-fe0f", "worried": "1f61f", "no_good": "1f645", "card_index": "1f4c7", "aland_islands": "1f1e6-1f1fd", "lion": "1f981", "hammer": "1f528", "bomb": "1f4a3", "reunion": "1f1f7-1f1ea", "walking_man_light_skin_tone": "1f6b6-1f3fb-200d-2640-fe0f", "family_woman_boy_medium_light_skin_tone": "1f469-1f3fc", "pouting_cat": "1f63e", "cow": "1f42e", "motor_scooter": "1f6f5", "hong_kong": "1f1ed-1f1f0", "family_man_girl_medium_dark_skin_tone": "1f468-1f3fe", "sailboat": "26f5-fe0f", "fiji": "1f1eb-1f1ef", "raised_hands_medium_light_skin_tone": "1f64c-1f3fc", "woman_office_worker_dark_skin_tone": "1f469-1f3ff", "family_man_woman_girl_girl_medium_light_skin_tone": "1f468-1f3fc", "arrow_up": "2b06-fe0f", "walking_woman_medium_light_skin_tone": "1f6b6-1f3fc-200d-2640-fe0f", "nose_medium_light_skin_tone": "1f443-1f3fc", "basketball_woman": "26f9-fe0f-200d-2640-fe0f", "+1_medium_light_skin_tone": "1f44d-1f3fc", "crossed_fingers_medium_skin_tone": "1f91e-1f3fd", "raised_back_of_hand_dark_skin_tone": "1f91a-1f3ff", "swimming_woman_medium_light_skin_tone": "1f3ca-1f3fc-200d-2640-fe0f", "construction_worker_woman": "1f477-200d-2640-fe0f", "rugby_football": "1f3c9", "micronesia": "1f1eb-1f1f2", "point_up_2_medium_light_skin_tone": "1f446-1f3fc", "running_man_dark_skin_tone": "1f3c3-1f3ff-200d-2640-fe0f", "woman_playing_handball_medium_light_skin_tone": "1f93e-1f3fc-200d-2640-fe0f", "speaker": "1f508", "jersey": "1f1ef-1f1ea", "laughing": "1f606", "pregnant_woman": "1f930", "haircut_woman": "1f487", "blue_car": "1f699", "microscope": "1f52c", "postbox": "1f4ee", "man_firefighter_dark_skin_tone": "1f468-1f3ff", "sunny": "2600-fe0f", "beginner": "1f530", "clap_medium_light_skin_tone": "1f44f-1f3fc", "man_with_turban_dark_skin_tone": "1f473-1f3ff-200d-2640-fe0f", "rotating_light": "1f6a8", "saudi_arabia": "1f1f8-1f1e6", "family_woman_woman_girl_girl_medium_skin_tone": "1f469-1f3fd", "family_woman_girl_boy_light_skin_tone": "1f469-1f3fb", "man_with_gua_pi_mao": "1f472", "electric_plug": "1f50c", "panama": "1f1f5-1f1e6", "family_woman_woman_girl_light_skin_tone": "1f469-1f3fb", "thinking": "1f914", "point_down": "1f447", "spider": "1f577", "cloud_with_lightning_and_rain": "26c8", "ice_skate": "26f8", "ok_man_medium_dark_skin_tone": "1f646-1f3fe-200d-2642-fe0f", "netherlands": "1f1f3-1f1f1", "family_man_woman_boy": "1f46a", "orange": "1f34a", "snowboarder": "1f3c2", "passenger_ship": "1f6f3", "arrows_counterclockwise": "1f504", "tractor": "1f69c", "gambia": "1f1ec-1f1f2", "middle_finger_dark_skin_tone": "1f595-1f3ff", "tipping_hand_woman_medium_dark_skin_tone": "1f481-1f3fe-200d-2640-fe0f", "family_man_man_girl_boy_medium_light_skin_tone": "1f468-1f3fc", "thumbsup": "1f44d", "couple": "1f46b", "pouch": "1f45d", "asterisk": "002a-fe0f-20e3", "anguilla": "1f1e6-1f1ee", "woman_cook_light_skin_tone": "1f469-1f3fb", "kissing_cat": "1f63d", "nose": "1f443", "point_left_medium_skin_tone": "1f448-1f3fd", "baby_chick": "1f424", "deciduous_tree": "1f333", "u7533": "1f238", "surfing_woman_dark_skin_tone": "1f3c4-1f3ff-200d-2640-fe0f", "woman_shrugging_medium_dark_skin_tone": "1f937-1f3fe-200d-2640-fe0f", "family_woman_woman_boy_boy_dark_skin_tone": "1f469-1f3ff", "cloud_with_rain": "1f327", "oden": "1f362", "botswana": "1f1e7-1f1fc", "greenland": "1f1ec-1f1f1", "man_office_worker_light_skin_tone": "1f468-1f3fb", "raising_hand_woman_medium_dark_skin_tone": "1f64b-1f3fe-200d-2640-fe0f", "family_man_man_girl_boy_medium_dark_skin_tone": "1f468-1f3fe", "school": "1f3eb", "woman_astronaut_light_skin_tone": "1f469-1f3fb", "woman_judge_medium_skin_tone": "1f469-1f3fd", "dancing_men": "1f46f-200d-2642-fe0f", "paperclips": "1f587", "underage": "1f51e", "ok_woman_dark_skin_tone": "1f646-1f3ff-200d-2640-fe0f", "man_playing_handball_dark_skin_tone": "1f93e-1f3ff-200d-2642-fe0f", "family_man_girl_girl": "1f468-200d-1f467-200d-1f467", "wind_face": "1f32c", "banana": "1f34c", "eight": "0038-fe0f-20e3", "man_technologist_medium_dark_skin_tone": "1f468-1f3fe", "man_office_worker_medium_skin_tone": "1f468-1f3fd", "walking_man_dark_skin_tone": "1f6b6-1f3ff-200d-2640-fe0f", "family_man_man_girl_girl_medium_skin_tone": "1f468-1f3fd", "snowman": "26c4-fe0f", "basketball_man": "26f9-fe0f", "information_source": "2139-fe0f", "cote_divoire": "1f1e8-1f1ee", "man_in_tuxedo_light_skin_tone": "1f935-1f3fb", "walking_woman_light_skin_tone": "1f6b6-1f3fb-200d-2640-fe0f", "woman_playing_water_polo_light_skin_tone": "1f93d-1f3fb-200d-2640-fe0f", "bird": "1f426", "o": "2b55-fe0f", "family_woman_girl_medium_skin_tone": "1f469-1f3fd", "rowing_woman_dark_skin_tone": "1f6a3-1f3ff-200d-2640-fe0f", "facepunch": "1f44a", "railway_car": "1f683", "wave_dark_skin_tone": "1f44b-1f3ff", "man_cook_medium_dark_skin_tone": "1f468-1f3fe", "prince_medium_light_skin_tone": "1f934-1f3fc", "cowboy_hat_face": "1f920", "handbag": "1f45c", "hourglass": "231b-fe0f", "albania": "1f1e6-1f1f1", "chile": "1f1e8-1f1f1", "woman_singer_medium_skin_tone": "1f469-1f3fd", "ear_medium_skin_tone": "1f442-1f3fd", "pouting_man_medium_skin_tone": "1f64e-1f3fd-200d-2642-fe0f", "surfing_man_medium_light_skin_tone": "1f3c4-1f3fc-200d-2640-fe0f", "eggplant": "1f346", "next_track_button": "23ed", "gabon": "1f1ec-1f1e6", "western_sahara": "1f1ea-1f1ed", "raised_hands_light_skin_tone": "1f64c-1f3fb", "older_woman_medium_light_skin_tone": "1f475-1f3fc", "joy_cat": "1f639", "feet": "1f43e", "partly_sunny": "26c5-fe0f", "pig_nose": "1f43d", "wc": "1f6be", "malaysia": "1f1f2-1f1fe", "girl_medium_light_skin_tone": "1f467-1f3fc", "man_office_worker_medium_dark_skin_tone": "1f468-1f3fe", "man_mechanic_medium_light_skin_tone": "1f468-1f3fc", "shamrock": "2618-fe0f", "tumbler_glass": "1f943", "palestinian_territories": "1f1f5-1f1f8", "kissing": "1f617", "city_sunset": "1f306", "pencil2": "270f-fe0f", "cool": "1f192", "australia": "1f1e6-1f1fa", "green_heart": "1f49a", "sparkle": "2747-fe0f", "ng_woman": "1f645", "high_heel": "1f460", "hamster": "1f439", "last_quarter_moon": "1f317", "stopwatch": "23f1", "date": "1f4c5", "nail_care_dark_skin_tone": "1f485-1f3ff", "santa_dark_skin_tone": "1f385-1f3ff", "astonished": "1f632", "mushroom": "1f344", "radio": "1f4fb", "hammer_and_wrench": "1f6e0", "arrow_down": "2b07-fe0f", "speech_balloon": "1f4ac", "couple_with_heart_man_man_medium_skin_tone": "1f468-1f3fd", "euro": "1f4b6", "es": "1f1ea-1f1f8", "woman_factory_worker_medium_light_skin_tone": "1f469-1f3fc", "pouting_woman_dark_skin_tone": "1f64e-1f3ff-200d-2640-fe0f", "massage_woman": "1f486", "spades": "2660-fe0f", "blonde_woman_dark_skin_tone": "1f471-1f3ff-200d-2640-fe0f", "man_farmer_medium_skin_tone": "1f468-1f3fd", "man_mechanic_medium_skin_tone": "1f468-1f3fd", "family_man_boy_dark_skin_tone": "1f468-1f3ff", "man_juggling_medium_light_skin_tone": "1f939-1f3fc-200d-2642-fe0f", "hearts": "2665-fe0f", "clock930": "1f564", "central_african_republic": "1f1e8-1f1eb", "boy_medium_skin_tone": "1f466-1f3fd", "pregnant_woman_medium_skin_tone": "1f930-1f3fd", "woman_facepalming_medium_light_skin_tone": "1f926-1f3fc-200d-2640-fe0f", "palm_tree": "1f334", "rose": "1f339", "beers": "1f37b", "red_car": "1f697", "no_entry": "26d4-fe0f", "candy": "1f36c", "fist_oncoming_medium_skin_tone": "1f44a-1f3fd", "rowing_woman_medium_skin_tone": "1f6a3-1f3fd-200d-2640-fe0f", "sake": "1f376", "oncoming_police_car": "1f694", "woman_teacher_medium_dark_skin_tone": "1f469-1f3fe", "family_man_woman_girl_girl_medium_skin_tone": "1f468-1f3fd", "kissing_closed_eyes": "1f61a", "pager": "1f4df", "pencil": "1f4dd", "copyright": "00a9-fe0f", "wave_medium_skin_tone": "1f44b-1f3fd", "loud_sound": "1f50a", "luxembourg": "1f1f1-1f1fa", "policewoman_dark_skin_tone": "1f46e-1f3ff-200d-2640-fe0f", "woman_cartwheeling_medium_skin_tone": "1f938-1f3fd-200d-2640-fe0f", "swimming_woman_medium_dark_skin_tone": "1f3ca-1f3fe-200d-2640-fe0f", "family_man_man_girl_boy": "1f468-200d-1f468-200d-1f467-200d-1f466", "police_car": "1f693", "mailbox_with_no_mail": "1f4ed", "middle_finger_light_skin_tone": "1f595-1f3fb", "pregnant_woman_medium_light_skin_tone": "1f930-1f3fc", "raising_hand_woman_medium_skin_tone": "1f64b-1f3fd-200d-2640-fe0f", "running": "1f3c3", "sun_with_face": "1f31e", "man_teacher_dark_skin_tone": "1f468-1f3ff", "family_man_woman_girl_girl_dark_skin_tone": "1f468-1f3ff", "izakaya_lantern": "1f3ee", "comoros": "1f1f0-1f1f2", "fist_oncoming_medium_dark_skin_tone": "1f44a-1f3fe", "man_singer": "1f468-200d-1f3a4", "mountain_bicyclist": "1f6b5", "point_down_light_skin_tone": "1f447-1f3fb", "family_man_woman_girl_boy_medium_dark_skin_tone": "1f468-1f3fe", "sob": "1f62d", "ophiuchus": "26ce", "greece": "1f1ec-1f1f7", "raised_back_of_hand_medium_skin_tone": "1f91a-1f3fd", "family_man_man_boy_light_skin_tone": "1f468-1f3fb", "woman_cartwheeling_light_skin_tone": "1f938-1f3fb-200d-2640-fe0f", "massage_woman_light_skin_tone": "1f486-1f3fb-200d-2640-fe0f", "fishing_pole_and_fish": "1f3a3", "two_hearts": "1f495", "armenia": "1f1e6-1f1f2", "south_africa": "1f1ff-1f1e6", "boy_light_skin_tone": "1f466-1f3fb", "man_in_tuxedo_medium_dark_skin_tone": "1f935-1f3fe", "kiribati": "1f1f0-1f1ee", "v_dark_skin_tone": "270c-1f3ff", "frowning_man_medium_light_skin_tone": "1f64d-1f3fc-200d-2642-fe0f", "family_woman_woman_girl_boy": "1f469-200d-1f469-200d-1f467-200d-1f466", "family_woman_girl_boy_medium_dark_skin_tone": "1f469-1f3fe", "leopard": "1f406", "fireworks": "1f386", "clock6": "1f555", "bowing_man_medium_light_skin_tone": "1f647-1f3fc-200d-2640-fe0f", "raising_hand": "1f64b", "family_man_woman_girl_girl_light_skin_tone": "1f468-1f3fb", "vulcan_salute_medium_light_skin_tone": "1f596-1f3fc", "guardswoman_medium_light_skin_tone": "1f482-1f3fc-200d-2640-fe0f", "muscle": "1f4aa", "full_moon": "1f315", "pisces": "2653-fe0f", "kosovo": "1f1fd-1f1f0", "fist_left_dark_skin_tone": "1f91b-1f3ff", "point_up_2_dark_skin_tone": "1f446-1f3ff", "man_technologist_dark_skin_tone": "1f468-1f3ff", "spoon": "1f944", "nigeria": "1f1f3-1f1ec", "raised_back_of_hand_medium_light_skin_tone": "1f91a-1f3fc", "blonde_woman_light_skin_tone": "1f471-1f3fb-200d-2640-fe0f", "man_dancing_light_skin_tone": "1f57a-1f3fb", "shrimp": "1f990", "mountain_biking_man": "1f6b5", "boat": "26f5-fe0f", "egypt": "1f1ea-1f1ec", "family_woman_woman_boy_light_skin_tone": "1f469-1f3fb", "man_playing_water_polo_light_skin_tone": "1f93d-1f3fb-200d-2642-fe0f", "family_man_man_boy_boy": "1f468-200d-1f468-200d-1f466-200d-1f466", "foggy": "1f301", "construction_worker_woman_medium_light_skin_tone": "1f477-1f3fc-200d-2640-fe0f", "princess_medium_skin_tone": "1f478-1f3fd", "man_dancing_medium_dark_skin_tone": "1f57a-1f3fe", "couple_with_heart_man_man_dark_skin_tone": "1f468-1f3ff", "carousel_horse": "1f3a0", "crayon": "1f58d", "niue": "1f1f3-1f1fa", "woman_office_worker_medium_skin_tone": "1f469-1f3fd", "swimming_man_medium_skin_tone": "1f3ca-1f3fd-200d-2640-fe0f", "pensive": "1f614", "fire": "1f525", "monorail": "1f69d", "guam": "1f1ec-1f1fa", "older_woman_light_skin_tone": "1f475-1f3fb", "man_facepalming_medium_light_skin_tone": "1f926-1f3fc-200d-2642-fe0f", "family_man_man_girl": "1f468-200d-1f468-200d-1f467", "hammer_and_pick": "2692", "space_invader": "1f47e", "waning_crescent_moon": "1f318", "love_letter": "1f48c", "star_and_crescent": "262a-fe0f", "man_with_turban_light_skin_tone": "1f473-1f3fb-200d-2640-fe0f", "tipping_hand_woman_light_skin_tone": "1f481-1f3fb-200d-2640-fe0f", "dress": "1f457", "rainbow": "1f308", "cheese": "1f9c0", "bento": "1f371", "gear": "2699-fe0f", "-1_medium_skin_tone": "1f44e-1f3fd", "family_man_girl_boy_dark_skin_tone": "1f468-1f3ff", "fish_cake": "1f365", "desert_island": "1f3dd", "crystal_ball": "1f52e", "lock": "1f512", "no_good_man_medium_skin_tone": "1f645-1f3fd-200d-2642-fe0f", "small_blue_diamond": "1f539", "fist_raised_medium_dark_skin_tone": "270a-1f3fe", "man_health_worker_medium_light_skin_tone": "1f468-1f3fc", "ok_man_medium_light_skin_tone": "1f646-1f3fc-200d-2642-fe0f", "man_cartwheeling_dark_skin_tone": "1f938-1f3ff-200d-2642-fe0f", "policeman": "1f46e", "closed_lock_with_key": "1f510", "koko": "1f201", "guardswoman": "1f482-200d-2640-fe0f", "mailbox": "1f4eb", "weight_lifting_woman_light_skin_tone": "1f3cb-1f3fb-200d-2640-fe0f", "drooling_face": "1f924", "motorway": "1f6e3", "orthodox_cross": "2626-fe0f", "peru": "1f1f5-1f1ea", "woman_firefighter_medium_light_skin_tone": "1f469-1f3fc", "atom_symbol": "269b-fe0f", "benin": "1f1e7-1f1ef", "montenegro": "1f1f2-1f1ea", "tonga": "1f1f9-1f1f4", "family_man_boy_boy_medium_skin_tone": "1f468-1f3fd", "man_mechanic_light_skin_tone": "1f468-1f3fb", "female_detective": "1f575-fe0f-200d-2640-fe0f", "closed_umbrella": "1f302", "cow2": "1f404", "ballot_box": "1f5f3", "construction_worker_man_dark_skin_tone": "1f477-1f3ff-200d-2640-fe0f", "woman_technologist_medium_dark_skin_tone": "1f469-1f3fe", "indonesia": "1f1ee-1f1e9", "woman_pilot_medium_light_skin_tone": "1f469-1f3fc", "family_man_man_boy_boy_medium_light_skin_tone": "1f468-1f3fc", "call_me_hand": "1f919", "sun_behind_small_cloud": "1f324", "national_park": "1f3de", "radio_button": "1f518", "selfie_medium_light_skin_tone": "1f933-1f3fc", "woman_firefighter": "1f469-200d-1f692", "metal_dark_skin_tone": "1f918-1f3ff", "older_woman": "1f475", "man_factory_worker_medium_skin_tone": "1f468-1f3fd", "pick": "26cf", "woman_student_medium_skin_tone": "1f469-1f3fd", "mountain_biking_woman_light_skin_tone": "1f6b5-1f3fb-200d-2640-fe0f", "flags": "1f38f", "black_nib": "2712-fe0f", "rwanda": "1f1f7-1f1fc", "surfing_man_light_skin_tone": "1f3c4-1f3fb-200d-2640-fe0f", "first_quarter_moon": "1f313", "oil_drum": "1f6e2", "heart_decoration": "1f49f", "jp": "1f1ef-1f1f5", "woman_pilot": "1f469-200d-2708-fe0f", "city_sunrise": "1f307", "leo": "264c-fe0f", "arrow_up_down": "2195-fe0f", "selfie_medium_skin_tone": "1f933-1f3fd", "surfing_man_medium_skin_tone": "1f3c4-1f3fd-200d-2640-fe0f", "ramen": "1f35c", "up": "1f199", "woman_medium_light_skin_tone": "1f469-1f3fc", "woman_artist": "1f469-200d-1f3a8", "football": "1f3c8", "shopping": "1f6cd", "small_red_triangle_down": "1f53b", "crossed_fingers_light_skin_tone": "1f91e-1f3fb", "woman_artist_medium_dark_skin_tone": "1f469-1f3fe", "milk_glass": "1f95b", "clapper": "1f3ac", "star_of_david": "2721-fe0f", "dominican_republic": "1f1e9-1f1f4", "woman_teacher_light_skin_tone": "1f469-1f3fb", "man_juggling_medium_skin_tone": "1f939-1f3fd-200d-2642-fe0f", "-1": "1f44e", "wedding": "1f492", "faroe_islands": "1f1eb-1f1f4", "raising_hand_man_medium_dark_skin_tone": "1f64b-1f3fe-200d-2642-fe0f", "gemini": "264a-fe0f", "st_helena": "1f1f8-1f1ed", "running_woman_medium_light_skin_tone": "1f3c3-1f3fc-200d-2640-fe0f", "biking_woman_light_skin_tone": "1f6b4-1f3fb-200d-2640-fe0f", "paperclip": "1f4ce", "wave_medium_light_skin_tone": "1f44b-1f3fc", "man_factory_worker_medium_dark_skin_tone": "1f468-1f3fe", "woman_cartwheeling_medium_dark_skin_tone": "1f938-1f3fe-200d-2640-fe0f", "clock12": "1f55b", "ru": "1f1f7-1f1fa", "clown_face": "1f921", "pizza": "1f355", "hole": "1f573", "incoming_envelope": "1f4e8", "yin_yang": "262f-fe0f", "warning": "26a0-fe0f", "family_man_man_girl_boy_dark_skin_tone": "1f468-1f3ff", "man_cartwheeling_medium_skin_tone": "1f938-1f3fd-200d-2642-fe0f", "ram": "1f40f", "cucumber": "1f952", "heartbeat": "1f493", "swaziland": "1f1f8-1f1ff", "nail_care_medium_dark_skin_tone": "1f485-1f3fe", "bath_medium_skin_tone": "1f6c0-1f3fd", "strawberry": "1f353", "peanuts": "1f95c", "field_hockey": "1f3d1", "cricket": "1f3cf", "woman_farmer_medium_dark_skin_tone": "1f469-1f3fe", "family_man_man_girl_girl_light_skin_tone": "1f468-1f3fb", "penguin": "1f427", "star": "2b50-fe0f", "woman_shrugging_light_skin_tone": "1f937-1f3fb-200d-2640-fe0f", "golfing_man_light_skin_tone": "1f3cc-1f3fb-200d-2640-fe0f", "innocent": "1f607", "mosque": "1f54c", "calendar": "1f4c6", "canada": "1f1e8-1f1e6", "rage4": "rage4", "woman_office_worker_medium_dark_skin_tone": "1f469-1f3fe", "poodle": "1f429", "grapes": "1f347", "love_hotel": "1f3e9", "vulcan_salute_medium_skin_tone": "1f596-1f3fd", "guardsman_medium_dark_skin_tone": "1f482-1f3fe-200d-2640-fe0f", "raising_hand_man_light_skin_tone": "1f64b-1f3fb-200d-2642-fe0f", "sleeping": "1f634", "nail_care": "1f485", "monkey": "1f412", "sao_tome_principe": "1f1f8-1f1f9", "dancer_medium_dark_skin_tone": "1f483-1f3fe", "classical_building": "1f3db", "swimming_woman_medium_skin_tone": "1f3ca-1f3fd-200d-2640-fe0f", "ok_hand": "1f44c", "rice_cracker": "1f358", "moyai": "1f5ff", "rage2": "rage2", "angel_light_skin_tone": "1f47c-1f3fb", "family_man_man_boy_boy_dark_skin_tone": "1f468-1f3ff", "smile_cat": "1f638", "angola": "1f1e6-1f1f4", "cameroon": "1f1e8-1f1f2", "man_student_medium_dark_skin_tone": "1f468-1f3fe", "weight_lifting_woman_medium_light_skin_tone": "1f3cb-1f3fc-200d-2640-fe0f", "waxing_crescent_moon": "1f312", "articulated_lorry": "1f69b", "pouting_woman_light_skin_tone": "1f64e-1f3fb-200d-2640-fe0f", "running_man_medium_dark_skin_tone": "1f3c3-1f3fe-200d-2640-fe0f", "couple_with_heart_woman_woman_light_skin_tone": "1f469-1f3fb", "horse_racing_light_skin_tone": "1f3c7-1f3fb", "raised_back_of_hand": "1f91a", "saxophone": "1f3b7", "right_anger_bubble": "1f5ef", "tokelau": "1f1f9-1f1f0", "no_good_woman_medium_light_skin_tone": "1f645-1f3fc-200d-2640-fe0f", "walking_woman_medium_skin_tone": "1f6b6-1f3fd-200d-2640-fe0f", "family_woman_girl_girl": "1f469-200d-1f467-200d-1f467", "cake": "1f370", "abcd": "1f521", "tuvalu": "1f1f9-1f1fb", "suspect": "suspect", "mattermost": "mattermost", "swimming_woman_light_skin_tone": "1f3ca-1f3fb-200d-2640-fe0f", "white_medium_square": "25fb-fe0f", "haircut_woman_medium_skin_tone": "1f487-1f3fd-200d-2640-fe0f", "massage_woman_dark_skin_tone": "1f486-1f3ff-200d-2640-fe0f", "family_man_woman_girl_light_skin_tone": "1f468-1f3fb", "turks_caicos_islands": "1f1f9-1f1e8", "point_left_dark_skin_tone": "1f448-1f3ff", "family_man_man_boy_medium_dark_skin_tone": "1f468-1f3fe", "hand": "270b", "coffee": "2615-fe0f", "somalia": "1f1f8-1f1f4", "mountain_biking_man_dark_skin_tone": "1f6b5-1f3ff-200d-2640-fe0f", "hatching_chick": "1f423", "pear": "1f350", "baby_bottle": "1f37c", "ribbon": "1f380", "st_kitts_nevis": "1f1f0-1f1f3", "radioactive": "2622-fe0f", "end": "1f51a", "hand_medium_skin_tone": "270b-1f3fd", "family_woman_woman_girl_medium_light_skin_tone": "1f469-1f3fc", "3rd_place_medal": "1f949", "fist_left_medium_dark_skin_tone": "1f91b-1f3fe", "bolivia": "1f1e7-1f1f4", "point_up_light_skin_tone": "261d-1f3fb", "cherries": "1f352", "inbox_tray": "1f4e5", "pitcairn_islands": "1f1f5-1f1f3", "rage1": "rage1", "man_farmer_medium_dark_skin_tone": "1f468-1f3fe", "woman_with_turban": "1f473-200d-2640-fe0f", "unicorn": "1f984", "butterfly": "1f98b", "watch": "231a-fe0f", "arrow_up_small": "1f53c", "triangular_flag_on_post": "1f6a9", "heart_eyes": "1f60d", "shallow_pan_of_food": "1f958", "broken_heart": "1f494", "family_man_boy_boy_dark_skin_tone": "1f468-1f3ff", "golfing_woman_dark_skin_tone": "1f3cc-1f3ff-200d-2640-fe0f", "bath_dark_skin_tone": "1f6c0-1f3ff", "selfie": "1f933", "congratulations": "3297-fe0f", "baby_medium_light_skin_tone": "1f476-1f3fc", "woman_health_worker_medium_skin_tone": "1f469-1f3fd", "man_juggling": "1f939-200d-2642-fe0f", "arrow_down_small": "1f53d", "writing_hand_medium_light_skin_tone": "270d-1f3fc", "blonde_woman": "1f471-200d-2640-fe0f", "massage": "1f486", "metro": "1f687", "bath": "1f6c0", "female_detective_light_skin_tone": "1f575-1f3fb-200d-2640-fe0f", "haircut_man_light_skin_tone": "1f487-1f3fb-200d-2642-fe0f", "bowing_woman_medium_dark_skin_tone": "1f647-1f3fe-200d-2640-fe0f", "family_woman_woman_boy_medium_dark_skin_tone": "1f469-1f3fe", "shell": "1f41a", "seychelles": "1f1f8-1f1e8", "tipping_hand_man_medium_skin_tone": "1f481-1f3fd-200d-2642-fe0f", "panda_face": "1f43c", "sint_maarten": "1f1f8-1f1fd", "face_with_head_bandage": "1f915", "checkered_flag": "1f3c1", "samoa": "1f1fc-1f1f8", "v_medium_skin_tone": "270c-1f3fd", "couple_with_heart_man_man": "1f468-200d-2764-fe0f-200d-1f468", "shaved_ice": "1f367", "badminton": "1f3f8", "clock530": "1f560", "man_playing_water_polo_medium_dark_skin_tone": "1f93d-1f3fe-200d-2642-fe0f", "bulgaria": "1f1e7-1f1ec", "hurtrealbad": "hurtrealbad", "fist_oncoming_dark_skin_tone": "1f44a-1f3ff", "bat": "1f987", "signal_strength": "1f4f6", "iran": "1f1ee-1f1f7", "construction_worker_woman_medium_dark_skin_tone": "1f477-1f3fe-200d-2640-fe0f", "kiwi_fruit": "1f95d", "2nd_place_medal": "1f948", "kaaba": "1f54b", "knife": "1f52a", "ok_hand_light_skin_tone": "1f44c-1f3fb", "angel_medium_dark_skin_tone": "1f47c-1f3fe", "spider_web": "1f578", "oncoming_taxi": "1f696", "bookmark": "1f516", "u6307": "1f22f-fe0f", "za": "1f1ff-1f1e6", "fist_raised_light_skin_tone": "270a-1f3fb", "mag_right": "1f50e", "guinea": "1f1ec-1f1f3", "family_woman_woman_girl_dark_skin_tone": "1f469-1f3ff", "man_playing_handball_medium_light_skin_tone": "1f93e-1f3fc-200d-2642-fe0f", "game_die": "1f3b2", "bullettrain_front": "1f685", "speedboat": "1f6a4", "hand_dark_skin_tone": "270b-1f3ff", "selfie_light_skin_tone": "1f933-1f3fb", "family_man_woman_boy_boy_dark_skin_tone": "1f468-1f3ff", "running_man": "1f3c3", "couplekiss_woman_woman": "1f469-200d-2764-fe0f-200d-1f48b-200d-1f469", "woman_teacher": "1f469-200d-1f3eb", "running_shirt_with_sash": "1f3bd", "bowing_man_medium_skin_tone": "1f647-1f3fd-200d-2640-fe0f", "point_right_medium_dark_skin_tone": "1f449-1f3fe", "man_cartwheeling_medium_light_skin_tone": "1f938-1f3fc-200d-2642-fe0f", "pouting_man_light_skin_tone": "1f64e-1f3fb-200d-2642-fe0f", "biking_man_light_skin_tone": "1f6b4-1f3fb-200d-2640-fe0f", "oncoming_automobile": "1f698", "steam_locomotive": "1f682", "newspaper": "1f4f0", "antigua_barbuda": "1f1e6-1f1ec", "macau": "1f1f2-1f1f4", "niger": "1f1f3-1f1ea", "chicken": "1f414", "flashlight": "1f526", "family_man_woman_boy_boy_medium_skin_tone": "1f468-1f3fd", "mens": "1f6b9", "it": "1f1ee-1f1f9", "new_caledonia": "1f1f3-1f1e8", "pray_medium_light_skin_tone": "1f64f-1f3fc", "nose_medium_dark_skin_tone": "1f443-1f3fe", "man_facepalming_medium_dark_skin_tone": "1f926-1f3fe-200d-2642-fe0f", "poop": "1f4a9", "clap_light_skin_tone": "1f44f-1f3fb", "guardsman_medium_skin_tone": "1f482-1f3fd-200d-2640-fe0f", "woman_teacher_dark_skin_tone": "1f469-1f3ff", "grinning": "1f600", "aries": "2648-fe0f", "mrs_claus": "1f936", "green_book": "1f4d7", "middle_finger_medium_dark_skin_tone": "1f595-1f3fe", "rowing_woman_light_skin_tone": "1f6a3-1f3fb-200d-2640-fe0f", "tokyo_tower": "1f5fc", "printer": "1f5a8", "put_litter_in_its_place": "1f6ae", "suriname": "1f1f8-1f1f7", "woman_light_skin_tone": "1f469-1f3fb", "man_playing_water_polo_medium_light_skin_tone": "1f93d-1f3fc-200d-2642-fe0f", "dove": "1f54a", "latin_cross": "271d-fe0f", "exclamation": "2757-fe0f", "man_health_worker_dark_skin_tone": "1f468-1f3ff", "bride_with_veil_light_skin_tone": "1f470-1f3fb", "rowboat": "1f6a3", "world_map": "1f5fa", "sleeping_bed": "1f6cc", "haircut_man_dark_skin_tone": "1f487-1f3ff-200d-2642-fe0f", "surfing_man_dark_skin_tone": "1f3c4-1f3ff-200d-2640-fe0f", "couple_with_heart_woman_man": "1f491", "chart_with_upwards_trend": "1f4c8", "fist_right_dark_skin_tone": "1f91c-1f3ff", "raised_hand_with_fingers_splayed_medium_skin_tone": "1f590-1f3fd", "older_woman_medium_dark_skin_tone": "1f475-1f3fe", "couplekiss_woman_woman_medium_dark_skin_tone": "1f469-1f3fe", "hatched_chick": "1f425", "running_man_medium_skin_tone": "1f3c3-1f3fd-200d-2640-fe0f", "chocolate_bar": "1f36b", "grenada": "1f1ec-1f1e9", "man_farmer_dark_skin_tone": "1f468-1f3ff", "milky_way": "1f30c", "slovakia": "1f1f8-1f1f0", "selfie_dark_skin_tone": "1f933-1f3ff", "prince_medium_skin_tone": "1f934-1f3fd", "family_man_boy": "1f468-200d-1f466", "chains": "26d3", "british_virgin_islands": "1f1fb-1f1ec", "bow_and_arrow": "1f3f9", "ferry": "26f4", "o2": "1f17e-fe0f", "st_barthelemy": "1f1e7-1f1f1", "policewoman_medium_skin_tone": "1f46e-1f3fd-200d-2640-fe0f", "imp": "1f47f", "bathtub": "1f6c1", "anger": "1f4a2", "previous_track_button": "23ee", "pouting_woman_medium_light_skin_tone": "1f64e-1f3fc-200d-2640-fe0f", "rabbit2": "1f407", "newspaper_roll": "1f5de", "one": "0031-fe0f-20e3", "family_man_man_girl_medium_skin_tone": "1f468-1f3fd", "pouting_woman": "1f64e", "moneybag": "1f4b0", "guardsman_dark_skin_tone": "1f482-1f3ff-200d-2640-fe0f", "man_shrugging_medium_light_skin_tone": "1f937-1f3fc-200d-2642-fe0f", "smirk": "1f60f", "woman_farmer": "1f469-200d-1f33e", "ocean": "1f30a", "sweat_drops": "1f4a6", "x": "274c", "woman_judge_medium_dark_skin_tone": "1f469-1f3fe", "tropical_drink": "1f379", "brunei": "1f1e7-1f1f3", "woman_artist_medium_light_skin_tone": "1f469-1f3fc", "pregnant_woman_medium_dark_skin_tone": "1f930-1f3fe", "basketball_woman_light_skin_tone": "26f9-1f3fb-200d-2640-fe0f", "evergreen_tree": "1f332", "fax": "1f4e0", "woman_medium_dark_skin_tone": "1f469-1f3fe", "walking_woman_medium_dark_skin_tone": "1f6b6-1f3fe-200d-2640-fe0f", "lollipop": "1f36d", "bicyclist": "1f6b4", "bulb": "1f4a1", "computer": "1f4bb", "frowning_man_dark_skin_tone": "1f64d-1f3ff-200d-2642-fe0f", "guardsman_medium_light_skin_tone": "1f482-1f3fc-200d-2640-fe0f", "dancer_light_skin_tone": "1f483-1f3fb", "no_good_woman": "1f645", "cherry_blossom": "1f338", "woman_playing_water_polo": "1f93d-200d-2640-fe0f", "heavy_division_sign": "2797", "sri_lanka": "1f1f1-1f1f0", "-1_medium_light_skin_tone": "1f44e-1f3fc", "family_woman_girl_girl_dark_skin_tone": "1f469-1f3ff", "raised_hands": "1f64c", "sandal": "1f461", "rhinoceros": "1f98f", "swimming_man": "1f3ca", "scissors": "2702-fe0f", "horse_racing_dark_skin_tone": "1f3c7-1f3ff", "coffin": "26b0-fe0f", "clock1": "1f550", "eritrea": "1f1ea-1f1f7", "qatar": "1f1f6-1f1e6", "tanzania": "1f1f9-1f1ff", "pregnant_woman_light_skin_tone": "1f930-1f3fb", "cop": "1f46e", "tipping_hand_woman": "1f481", "estonia": "1f1ea-1f1ea", "man_singer_light_skin_tone": "1f468-1f3fb", "woman_judge_dark_skin_tone": "1f469-1f3ff", "business_suit_levitating_medium_skin_tone": "1f574-1f3fd", "blowfish": "1f421", "mountain_railway": "1f69e", "fast_forward": "23e9", "+1_medium_skin_tone": "1f44d-1f3fd", "goat": "1f410", "congo_kinshasa": "1f1e8-1f1e9", "point_down_dark_skin_tone": "1f447-1f3ff", "basketball_man_light_skin_tone": "26f9-1f3fb-200d-2640-fe0f", "woman_playing_handball_medium_dark_skin_tone": "1f93e-1f3fe-200d-2640-fe0f", "doughnut": "1f369", "musical_keyboard": "1f3b9", "couplekiss_woman_woman_medium_skin_tone": "1f469-1f3fd", "heavy_heart_exclamation": "2763-fe0f", "u6e80": "1f235", "woman_with_turban_medium_skin_tone": "1f473-1f3fd-200d-2640-fe0f", "horse_racing_medium_skin_tone": "1f3c7-1f3fd", "ear": "1f442", "canoe": "1f6f6", "andorra": "1f1e6-1f1e9", "ca": "1f1e8-1f1e6", "family_man_man_boy": "1f468-200d-1f468-200d-1f466", "ticket": "1f3ab", "station": "1f689", "large_blue_circle": "1f535", "palau": "1f1f5-1f1fc", "blush": "1f60a", "man_student": "1f468-200d-1f393", "woman_singer": "1f469-200d-1f3a4", "house_with_garden": "1f3e1", "smoking": "1f6ac", "b": "1f171-fe0f", "golfing_woman_light_skin_tone": "1f3cc-1f3fb-200d-2640-fe0f", "man_artist_light_skin_tone": "1f468-1f3fb", "unamused": "1f612", "japanese_ogre": "1f479", "film_projector": "1f4fd", "ballot_box_with_check": "2611-fe0f", "goberserk": "goberserk", "metal_medium_light_skin_tone": "1f918-1f3fc", "latvia": "1f1f1-1f1fb", "moldova": "1f1f2-1f1e9", "mask": "1f637", "bowing_man": "1f647", "man_shrugging": "1f937-200d-2642-fe0f", "ping_pong": "1f3d3", "trackball": "1f5b2", "six": "0036-fe0f-20e3", "point_up_medium_skin_tone": "261d-1f3fd", "hotel": "1f3e8", "bookmark_tabs": "1f4d1", "chart_with_downwards_trend": "1f4c9", "v_light_skin_tone": "270c-1f3fb", "tipping_hand_woman_medium_skin_tone": "1f481-1f3fd-200d-2640-fe0f", "heart_eyes_cat": "1f63b", "dancer": "1f483", "movie_camera": "1f3a5", "two": "0032-fe0f-20e3", "clap_medium_dark_skin_tone": "1f44f-1f3fe", "woman_astronaut_medium_skin_tone": "1f469-1f3fd", "frowning": "1f626", "cry": "1f622", "no_bell": "1f515", "hand_medium_dark_skin_tone": "270b-1f3fe", "ear_light_skin_tone": "1f442-1f3fb", "family_man_girl_boy": "1f468-200d-1f467-200d-1f466", "swimming_woman": "1f3ca-200d-2640-fe0f", "mountain_biking_woman": "1f6b5-200d-2640-fe0f", "mantelpiece_clock": "1f570", "bermuda": "1f1e7-1f1f2", "new_zealand": "1f1f3-1f1ff", "massage_man_medium_dark_skin_tone": "1f486-1f3fe-200d-2642-fe0f", "crown": "1f451", "biking_man": "1f6b4", "woman_playing_water_polo_medium_skin_tone": "1f93d-1f3fd-200d-2640-fe0f", "man_in_tuxedo_dark_skin_tone": "1f935-1f3ff", "no_entry_sign": "1f6ab", "hash": "0023-fe0f-20e3", "white_small_square": "25ab-fe0f", "iraq": "1f1ee-1f1f6", "switzerland": "1f1e8-1f1ed", "woman_mechanic_light_skin_tone": "1f469-1f3fb", "squirrel": "shipit", "woman_cook_medium_light_skin_tone": "1f469-1f3fc", "confounded": "1f616", "+1": "1f44d", "rowing_man": "1f6a3", "mailbox_closed": "1f4ea", "customs": "1f6c3", "mayotte": "1f1fe-1f1f9", "man_mechanic_medium_dark_skin_tone": "1f468-1f3fe", "man_artist_medium_skin_tone": "1f468-1f3fd", "man_playing_handball_medium_skin_tone": "1f93e-1f3fd-200d-2642-fe0f", "grimacing": "1f62c", "dart": "1f3af", "wave_medium_dark_skin_tone": "1f44b-1f3fe", "slightly_smiling_face": "1f642", "medal_sports": "1f3c5", "bank": "1f3e6", "man_student_medium_light_skin_tone": "1f468-1f3fc", "man_pilot_light_skin_tone": "1f468-1f3fb", "weight_lifting_woman_medium_skin_tone": "1f3cb-1f3fd-200d-2640-fe0f", "dash": "1f4a8", "volcano": "1f30b", "antarctica": "1f1e6-1f1f6", "woman_facepalming_light_skin_tone": "1f926-1f3fb-200d-2640-fe0f", "man_dancing_medium_light_skin_tone": "1f57a-1f3fc", "scream_cat": "1f640", "fog": "1f32b", "fist_oncoming_light_skin_tone": "1f44a-1f3fb", "man_dancing_medium_skin_tone": "1f57a-1f3fd", "burrito": "1f32f", "thought_balloon": "1f4ad", "massage_man_medium_light_skin_tone": "1f486-1f3fc-200d-2642-fe0f", "couple_with_heart_woman_woman_dark_skin_tone": "1f469-1f3ff", "writing_hand": "270d-fe0f", "zap": "26a1-fe0f", "recycle": "267b-fe0f", "policewoman_medium_light_skin_tone": "1f46e-1f3fc-200d-2640-fe0f", "frowning_woman_medium_skin_tone": "1f64d-1f3fd-200d-2640-fe0f", "massage_man_light_skin_tone": "1f486-1f3fb-200d-2642-fe0f", "woman_student": "1f469-200d-1f393", "surfing_woman": "1f3c4-200d-2640-fe0f", "sunrise": "1f305", "open_file_folder": "1f4c2", "diamonds": "2666-fe0f", "family_man_woman_girl_girl": "1f468-200d-1f469-200d-1f467-200d-1f467", "airplane": "2708-fe0f", "arrow_heading_down": "2935-fe0f", "uruguay": "1f1fa-1f1fe", "point_down_medium_dark_skin_tone": "1f447-1f3fe", "family_man_man_boy_dark_skin_tone": "1f468-1f3ff", "family_man_woman_girl_boy": "1f468-200d-1f469-200d-1f467-200d-1f466", "confetti_ball": "1f38a", "flower_playing_cards": "1f3b4", "algeria": "1f1e9-1f1ff", "man_teacher_medium_light_skin_tone": "1f468-1f3fc", "woman_artist_light_skin_tone": "1f469-1f3fb", "family_man_woman_girl_medium_skin_tone": "1f468-1f3fd", "nerd_face": "1f913", "eyes": "1f440", "boot": "1f462", "unlock": "1f513", "zzz": "1f4a4", "vatican_city": "1f1fb-1f1e6", "hot_pepper": "1f336", "slot_machine": "1f3b0", "sunrise_over_mountains": "1f304", "haircut_man_medium_skin_tone": "1f487-1f3fd-200d-2642-fe0f", "stuck_out_tongue": "1f61b", "point_up_medium_dark_skin_tone": "261d-1f3fe", "vulcan_salute_medium_dark_skin_tone": "1f596-1f3fe", "family": "1f46a", "key": "1f511", "myanmar": "1f1f2-1f1f2", "policeman_medium_light_skin_tone": "1f46e-1f3fc-200d-2640-fe0f", "man_shrugging_medium_dark_skin_tone": "1f937-1f3fe-200d-2642-fe0f", "woman_health_worker": "1f469-200d-2695-fe0f", "woman_judge": "1f469-200d-2696-fe0f", "japan": "1f5fe", "dominica": "1f1e9-1f1f2", "dragon": "1f409", "open_book": "1f4d6", "raising_hand_man": "1f64b-200d-2642-fe0f", "bikini": "1f459", "loudspeaker": "1f4e2", "woman_astronaut_medium_light_skin_tone": "1f469-1f3fc", "envelope_with_arrow": "1f4e9", "thailand": "1f1f9-1f1ed", "point_up_medium_light_skin_tone": "261d-1f3fc", "baby_medium_dark_skin_tone": "1f476-1f3fe", "man_scientist_medium_skin_tone": "1f468-1f3fd", "bowing_woman_medium_light_skin_tone": "1f647-1f3fc-200d-2640-fe0f", "construction_worker": "1f477", "nut_and_bolt": "1f529", "sparkling_heart": "1f496", "couplekiss_woman_woman_dark_skin_tone": "1f469-1f3ff", "elephant": "1f418", "bar_chart": "1f4ca", "nose_dark_skin_tone": "1f443-1f3ff", "stop_button": "23f9", "family_man_woman_boy_boy_light_skin_tone": "1f468-1f3fb", "family_man_girl_medium_light_skin_tone": "1f468-1f3fc", "relieved": "1f60c", "man_in_tuxedo": "1f935", "kick_scooter": "1f6f4", "statue_of_liberty": "1f5fd", "information_desk_person": "1f481", "sa": "1f202-fe0f", "abc": "1f524", "robot": "1f916", "cat": "1f431", "accept": "1f251", "upside_down_face": "1f643", "cloud": "2601-fe0f", "frowning_man_light_skin_tone": "1f64d-1f3fb-200d-2642-fe0f", "walking_man_medium_skin_tone": "1f6b6-1f3fd-200d-2640-fe0f", "sparkles": "2728", "u5272": "1f239", "globe_with_meridians": "1f310", "frowning_woman_medium_dark_skin_tone": "1f64d-1f3fe-200d-2640-fe0f", "grey_exclamation": "2755", "tm": "2122-fe0f", "massage_man_dark_skin_tone": "1f486-1f3ff-200d-2642-fe0f", "family_woman_woman_girl_boy_dark_skin_tone": "1f469-1f3ff", "paintbrush": "1f58c", "arrow_right_hook": "21aa-fe0f", "mauritania": "1f1f2-1f1f7", "man_scientist_light_skin_tone": "1f468-1f3fb", "woman_juggling_light_skin_tone": "1f939-1f3fb-200d-2640-fe0f", "ok_woman": "1f646", "snail": "1f40c", "hocho": "1f52a", "arrow_forward": "25b6-fe0f", "french_southern_territories": "1f1f9-1f1eb", "iphone": "1f4f1", "princess_medium_light_skin_tone": "1f478-1f3fc", "maple_leaf": "1f341", "open_hands": "1f450", "racing_car": "1f3ce", "pill": "1f48a", "cuba": "1f1e8-1f1fa", "fist_raised_dark_skin_tone": "270a-1f3ff", "blonde_man_dark_skin_tone": "1f471-1f3ff-200d-2640-fe0f", "family_woman_girl_boy_dark_skin_tone": "1f469-1f3ff", "fox_face": "1f98a", "man_playing_handball": "1f93e-200d-2642-fe0f", "bullettrain_side": "1f684", "black_small_square": "25aa-fe0f", "kazakhstan": "1f1f0-1f1ff", "vanuatu": "1f1fb-1f1fa", "older_man_medium_skin_tone": "1f474-1f3fd", "man_teacher": "1f468-200d-1f3eb", "family_man_man_boy_boy_medium_dark_skin_tone": "1f468-1f3fe", "back": "1f519", "point_up_2_medium_dark_skin_tone": "1f446-1f3fe", "woman_teacher_medium_light_skin_tone": "1f469-1f3fc", "family_woman_boy_boy_medium_dark_skin_tone": "1f469-1f3fe", "surfing_woman_medium_light_skin_tone": "1f3c4-1f3fc-200d-2640-fe0f", "portugal": "1f1f5-1f1f9", "construction_worker_woman_dark_skin_tone": "1f477-1f3ff-200d-2640-fe0f", "family_man_man_boy_medium_skin_tone": "1f468-1f3fd", "family_man_girl_dark_skin_tone": "1f468-1f3ff", "woman_mechanic": "1f469-200d-1f527", "arrow_heading_up": "2934-fe0f", "clock330": "1f55e", "malawi": "1f1f2-1f1fc", "ok_hand_medium_dark_skin_tone": "1f44c-1f3fe", "prince_dark_skin_tone": "1f934-1f3ff", "ice_hockey": "1f3d2", "pk": "1f1f5-1f1f0", "san_marino": "1f1f8-1f1f2", "point_left_light_skin_tone": "1f448-1f3fb", "woman_office_worker_medium_light_skin_tone": "1f469-1f3fc", "swimming_man_medium_light_skin_tone": "1f3ca-1f3fc-200d-2640-fe0f", "stuffed_flatbread": "1f959", "aerial_tramway": "1f6a1", "family_man_man_girl_girl_dark_skin_tone": "1f468-1f3ff", "family_woman_girl_girl_medium_dark_skin_tone": "1f469-1f3fe", "closed_book": "1f4d5", "family_woman_girl_boy_medium_light_skin_tone": "1f469-1f3fc", "family_man_man_girl_boy_medium_skin_tone": "1f468-1f3fd", "v": "270c-fe0f", "play_or_pause_button": "23ef", "el_salvador": "1f1f8-1f1fb", "woman_judge_medium_light_skin_tone": "1f469-1f3fc", "santa_medium_light_skin_tone": "1f385-1f3fc", "couplekiss_man_man_light_skin_tone": "1f468-1f3fb", "blonde_man_light_skin_tone": "1f471-1f3fb-200d-2640-fe0f", "fist_right": "1f91c", "man_with_turban": "1f473", "cancer": "264b-fe0f", "tunisia": "1f1f9-1f1f3", "open_hands_medium_light_skin_tone": "1f450-1f3fc", "call_me_hand_medium_dark_skin_tone": "1f919-1f3fe", "tired_face": "1f62b", "tongue": "1f445", "shower": "1f6bf", "british_indian_ocean_territory": "1f1ee-1f1f4", "man_firefighter_medium_light_skin_tone": "1f468-1f3fc", "couple_with_heart_woman_woman_medium_dark_skin_tone": "1f469-1f3fe", "crescent_moon": "1f319", "ecuador": "1f1ea-1f1e8", "french_polynesia": "1f1f5-1f1eb", "man_light_skin_tone": "1f468-1f3fb", "mountain_biking_woman_medium_skin_tone": "1f6b5-1f3fd-200d-2640-fe0f", "pakistan": "1f1f5-1f1f0", "open_hands_medium_dark_skin_tone": "1f450-1f3fe", "telephone": "260e-fe0f", "envelope": "2709-fe0f", "revolving_hearts": "1f49e", "mega": "1f4e3", "montserrat": "1f1f2-1f1f8", "uganda": "1f1fa-1f1ec", "tropical_fish": "1f420", "hibiscus": "1f33a", "rainbow_flag": "1f3f3-fe0f-200d-1f308", "bangladesh": "1f1e7-1f1e9", "shipit": "shipit", "no_good_man_dark_skin_tone": "1f645-1f3ff-200d-2642-fe0f", "no_mouth": "1f636", "man_farmer": "1f468-200d-1f33e", "speak_no_evil": "1f64a", "level_slider": "1f39a", "guatemala": "1f1ec-1f1f9", "woman_factory_worker": "1f469-200d-1f3ed", "fork_and_knife": "1f374", "belarus": "1f1e7-1f1fe", "family_woman_woman_girl_boy_medium_dark_skin_tone": "1f469-1f3fe", "yum": "1f60b", "helicopter": "1f681", "busstop": "1f68f", "policewoman_light_skin_tone": "1f46e-1f3fb-200d-2640-fe0f", "man_technologist_medium_skin_tone": "1f468-1f3fd", "man_with_gua_pi_mao_light_skin_tone": "1f472-1f3fb", "man_astronaut_dark_skin_tone": "1f468-1f3ff", "skull": "1f480", "smirk_cat": "1f63c", "jeans": "1f456", "flipper": "1f42c", "dizzy": "1f4ab", "cocktail": "1f378", "basketball_woman_medium_skin_tone": "26f9-1f3fd-200d-2640-fe0f", "v_medium_light_skin_tone": "270c-1f3fc", "secret": "3299-fe0f", "seven": "0037-fe0f-20e3", "ghana": "1f1ec-1f1ed", "guernsey": "1f1ec-1f1ec", "kyrgyzstan": "1f1f0-1f1ec", "godmode": "godmode", "female_detective_dark_skin_tone": "1f575-1f3ff-200d-2640-fe0f", "fallen_leaf": "1f342", "snowflake": "2744-fe0f", "raised_hand_with_fingers_splayed_medium_dark_skin_tone": "1f590-1f3fe", "woman_health_worker_medium_dark_skin_tone": "1f469-1f3fe", "man_shrugging_dark_skin_tone": "1f937-1f3ff-200d-2642-fe0f", "pout": "1f621", "stars": "1f320", "family_woman_girl_boy": "1f469-200d-1f467-200d-1f466", "gun": "1f52b", "woman_scientist_dark_skin_tone": "1f469-1f3ff", "basketball_woman_dark_skin_tone": "26f9-1f3ff-200d-2640-fe0f", "biking_woman_medium_light_skin_tone": "1f6b4-1f3fc-200d-2640-fe0f", "family_man_girl_boy_medium_dark_skin_tone": "1f468-1f3fe", "oncoming_bus": "1f68d", "seat": "1f4ba", "vhs": "1f4fc", "lithuania": "1f1f1-1f1f9", "v_medium_dark_skin_tone": "270c-1f3fe", "man_with_gua_pi_mao_medium_skin_tone": "1f472-1f3fd", "frowning_face": "2639-fe0f", "shit": "1f4a9", "ab": "1f18e", "couple_with_heart_woman_woman_medium_skin_tone": "1f469-1f3fd", "family_woman_woman_girl_girl": "1f469-200d-1f469-200d-1f467-200d-1f467", "potato": "1f954", "minidisc": "1f4bd", "libya": "1f1f1-1f1fe", "point_right_dark_skin_tone": "1f449-1f3ff", "man_artist": "1f468-200d-1f3a8", "pineapple": "1f34d", "spaghetti": "1f35d", "couch_and_lamp": "1f6cb", "free": "1f193", "jamaica": "1f1ef-1f1f2", "woman_astronaut_dark_skin_tone": "1f469-1f3ff", "man_mechanic": "1f468-200d-1f527", "curry": "1f35b", "small_orange_diamond": "1f538", "pray": "1f64f", "hotdog": "1f32d", "currency_exchange": "1f4b1", "-1_dark_skin_tone": "1f44e-1f3ff", "man_office_worker_dark_skin_tone": "1f468-1f3ff", "clock830": "1f563", "policeman_medium_skin_tone": "1f46e-1f3fd-200d-2640-fe0f", "grin": "1f601", "water_buffalo": "1f403", "older_man_dark_skin_tone": "1f474-1f3ff", "business_suit_levitating_medium_dark_skin_tone": "1f574-1f3fe", "couple_with_heart_man_man_medium_light_skin_tone": "1f468-1f3fc", "rowing_man_medium_light_skin_tone": "1f6a3-1f3fc-200d-2640-fe0f", "purse": "1f45b", "slovenia": "1f1f8-1f1ee", "tipping_hand_man_medium_light_skin_tone": "1f481-1f3fc-200d-2642-fe0f", "madagascar": "1f1f2-1f1ec", "south_georgia_south_sandwich_islands": "1f1ec-1f1f8", "punch": "1f44a", "man_pilot": "1f468-200d-2708-fe0f", "owl": "1f989", "croissant": "1f950", "email": "2709-fe0f", "outbox_tray": "1f4e4", "construction_worker_man_medium_light_skin_tone": "1f477-1f3fc-200d-2640-fe0f", "mrs_claus_medium_dark_skin_tone": "1f936-1f3fe", "family_man_woman_girl_boy_dark_skin_tone": "1f468-1f3ff", "file_cabinet": "1f5c4", "hungary": "1f1ed-1f1fa", "pray_medium_dark_skin_tone": "1f64f-1f3fe", "woman_mechanic_dark_skin_tone": "1f469-1f3ff", "angel_medium_skin_tone": "1f47c-1f3fd", "man_dancing": "1f57a", "pound": "1f4b7", "macedonia": "1f1f2-1f1f0", "man_facepalming_medium_skin_tone": "1f926-1f3fd-200d-2642-fe0f", "scroll": "1f4dc", "rescue_worker_helmet": "26d1", "desktop_computer": "1f5a5", "heavy_plus_sign": "2795", "man_with_turban_medium_skin_tone": "1f473-1f3fd-200d-2640-fe0f", "horse_racing": "1f3c7", "low_brightness": "1f505", "loop": "27bf", "man_with_turban_medium_dark_skin_tone": "1f473-1f3fe-200d-2640-fe0f", "champagne": "1f37e", "construction_worker_woman_light_skin_tone": "1f477-1f3fb-200d-2640-fe0f", "man_teacher_light_skin_tone": "1f468-1f3fb", "family_woman_woman_girl_girl_medium_light_skin_tone": "1f469-1f3fc", "footprints": "1f463", "cloud_with_snow": "1f328", "man_cook_medium_light_skin_tone": "1f468-1f3fc", "woman_mechanic_medium_light_skin_tone": "1f469-1f3fc", "point_up_2": "1f446", "circus_tent": "1f3aa", "serbia": "1f1f7-1f1f8", "fist_right_medium_dark_skin_tone": "1f91c-1f3fe", "weight_lifting_woman_dark_skin_tone": "1f3cb-1f3ff-200d-2640-fe0f", "musical_score": "1f3bc", "violin": "1f3bb", "card_file_box": "1f5c3", "tipping_hand_woman_dark_skin_tone": "1f481-1f3ff-200d-2640-fe0f", "man_facepalming_dark_skin_tone": "1f926-1f3ff-200d-2642-fe0f", "open_mouth": "1f62e", "left_right_arrow": "2194-fe0f", "no_good_man_light_skin_tone": "1f645-1f3fb-200d-2642-fe0f", "man_factory_worker": "1f468-200d-1f3ed", "man_judge": "1f468-200d-2696-fe0f", "negative_squared_cross_mark": "274e", "bowing_woman_medium_skin_tone": "1f647-1f3fd-200d-2640-fe0f", "family_woman_boy_boy_light_skin_tone": "1f469-1f3fb", "battery": "1f50b", "couplekiss_man_man_medium_light_skin_tone": "1f468-1f3fc", "clock5": "1f554", "white_flag": "1f3f3-fe0f", "guadeloupe": "1f1ec-1f1f5", "muscle_medium_dark_skin_tone": "1f4aa-1f3fe", "man_scientist_dark_skin_tone": "1f468-1f3ff", "business_suit_levitating_light_skin_tone": "1f574-1f3fb", "woman_office_worker": "1f469-200d-1f4bc", "gift": "1f381", "sound": "1f509", "clubs": "2663-fe0f", "woman_scientist_medium_light_skin_tone": "1f469-1f3fc", "female_detective_medium_skin_tone": "1f575-1f3fd-200d-2640-fe0f", "man_singer_medium_light_skin_tone": "1f468-1f3fc", "family_man_girl": "1f468-200d-1f467", "bee": "1f41d", "full_moon_with_face": "1f31d", "black_medium_square": "25fc-fe0f", "zambia": "1f1ff-1f1f2", "raised_hands_dark_skin_tone": "1f64c-1f3ff", "family_woman_woman_boy_boy_medium_skin_tone": "1f469-1f3fd", "bread": "1f35e", "clock11": "1f55a", "man_office_worker_medium_light_skin_tone": "1f468-1f3fc", "woman_firefighter_medium_skin_tone": "1f469-1f3fd", "man_dancing_dark_skin_tone": "1f57a-1f3ff", "family_man_boy_medium_dark_skin_tone": "1f468-1f3fe", "hugs": "1f917", "roll_eyes": "1f644", "raised_hand": "270b", "tangerine": "1f34a", "grey_question": "2754", "princess_light_skin_tone": "1f478-1f3fb", "motor_boat": "1f6e5", "passport_control": "1f6c2", "man_artist_medium_dark_skin_tone": "1f468-1f3fe", "golfing_man_medium_skin_tone": "1f3cc-1f3fd-200d-2640-fe0f", "shirt": "1f455", "whale": "1f433", "apple": "1f34e", "ethiopia": "1f1ea-1f1f9", "jordan": "1f1ef-1f1f4", "biking_woman_medium_dark_skin_tone": "1f6b4-1f3fe-200d-2640-fe0f", "family_woman_girl_girl_medium_skin_tone": "1f469-1f3fd", "turkey": "1f983", "snowman_with_snow": "2603-fe0f", "fist_left_medium_skin_tone": "1f91b-1f3fd", "woman_with_turban_light_skin_tone": "1f473-1f3fb-200d-2640-fe0f", "woman_pilot_dark_skin_tone": "1f469-1f3ff", "family_woman_woman_boy_medium_skin_tone": "1f469-1f3fd", "purple_heart": "1f49c", "black_heart": "1f5a4", "haircut_man": "1f487-200d-2642-fe0f", "arrow_lower_left": "2199-fe0f", "guinea_bissau": "1f1ec-1f1fc", "sudan": "1f1f8-1f1e9", "woman_scientist_light_skin_tone": "1f469-1f3fb", "bust_in_silhouette": "1f464", "walking": "1f6b6", "european_union": "1f1ea-1f1fa", "running_woman_dark_skin_tone": "1f3c3-1f3ff-200d-2640-fe0f", "om": "1f549", "rowing_man_medium_skin_tone": "1f6a3-1f3fd-200d-2640-fe0f", "ideograph_advantage": "1f250", "nepal": "1f1f3-1f1f5", "syria": "1f1f8-1f1fe", "man_pilot_medium_dark_skin_tone": "1f468-1f3fe", "princess_medium_dark_skin_tone": "1f478-1f3fe", "watermelon": "1f349", "left_luggage": "1f6c5", "us": "1f1fa-1f1f8", "point_left_medium_light_skin_tone": "1f448-1f3fc", "family_man_girl_boy_medium_skin_tone": "1f468-1f3fd", "biking_man_medium_dark_skin_tone": "1f6b4-1f3fe-200d-2640-fe0f", "keycap_ten": "1f51f", "man_medium_light_skin_tone": "1f468-1f3fc", "couple_with_heart_man_man_light_skin_tone": "1f468-1f3fb", "family_man_man_girl_boy_light_skin_tone": "1f468-1f3fb", "dog2": "1f415", "art": "1f3a8", "taxi": "1f695", "motorcycle": "1f3cd", "diamond_shape_with_a_dot_inside": "1f4a0", "writing_hand_light_skin_tone": "270d-1f3fb", "woman_playing_water_polo_medium_dark_skin_tone": "1f93d-1f3fe-200d-2640-fe0f", "martial_arts_uniform": "1f94b", "spiral_calendar": "1f5d3", "older_man_medium_dark_skin_tone": "1f474-1f3fe", "woman_artist_medium_skin_tone": "1f469-1f3fd", "no_good_man_medium_dark_skin_tone": "1f645-1f3fe-200d-2642-fe0f", "family_woman_woman_boy_medium_light_skin_tone": "1f469-1f3fc", "ship": "1f6a2", "bangbang": "203c-fe0f", "israel": "1f1ee-1f1f1", "rowing_man_medium_dark_skin_tone": "1f6a3-1f3fe-200d-2640-fe0f", "calling": "1f4f2", "scorpius": "264f-fe0f", "vulcan_salute_dark_skin_tone": "1f596-1f3ff", "woman_office_worker_light_skin_tone": "1f469-1f3fb", "man_judge_light_skin_tone": "1f468-1f3fb", "family_woman_woman_boy_boy_medium_dark_skin_tone": "1f469-1f3fe", "woman_playing_handball": "1f93e-200d-2640-fe0f", "bridge_at_night": "1f309", "stop_sign": "1f6d1", "8ball": "1f3b1", "orange_book": "1f4d9", "couplekiss_man_woman": "1f48f", "no_mobile_phones": "1f4f5", "pouting_man_dark_skin_tone": "1f64e-1f3ff-200d-2642-fe0f", "man_juggling_light_skin_tone": "1f939-1f3fb-200d-2642-fe0f", "cold_sweat": "1f630", "star2": "1f31f", "taco": "1f32e", "point_right_medium_light_skin_tone": "1f449-1f3fc", "selfie_medium_dark_skin_tone": "1f933-1f3fe", "family_woman_woman_girl_boy_light_skin_tone": "1f469-1f3fb", "hankey": "1f4a9", "monkey_face": "1f435", "sweden": "1f1f8-1f1ea", "crocodile": "1f40a", "last_quarter_moon_with_face": "1f31c", "comet": "2604-fe0f", "caribbean_netherlands": "1f1e7-1f1f6", "walking_man_medium_dark_skin_tone": "1f6b6-1f3fe-200d-2640-fe0f", "basketball_man_medium_light_skin_tone": "26f9-1f3fc-200d-2640-fe0f", "deer": "1f98c", "clock4": "1f553", "christmas_island": "1f1e8-1f1fd", "fist_right_medium_skin_tone": "1f91c-1f3fd", "man_cook_dark_skin_tone": "1f468-1f3ff", "family_man_man_girl_medium_light_skin_tone": "1f468-1f3fc", "whale2": "1f40b", "sagittarius": "2650-fe0f", "children_crossing": "1f6b8", "call_me_hand_dark_skin_tone": "1f919-1f3ff", "ok_woman_medium_dark_skin_tone": "1f646-1f3fe-200d-2640-fe0f", "man_firefighter": "1f468-200d-1f692", "rewind": "23ea", "guardswoman_light_skin_tone": "1f482-1f3fb-200d-2640-fe0f", "woman_technologist_light_skin_tone": "1f469-1f3fb", "woman_pilot_light_skin_tone": "1f469-1f3fb", "raising_hand_woman_light_skin_tone": "1f64b-1f3fb-200d-2640-fe0f", "bowing_man_light_skin_tone": "1f647-1f3fb-200d-2640-fe0f", "frowning_man_medium_skin_tone": "1f64d-1f3fd-200d-2642-fe0f", "shark": "1f988", "sun_behind_rain_cloud": "1f326", "dagger": "1f5e1", "musical_note": "1f3b5", "crossed_fingers_dark_skin_tone": "1f91e-1f3ff", "man_pilot_medium_skin_tone": "1f468-1f3fd", "family_woman_boy_medium_skin_tone": "1f469-1f3fd", "golfing_man_medium_light_skin_tone": "1f3cc-1f3fc-200d-2640-fe0f", "girl": "1f467", "family_man_woman_boy_boy": "1f468-200d-1f469-200d-1f466-200d-1f466", "biking_woman": "1f6b4-200d-2640-fe0f", "cl": "1f191", "raised_back_of_hand_medium_dark_skin_tone": "1f91a-1f3fe", "raising_hand_woman_medium_light_skin_tone": "1f64b-1f3fc-200d-2640-fe0f", "baby_medium_skin_tone": "1f476-1f3fd", "guardsman": "1f482", "woman_astronaut": "1f469-200d-1f680", "tophat": "1f3a9", "honduras": "1f1ed-1f1f3", "mexico": "1f1f2-1f1fd", "nauru": "1f1f3-1f1f7", "mrs_claus_medium_light_skin_tone": "1f936-1f3fc", "weary": "1f629", "womans_hat": "1f452", "person_fencing": "1f93a", "u6708": "1f237-fe0f", "a": "1f170-fe0f", "de": "1f1e9-1f1ea", "lebanon": "1f1f1-1f1e7", "puerto_rico": "1f1f5-1f1f7", "man_mechanic_dark_skin_tone": "1f468-1f3ff", "policeman_dark_skin_tone": "1f46e-1f3ff-200d-2640-fe0f", "kissing_smiling_eyes": "1f619", "avocado": "1f951", "six_pointed_star": "1f52f", "record_button": "23fa", "family_woman_woman_girl_girl_light_skin_tone": "1f469-1f3fb", "mountain_biking_man_medium_light_skin_tone": "1f6b5-1f3fc-200d-2640-fe0f", "couple_with_heart_man_man_medium_dark_skin_tone": "1f468-1f3fe", "family_man_girl_girl_light_skin_tone": "1f468-1f3fb", "post_office": "1f3e3", "telescope": "1f52d", "baby_symbol": "1f6bc", "capital_abcd": "1f520", "woman_singer_light_skin_tone": "1f469-1f3fb", "woman_facepalming_medium_dark_skin_tone": "1f926-1f3fe-200d-2640-fe0f", "ant": "1f41c", "house": "1f3e0", "shield": "1f6e1", "yellow_heart": "1f49b", "u55b6": "1f23a", "senegal": "1f1f8-1f1f3", "united_arab_emirates": "1f1e6-1f1ea", "no_good_woman_medium_skin_tone": "1f645-1f3fd-200d-2640-fe0f", "running_man_medium_light_skin_tone": "1f3c3-1f3fc-200d-2640-fe0f", "beetle": "1f41e", "bus": "1f68c", "flight_arrival": "1f6ec", "black_large_square": "2b1b-fe0f", "white_large_square": "2b1c-fe0f", "woman_technologist_medium_light_skin_tone": "1f469-1f3fc", "skier": "26f7", "ok_hand_medium_light_skin_tone": "1f44c-1f3fc", "rofl": "1f923", "hushed": "1f62f", "ng_man": "1f645-200d-2642-fe0f", "running_woman": "1f3c3-200d-2640-fe0f", "family_man_man_girl_girl": "1f468-200d-1f468-200d-1f467-200d-1f467", "gorilla": "1f98d", "horse_racing_medium_light_skin_tone": "1f3c7-1f3fc", "mountain_biking_man_medium_skin_tone": "1f6b5-1f3fd-200d-2640-fe0f", "disappointed": "1f61e", "dolphin": "1f42c", "green_apple": "1f34f", "honey_pot": "1f36f", "georgia": "1f1ec-1f1ea", "business_suit_levitating": "1f574", "camera": "1f4f7", "ledger": "1f4d2", "woman_cook_medium_skin_tone": "1f469-1f3fd", "bahamas": "1f1e7-1f1f8", "family_woman_woman_girl": "1f469-200d-1f469-200d-1f467", "man_factory_worker_light_skin_tone": "1f468-1f3fb", "golfing_woman_medium_dark_skin_tone": "1f3cc-1f3fe-200d-2640-fe0f", "family_woman_girl": "1f469-200d-1f467", "turtle": "1f422", "mauritius": "1f1f2-1f1fa", "family_man_girl_light_skin_tone": "1f468-1f3fb", "hospital": "1f3e5", "church": "26ea-fe0f", "wheel_of_dharma": "2638-fe0f", "mongolia": "1f1f2-1f1f3", "man_facepalming": "1f926-200d-2642-fe0f", "bed": "1f6cf", "man_artist_medium_light_skin_tone": "1f468-1f3fc", "santa_medium_dark_skin_tone": "1f385-1f3fe", "family_man_woman_girl_medium_light_skin_tone": "1f468-1f3fc", "large_blue_diamond": "1f537", "colombia": "1f1e8-1f1f4", "philippines": "1f1f5-1f1ed", "older_woman_dark_skin_tone": "1f475-1f3ff", "woman_scientist_medium_skin_tone": "1f469-1f3fd", "couplekiss_woman_woman_light_skin_tone": "1f469-1f3fb", "male_detective": "1f575-fe0f", "crossed_swords": "2694-fe0f", "notebook": "1f4d3", "nail_care_medium_light_skin_tone": "1f485-1f3fc", "blonde_man_medium_skin_tone": "1f471-1f3fd-200d-2640-fe0f", "tipping_hand_man_light_skin_tone": "1f481-1f3fb-200d-2642-fe0f", "sweat_smile": "1f605", "white_medium_small_square": "25fd-fe0f", "bowtie": "bowtie", "reminder_ribbon": "1f397", "clamp": "1f5dc", "balance_scale": "2696-fe0f", "postal_horn": "1f4ef", "swimming_woman_dark_skin_tone": "1f3ca-1f3ff-200d-2640-fe0f", "raised_hand_with_fingers_splayed": "1f590", "ok_man": "1f646-200d-2642-fe0f", "new": "1f195", "male_detective_medium_dark_skin_tone": "1f575-1f3fe-200d-2640-fe0f", "computer_mouse": "1f5b1", "hourglass_flowing_sand": "23f3", "bahrain": "1f1e7-1f1ed", "djibouti": "1f1e9-1f1ef", "zimbabwe": "1f1ff-1f1fc", "swimming_man_light_skin_tone": "1f3ca-1f3fb-200d-2640-fe0f", "interrobang": "2049-fe0f", "clock8": "1f557", "pancakes": "1f95e", "thermometer": "1f321", "label": "1f3f7", "denmark": "1f1e9-1f1f0", "raising_hand_man_medium_light_skin_tone": "1f64b-1f3fc-200d-2642-fe0f", "frowning_man_medium_dark_skin_tone": "1f64d-1f3fe-200d-2642-fe0f", "point_right": "1f449", "guitar": "1f3b8", "family_woman_woman_girl_medium_skin_tone": "1f469-1f3fd", "woman_juggling_medium_skin_tone": "1f939-1f3fd-200d-2640-fe0f", "man_health_worker": "1f468-200d-2695-fe0f", "stew": "1f372", "surfing_man": "1f3c4", "twisted_rightwards_arrows": "1f500", "timor_leste": "1f1f9-1f1f1", "weight_lifting_woman": "1f3cb-fe0f-200d-2640-fe0f", "amphora": "1f3fa", "heart": "2764-fe0f", "bowing_man_medium_dark_skin_tone": "1f647-1f3fe-200d-2640-fe0f"}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/emoji_search.go b/vendor/github.com/mattermost/mattermost-server/model/emoji_search.go
new file mode 100644
index 00000000..3a768a57
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/emoji_search.go
@@ -0,0 +1,25 @@
+// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type EmojiSearch struct {
+ Term string `json:"term"`
+ PrefixOnly bool `json:"prefix_only"`
+}
+
+func (es *EmojiSearch) ToJson() string {
+ b, _ := json.Marshal(es)
+ return string(b)
+}
+
+func EmojiSearchFromJson(data io.Reader) *EmojiSearch {
+ var es *EmojiSearch
+ json.NewDecoder(data).Decode(&es)
+ return es
+}
diff --git a/vendor/github.com/mattermost/platform/model/file.go b/vendor/github.com/mattermost/mattermost-server/model/file.go
index f9258146..c7ffbf0b 100644
--- a/vendor/github.com/mattermost/platform/model/file.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/file.go
@@ -23,21 +23,12 @@ type FileUploadResponse struct {
}
func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse {
- decoder := json.NewDecoder(data)
- var o FileUploadResponse
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *FileUploadResponse
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *FileUploadResponse) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
diff --git a/vendor/github.com/mattermost/platform/model/file_info.go b/vendor/github.com/mattermost/mattermost-server/model/file_info.go
index 0ee2c50d..e0bbfcfc 100644
--- a/vendor/github.com/mattermost/platform/model/file_info.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/file_info.go
@@ -35,12 +35,8 @@ type FileInfo struct {
}
func (info *FileInfo) ToJson() string {
- b, err := json.Marshal(info)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(info)
+ return string(b)
}
func FileInfoFromJson(data io.Reader) *FileInfo {
@@ -55,12 +51,8 @@ func FileInfoFromJson(data io.Reader) *FileInfo {
}
func FileInfosToJson(infos []*FileInfo) string {
- b, err := json.Marshal(infos)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(infos)
+ return string(b)
}
func FileInfosFromJson(data io.Reader) []*FileInfo {
diff --git a/vendor/github.com/mattermost/platform/model/gitlab.go b/vendor/github.com/mattermost/mattermost-server/model/gitlab.go
index 8777614c..8777614c 100644
--- a/vendor/github.com/mattermost/platform/model/gitlab.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/gitlab.go
diff --git a/vendor/github.com/mattermost/platform/model/incoming_webhook.go b/vendor/github.com/mattermost/mattermost-server/model/incoming_webhook.go
index 3e0488d2..3856d22f 100644
--- a/vendor/github.com/mattermost/platform/model/incoming_webhook.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/incoming_webhook.go
@@ -16,17 +16,18 @@ const (
)
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"`
+ 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 {
@@ -40,43 +41,25 @@ type IncomingWebhookRequest struct {
}
func (o *IncomingWebhook) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func IncomingWebhookFromJson(data io.Reader) *IncomingWebhook {
- decoder := json.NewDecoder(data)
- var o IncomingWebhook
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *IncomingWebhook
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func IncomingWebhookListToJson(l []*IncomingWebhook) string {
- b, err := json.Marshal(l)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(l)
+ return string(b)
}
func IncomingWebhookListFromJson(data io.Reader) []*IncomingWebhook {
- decoder := json.NewDecoder(data)
var o []*IncomingWebhook
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *IncomingWebhook) IsValid() *AppError {
@@ -110,7 +93,7 @@ func (o *IncomingWebhook) IsValid() *AppError {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "", http.StatusBadRequest)
}
- if len(o.Description) > 128 {
+ if len(o.Description) > 500 {
return NewAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "", http.StatusBadRequest)
}
@@ -214,7 +197,7 @@ func IncomingWebhookRequestFromJson(data io.Reader) (*IncomingWebhookRequest, *A
if err != nil {
o, err = decodeIncomingWebhookRequest(escapeControlCharsFromPayload(by))
if err != nil {
- return nil, NewAppError("IncomingWebhookRequestFromJson", "Unable to parse incoming data", nil, err.Error(), http.StatusBadRequest)
+ return nil, NewAppError("IncomingWebhookRequestFromJson", "model.incoming_hook.parse_data.app_error", nil, err.Error(), http.StatusBadRequest)
}
}
@@ -222,3 +205,12 @@ func IncomingWebhookRequestFromJson(data io.Reader) (*IncomingWebhookRequest, *A
return o, nil
}
+
+func (o *IncomingWebhookRequest) ToJson() string {
+ b, err := json.Marshal(o)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
diff --git a/vendor/github.com/mattermost/platform/model/initial_load.go b/vendor/github.com/mattermost/mattermost-server/model/initial_load.go
index 71ba1769..3be68044 100644
--- a/vendor/github.com/mattermost/platform/model/initial_load.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/initial_load.go
@@ -19,21 +19,12 @@ type InitialLoad struct {
}
func (me *InitialLoad) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func InitialLoadFromJson(data io.Reader) *InitialLoad {
- decoder := json.NewDecoder(data)
- var o InitialLoad
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *InitialLoad
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/job.go b/vendor/github.com/mattermost/mattermost-server/model/job.go
index 9a756602..c1661495 100644
--- a/vendor/github.com/mattermost/platform/model/job.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/job.go
@@ -16,6 +16,7 @@ const (
JOB_TYPE_ELASTICSEARCH_POST_INDEXING = "elasticsearch_post_indexing"
JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION = "elasticsearch_post_aggregation"
JOB_TYPE_LDAP_SYNC = "ldap_sync"
+ JOB_TYPE_MIGRATIONS = "migrations"
JOB_STATUS_PENDING = "pending"
JOB_STATUS_IN_PROGRESS = "in_progress"
@@ -52,6 +53,7 @@ func (j *Job) IsValid() *AppError {
case JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION:
case JOB_TYPE_LDAP_SYNC:
case JOB_TYPE_MESSAGE_EXPORT:
+ case JOB_TYPE_MIGRATIONS:
default:
return NewAppError("Job.IsValid", "model.job.is_valid.type.app_error", nil, "id="+j.Id, http.StatusBadRequest)
}
@@ -71,11 +73,8 @@ func (j *Job) IsValid() *AppError {
}
func (js *Job) ToJson() string {
- if b, err := json.Marshal(js); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(js)
+ return string(b)
}
func JobFromJson(data io.Reader) *Job {
@@ -88,11 +87,8 @@ func JobFromJson(data io.Reader) *Job {
}
func JobsToJson(jobs []*Job) string {
- if b, err := json.Marshal(jobs); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(jobs)
+ return string(b)
}
func JobsFromJson(data io.Reader) []*Job {
@@ -105,11 +101,8 @@ func JobsFromJson(data io.Reader) []*Job {
}
func (js *Job) DataToJson() string {
- if b, err := json.Marshal(js.Data); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(js.Data)
+ return string(b)
}
type Worker interface {
diff --git a/vendor/github.com/mattermost/platform/model/ldap.go b/vendor/github.com/mattermost/mattermost-server/model/ldap.go
index 1453a4ad..9051c5a3 100644
--- a/vendor/github.com/mattermost/platform/model/ldap.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/ldap.go
@@ -5,5 +5,4 @@ package model
const (
USER_AUTH_SERVICE_LDAP = "ldap"
- LDAP_SYNC_TASK_NAME = "LDAP Syncronization"
)
diff --git a/vendor/github.com/mattermost/platform/model/license.go b/vendor/github.com/mattermost/mattermost-server/model/license.go
index a81f882c..c30fecf7 100644
--- a/vendor/github.com/mattermost/platform/model/license.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/license.go
@@ -46,16 +46,16 @@ type Features struct {
Compliance *bool `json:"compliance"`
Cluster *bool `json:"cluster"`
Metrics *bool `json:"metrics"`
- CustomBrand *bool `json:"custom_brand"`
MHPNS *bool `json:"mhpns"`
SAML *bool `json:"saml"`
- PasswordRequirements *bool `json:"password_requirements"`
Elasticsearch *bool `json:"elastic_search"`
Announcement *bool `json:"announcement"`
ThemeManagement *bool `json:"theme_management"`
EmailNotificationContents *bool `json:"email_notification_contents"`
DataRetention *bool `json:"data_retention"`
MessageExport *bool `json:"message_export"`
+ CustomPermissionsSchemes *bool `json:"custom_permissions_schemes"`
+ CustomTermsOfService *bool `json:"custom_terms_of_service"`
// after we enabled more features for webrtc we'll need to control them with this
FutureFeatures *bool `json:"future_features"`
@@ -70,14 +70,13 @@ func (f *Features) ToMap() map[string]interface{} {
"compliance": *f.Compliance,
"cluster": *f.Cluster,
"metrics": *f.Metrics,
- "custom_brand": *f.CustomBrand,
"mhpns": *f.MHPNS,
"saml": *f.SAML,
- "password": *f.PasswordRequirements,
"elastic_search": *f.Elasticsearch,
"email_notification_contents": *f.EmailNotificationContents,
"data_retention": *f.DataRetention,
"message_export": *f.MessageExport,
+ "custom_permissions_schemes": *f.CustomPermissionsSchemes,
"future": *f.FutureFeatures,
}
}
@@ -119,10 +118,6 @@ func (f *Features) SetDefaults() {
f.Metrics = NewBool(*f.FutureFeatures)
}
- if f.CustomBrand == nil {
- f.CustomBrand = NewBool(*f.FutureFeatures)
- }
-
if f.MHPNS == nil {
f.MHPNS = NewBool(*f.FutureFeatures)
}
@@ -131,10 +126,6 @@ func (f *Features) SetDefaults() {
f.SAML = NewBool(*f.FutureFeatures)
}
- if f.PasswordRequirements == nil {
- f.PasswordRequirements = NewBool(*f.FutureFeatures)
- }
-
if f.Elasticsearch == nil {
f.Elasticsearch = NewBool(*f.FutureFeatures)
}
@@ -158,6 +149,14 @@ func (f *Features) SetDefaults() {
if f.MessageExport == nil {
f.MessageExport = NewBool(*f.FutureFeatures)
}
+
+ if f.CustomPermissionsSchemes == nil {
+ f.CustomPermissionsSchemes = NewBool(*f.FutureFeatures)
+ }
+
+ if f.CustomTermsOfService == nil {
+ f.CustomTermsOfService = NewBool(*f.FutureFeatures)
+ }
}
func (l *License) IsExpired() bool {
@@ -169,23 +168,33 @@ func (l *License) IsStarted() bool {
}
func (l *License) ToJson() string {
- b, err := json.Marshal(l)
- if err != nil {
- return ""
- } else {
- return string(b)
+ b, _ := json.Marshal(l)
+ return string(b)
+}
+
+// NewTestLicense returns a license that expires in the future and has the given features.
+func NewTestLicense(features ...string) *License {
+ ret := &License{
+ ExpiresAt: GetMillis() + 90*24*60*60*1000,
+ Customer: &Customer{},
+ Features: &Features{},
}
+ ret.Features.SetDefaults()
+
+ featureMap := map[string]bool{}
+ for _, feature := range features {
+ featureMap[feature] = true
+ }
+ featureJson, _ := json.Marshal(featureMap)
+ json.Unmarshal(featureJson, &ret.Features)
+
+ return ret
}
func LicenseFromJson(data io.Reader) *License {
- decoder := json.NewDecoder(data)
- var o License
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *License
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (lr *LicenseRecord) IsValid() *AppError {
diff --git a/vendor/github.com/mattermost/platform/model/manifest.go b/vendor/github.com/mattermost/mattermost-server/model/manifest.go
index 03d78f84..6a7df59f 100644
--- a/vendor/github.com/mattermost/platform/model/manifest.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/manifest.go
@@ -5,23 +5,16 @@ package model
import (
"encoding/json"
+ "fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
+ "strings"
"gopkg.in/yaml.v2"
)
-const (
- PLUGIN_CONFIG_TYPE_TEXT = "text"
- PLUGIN_CONFIG_TYPE_BOOL = "bool"
- PLUGIN_CONFIG_TYPE_RADIO = "radio"
- PLUGIN_CONFIG_TYPE_DROPDOWN = "dropdown"
- PLUGIN_CONFIG_TYPE_GENERATED = "generated"
- PLUGIN_CONFIG_TYPE_USERNAME = "username"
-)
-
type PluginOption struct {
// The display name for the option.
DisplayName string `json:"display_name" yaml:"display_name"`
@@ -92,7 +85,7 @@ type PluginSettingsSchema struct {
// id: com.mycompany.myplugin
// name: My Plugin
// description: This is my plugin. It does stuff.
-// backend:
+// server:
// executable: myplugin
// settings_schema:
// settings:
@@ -102,9 +95,9 @@ type PluginSettingsSchema struct {
// help_text: When true, an extra thing will be enabled!
// default: false
type Manifest struct {
- // The id is a globally unique identifier that represents your plugin. Ids are limited
- // to 190 characters. Reverse-DNS notation using a name you control is a good option.
- // For example, "com.mycompany.myplugin".
+ // The id is a globally unique identifier that represents your plugin. Ids must be at least
+ // 3 characters, at most 190 characters and must match ^[a-zA-Z0-9-_\.]+$.
+ // Reverse-DNS notation using a name you control is a good option, e.g. "com.mycompany.myplugin".
Id string `json:"id" yaml:"id"`
// The name to be displayed for the plugin.
@@ -116,8 +109,11 @@ type Manifest struct {
// A version number for your plugin. Semantic versioning is recommended: http://semver.org
Version string `json:"version" yaml:"version"`
- // If your plugin extends the server, you'll need define backend.
- Backend *ManifestBackend `json:"backend,omitempty" yaml:"backend,omitempty"`
+ // Server defines the server-side portion of your plugin.
+ Server *ManifestServer `json:"server,omitempty" yaml:"server,omitempty"`
+
+ // Backend is a deprecated flag for defining the server-side portion of your plugin. Going forward, use Server instead.
+ Backend *ManifestServer `json:"backend,omitempty" yaml:"backend,omitempty"`
// If your plugin extends the web app, you'll need to define webapp.
Webapp *ManifestWebapp `json:"webapp,omitempty" yaml:"webapp,omitempty"`
@@ -127,58 +123,60 @@ type Manifest struct {
SettingsSchema *PluginSettingsSchema `json:"settings_schema,omitempty" yaml:"settings_schema,omitempty"`
}
-type ManifestBackend struct {
- // The path to your executable binary. This should be relative to the root of your bundle and the
- // location of the manifest file.
+type ManifestServer struct {
+ // Executables are the paths to your executable binaries, specifying multiple entry points
+ // for different platforms when bundled together in a single plugin.
+ Executables *ManifestExecutables `json:"executables,omitempty" yaml:"executables,omitempty"`
+
+ // Executable is the path to your executable binary. This should be relative to the root
+ // of your bundle and the location of the manifest file.
//
// On Windows, this file must have a ".exe" extension.
+ //
+ // If your plugin is compiled for multiple platforms, consider bundling them together
+ // and using the Executables field instead.
Executable string `json:"executable" yaml:"executable"`
}
+type ManifestExecutables struct {
+ // LinuxAmd64 is the path to your executable binary for the corresponding platform
+ LinuxAmd64 string `json:"linux-amd64,omitempty" yaml:"linux-amd64,omitempty"`
+ // DarwinAmd64 is the path to your executable binary for the corresponding platform
+ DarwinAmd64 string `json:"darwin-amd64,omitempty" yaml:"darwin-amd64,omitempty"`
+ // WindowsAmd64 is the path to your executable binary for the corresponding platform
+ // This file must have a ".exe" extension
+ WindowsAmd64 string `json:"windows-amd64,omitempty" yaml:"windows-amd64,omitempty"`
+}
+
type ManifestWebapp struct {
// The path to your webapp bundle. This should be relative to the root of your bundle and the
// location of the manifest file.
BundlePath string `json:"bundle_path" yaml:"bundle_path"`
+
+ // BundleHash is the 64-bit FNV-1a hash of the webapp bundle, computed when the plugin is loaded
+ BundleHash []byte `json:"-"`
}
func (m *Manifest) ToJson() string {
- b, err := json.Marshal(m)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(m)
+ return string(b)
}
func ManifestListToJson(m []*Manifest) string {
- b, err := json.Marshal(m)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(m)
+ return string(b)
}
func ManifestFromJson(data io.Reader) *Manifest {
- decoder := json.NewDecoder(data)
- var m Manifest
- err := decoder.Decode(&m)
- if err == nil {
- return &m
- } else {
- return nil
- }
+ var m *Manifest
+ json.NewDecoder(data).Decode(&m)
+ return m
}
func ManifestListFromJson(data io.Reader) []*Manifest {
- decoder := json.NewDecoder(data)
var manifests []*Manifest
- err := decoder.Decode(&manifests)
- if err == nil {
- return manifests
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&manifests)
+ return manifests
}
func (m *Manifest) HasClient() bool {
@@ -190,10 +188,58 @@ func (m *Manifest) ClientManifest() *Manifest {
*cm = *m
cm.Name = ""
cm.Description = ""
- cm.Backend = nil
+ cm.Server = nil
+ if cm.Webapp != nil {
+ cm.Webapp = new(ManifestWebapp)
+ *cm.Webapp = *m.Webapp
+ cm.Webapp.BundlePath = "/static/" + m.Id + "/" + fmt.Sprintf("%s_%x_bundle.js", m.Id, m.Webapp.BundleHash)
+ }
return cm
}
+// GetExecutableForRuntime returns the path to the executable for the given runtime architecture.
+//
+// If the manifest defines multiple executables, but none match, or if only a single executable
+// is defined, the Executable field will be returned. This method does not guarantee that the
+// resulting binary can actually execute on the given platform.
+func (m *Manifest) GetExecutableForRuntime(goOs, goArch string) string {
+ server := m.Server
+
+ // Support the deprecated backend parameter.
+ if server == nil {
+ server = m.Backend
+ }
+
+ if server == nil {
+ return ""
+ }
+
+ var executable string
+ if server.Executables != nil {
+ if goOs == "linux" && goArch == "amd64" {
+ executable = server.Executables.LinuxAmd64
+ } else if goOs == "darwin" && goArch == "amd64" {
+ executable = server.Executables.DarwinAmd64
+ } else if goOs == "windows" && goArch == "amd64" {
+ executable = server.Executables.WindowsAmd64
+ }
+ }
+
+ if executable == "" {
+ executable = server.Executable
+ }
+
+ return executable
+}
+
+func (m *Manifest) HasServer() bool {
+ return m.Server != nil || m.Backend != nil
+}
+
+func (m *Manifest) HasWebapp() bool {
+ return m.Webapp != nil
+}
+
// FindManifest will find and parse the manifest in a given directory.
//
// In all cases other than a does-not-exist error, path is set to the path of the manifest file that was
@@ -223,6 +269,7 @@ func FindManifest(dir string) (manifest *Manifest, path string, err error) {
return
}
manifest = &parsed
+ manifest.Id = strings.ToLower(manifest.Id)
return
}
@@ -242,5 +289,6 @@ func FindManifest(dir string) (manifest *Manifest, path string, err error) {
return
}
manifest = &parsed
+ manifest.Id = strings.ToLower(manifest.Id)
return
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/message_export.go b/vendor/github.com/mattermost/mattermost-server/model/message_export.go
new file mode 100644
index 00000000..1cf764a6
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/message_export.go
@@ -0,0 +1,27 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+type MessageExport struct {
+ TeamId *string
+ TeamName *string
+ TeamDisplayName *string
+
+ ChannelId *string
+ ChannelName *string
+ ChannelDisplayName *string
+ ChannelType *string
+
+ UserId *string
+ UserEmail *string
+ Username *string
+
+ PostId *string
+ PostCreateAt *int64
+ PostMessage *string
+ PostType *string
+ PostRootId *string
+ PostOriginalId *string
+ PostFileIds StringArray
+}
diff --git a/vendor/github.com/mattermost/platform/model/mfa_secret.go b/vendor/github.com/mattermost/mattermost-server/model/mfa_secret.go
index 99580f5f..23a903c8 100644
--- a/vendor/github.com/mattermost/platform/model/mfa_secret.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/mfa_secret.go
@@ -14,21 +14,12 @@ type MfaSecret struct {
}
func (me *MfaSecret) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func MfaSecretFromJson(data io.Reader) *MfaSecret {
- decoder := json.NewDecoder(data)
- var me MfaSecret
- err := decoder.Decode(&me)
- if err == nil {
- return &me
- } else {
- return nil
- }
+ var me *MfaSecret
+ json.NewDecoder(data).Decode(&me)
+ return me
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/migration.go b/vendor/github.com/mattermost/mattermost-server/model/migration.go
new file mode 100644
index 00000000..ead7acce
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/migration.go
@@ -0,0 +1,8 @@
+// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+const (
+ MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2 = "migration_advanced_permissions_phase_2"
+)
diff --git a/vendor/github.com/mattermost/platform/model/oauth.go b/vendor/github.com/mattermost/mattermost-server/model/oauth.go
index 3139aefe..6f662a5a 100644
--- a/vendor/github.com/mattermost/platform/model/oauth.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/oauth.go
@@ -17,6 +17,7 @@ const (
OAUTH_ACTION_EMAIL_TO_SSO = "email_to_sso"
OAUTH_ACTION_SSO_TO_EMAIL = "sso_to_email"
OAUTH_ACTION_MOBILE = "mobile"
+ OAUTH_ACTION_CLIENT = "client"
)
type OAuthApp struct {
@@ -108,14 +109,9 @@ func (a *OAuthApp) PreUpdate() {
a.UpdateAt = GetMillis()
}
-// ToJson convert a User to a json string
func (a *OAuthApp) ToJson() string {
- b, err := json.Marshal(a)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(a)
+ return string(b)
}
// Generate a valid strong etag so the browser can cache the results
@@ -138,54 +134,19 @@ func (a *OAuthApp) IsValidRedirectURL(url string) bool {
return false
}
-// OAuthAppFromJson will decode the input and return a User
func OAuthAppFromJson(data io.Reader) *OAuthApp {
- decoder := json.NewDecoder(data)
- var app OAuthApp
- err := decoder.Decode(&app)
- if err == nil {
- return &app
- } else {
- return nil
- }
-}
-
-func OAuthAppMapToJson(a map[string]*OAuthApp) string {
- b, err := json.Marshal(a)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func OAuthAppMapFromJson(data io.Reader) map[string]*OAuthApp {
- decoder := json.NewDecoder(data)
- var apps map[string]*OAuthApp
- err := decoder.Decode(&apps)
- if err == nil {
- return apps
- } else {
- return nil
- }
+ var app *OAuthApp
+ json.NewDecoder(data).Decode(&app)
+ return app
}
func OAuthAppListToJson(l []*OAuthApp) string {
- b, err := json.Marshal(l)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(l)
+ return string(b)
}
func OAuthAppListFromJson(data io.Reader) []*OAuthApp {
- decoder := json.NewDecoder(data)
var o []*OAuthApp
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/outgoing_webhook.go b/vendor/github.com/mattermost/mattermost-server/model/outgoing_webhook.go
index 477a277d..5f7a67d0 100644
--- a/vendor/github.com/mattermost/platform/model/outgoing_webhook.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/outgoing_webhook.go
@@ -28,6 +28,8 @@ type OutgoingWebhook struct {
DisplayName string `json:"display_name"`
Description string `json:"description"`
ContentType string `json:"content_type"`
+ Username string `json:"username"`
+ IconURL string `json:"icon_url"`
}
type OutgoingWebhookPayload struct {
@@ -46,23 +48,20 @@ type OutgoingWebhookPayload struct {
}
type OutgoingWebhookResponse struct {
- Text *string `json:"text"`
- Username string `json:"username"`
- IconURL string `json:"icon_url"`
- Props StringInterface `json:"props"`
- Type string `json:"type"`
- ResponseType string `json:"response_type"`
+ Text *string `json:"text"`
+ Username string `json:"username"`
+ IconURL string `json:"icon_url"`
+ Props StringInterface `json:"props"`
+ Attachments []*SlackAttachment `json:"attachments"`
+ Type string `json:"type"`
+ ResponseType string `json:"response_type"`
}
const OUTGOING_HOOK_RESPONSE_TYPE_COMMENT = "comment"
func (o *OutgoingWebhookPayload) ToJSON() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func (o *OutgoingWebhookPayload) ToFormValues() string {
@@ -84,63 +83,36 @@ func (o *OutgoingWebhookPayload) ToFormValues() string {
}
func (o *OutgoingWebhook) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func OutgoingWebhookFromJson(data io.Reader) *OutgoingWebhook {
- decoder := json.NewDecoder(data)
- var o OutgoingWebhook
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *OutgoingWebhook
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func OutgoingWebhookListToJson(l []*OutgoingWebhook) string {
- b, err := json.Marshal(l)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(l)
+ return string(b)
}
func OutgoingWebhookListFromJson(data io.Reader) []*OutgoingWebhook {
- decoder := json.NewDecoder(data)
var o []*OutgoingWebhook
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *OutgoingWebhookResponse) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func OutgoingWebhookResponseFromJson(data io.Reader) *OutgoingWebhookResponse {
- decoder := json.NewDecoder(data)
- var o OutgoingWebhookResponse
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *OutgoingWebhookResponse
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *OutgoingWebhook) IsValid() *AppError {
@@ -199,7 +171,7 @@ func (o *OutgoingWebhook) IsValid() *AppError {
return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.display_name.app_error", nil, "", http.StatusBadRequest)
}
- if len(o.Description) > 128 {
+ if len(o.Description) > 500 {
return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "", http.StatusBadRequest)
}
@@ -211,6 +183,14 @@ func (o *OutgoingWebhook) IsValid() *AppError {
return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.content_type.app_error", nil, "", http.StatusBadRequest)
}
+ if len(o.Username) > 64 {
+ return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.username.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if len(o.IconURL) > 1024 {
+ return NewAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.icon_url.app_error", nil, "", http.StatusBadRequest)
+ }
+
return nil
}
diff --git a/vendor/github.com/mattermost/platform/model/authorization.go b/vendor/github.com/mattermost/mattermost-server/model/permission.go
index 9f4e36ea..737321cc 100644
--- a/vendor/github.com/mattermost/platform/model/authorization.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/permission.go
@@ -3,17 +3,17 @@
package model
+const (
+ PERMISSION_SCOPE_SYSTEM = "system_scope"
+ PERMISSION_SCOPE_TEAM = "team_scope"
+ PERMISSION_SCOPE_CHANNEL = "channel_scope"
+)
+
type Permission struct {
Id string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
-}
-
-type Role struct {
- Id string `json:"id"`
- Name string `json:"name"`
- Description string `json:"description"`
- Permissions []string `json:"permissions"`
+ Scope string `json:"scope"`
}
var PERMISSION_INVITE_USER *Permission
@@ -40,6 +40,9 @@ var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission
var PERMISSION_EDIT_OTHER_USERS *Permission
var PERMISSION_READ_CHANNEL *Permission
var PERMISSION_READ_PUBLIC_CHANNEL *Permission
+var PERMISSION_ADD_REACTION *Permission
+var PERMISSION_REMOVE_REACTION *Permission
+var PERMISSION_REMOVE_OTHERS_REACTIONS *Permission
var PERMISSION_PERMANENT_DELETE_USER *Permission
var PERMISSION_UPLOAD_FILE *Permission
var PERMISSION_GET_PUBLIC_LINK *Permission
@@ -47,8 +50,11 @@ var PERMISSION_MANAGE_WEBHOOKS *Permission
var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission
var PERMISSION_MANAGE_OAUTH *Permission
var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission
+var PERMISSION_MANAGE_EMOJIS *Permission
+var PERMISSION_MANAGE_OTHERS_EMOJIS *Permission
var PERMISSION_CREATE_POST *Permission
var PERMISSION_CREATE_POST_PUBLIC *Permission
+var PERMISSION_CREATE_POST_EPHEMERAL *Permission
var PERMISSION_EDIT_POST *Permission
var PERMISSION_EDIT_OTHERS_POSTS *Permission
var PERMISSION_DELETE_POST *Permission
@@ -64,459 +70,397 @@ var PERMISSION_CREATE_USER_ACCESS_TOKEN *Permission
var PERMISSION_READ_USER_ACCESS_TOKEN *Permission
var PERMISSION_REVOKE_USER_ACCESS_TOKEN *Permission
-// General permission that encompases all system admin functions
+// General permission that encompasses all system admin functions
// in the future this could be broken up to allow access to some
// admin functions but not others
var PERMISSION_MANAGE_SYSTEM *Permission
-const (
- SYSTEM_USER_ROLE_ID = "system_user"
- SYSTEM_ADMIN_ROLE_ID = "system_admin"
- SYSTEM_POST_ALL_ROLE_ID = "system_post_all"
- SYSTEM_POST_ALL_PUBLIC_ROLE_ID = "system_post_all_public"
- SYSTEM_USER_ACCESS_TOKEN_ROLE_ID = "system_user_access_token"
-
- TEAM_USER_ROLE_ID = "team_user"
- TEAM_ADMIN_ROLE_ID = "team_admin"
- TEAM_POST_ALL_ROLE_ID = "team_post_all"
- TEAM_POST_ALL_PUBLIC_ROLE_ID = "team_post_all_public"
-
- CHANNEL_USER_ROLE_ID = "channel_user"
- CHANNEL_ADMIN_ROLE_ID = "channel_admin"
- CHANNEL_GUEST_ROLE_ID = "guest"
-)
+var ALL_PERMISSIONS []*Permission
func initializePermissions() {
PERMISSION_INVITE_USER = &Permission{
"invite_user",
"authentication.permissions.team_invite_user.name",
"authentication.permissions.team_invite_user.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_ADD_USER_TO_TEAM = &Permission{
"add_user_to_team",
"authentication.permissions.add_user_to_team.name",
"authentication.permissions.add_user_to_team.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_USE_SLASH_COMMANDS = &Permission{
"use_slash_commands",
"authentication.permissions.team_use_slash_commands.name",
"authentication.permissions.team_use_slash_commands.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_MANAGE_SLASH_COMMANDS = &Permission{
"manage_slash_commands",
"authentication.permissions.manage_slash_commands.name",
"authentication.permissions.manage_slash_commands.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS = &Permission{
"manage_others_slash_commands",
"authentication.permissions.manage_others_slash_commands.name",
"authentication.permissions.manage_others_slash_commands.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_CREATE_PUBLIC_CHANNEL = &Permission{
"create_public_channel",
"authentication.permissions.create_public_channel.name",
"authentication.permissions.create_public_channel.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_CREATE_PRIVATE_CHANNEL = &Permission{
"create_private_channel",
"authentication.permissions.create_private_channel.name",
"authentication.permissions.create_private_channel.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS = &Permission{
"manage_public_channel_members",
"authentication.permissions.manage_public_channel_members.name",
"authentication.permissions.manage_public_channel_members.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS = &Permission{
"manage_private_channel_members",
"authentication.permissions.manage_private_channel_members.name",
"authentication.permissions.manage_private_channel_members.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE = &Permission{
"assign_system_admin_role",
"authentication.permissions.assign_system_admin_role.name",
"authentication.permissions.assign_system_admin_role.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_MANAGE_ROLES = &Permission{
"manage_roles",
"authentication.permissions.manage_roles.name",
"authentication.permissions.manage_roles.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_MANAGE_TEAM_ROLES = &Permission{
"manage_team_roles",
"authentication.permissions.manage_team_roles.name",
"authentication.permissions.manage_team_roles.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_MANAGE_CHANNEL_ROLES = &Permission{
"manage_channel_roles",
"authentication.permissions.manage_channel_roles.name",
"authentication.permissions.manage_channel_roles.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_MANAGE_SYSTEM = &Permission{
"manage_system",
"authentication.permissions.manage_system.name",
"authentication.permissions.manage_system.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_CREATE_DIRECT_CHANNEL = &Permission{
"create_direct_channel",
"authentication.permissions.create_direct_channel.name",
"authentication.permissions.create_direct_channel.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_CREATE_GROUP_CHANNEL = &Permission{
"create_group_channel",
"authentication.permissions.create_group_channel.name",
"authentication.permissions.create_group_channel.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{
- "manage__publicchannel_properties",
+ "manage_public_channel_properties",
"authentication.permissions.manage_public_channel_properties.name",
"authentication.permissions.manage_public_channel_properties.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES = &Permission{
"manage_private_channel_properties",
"authentication.permissions.manage_private_channel_properties.name",
"authentication.permissions.manage_private_channel_properties.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_LIST_TEAM_CHANNELS = &Permission{
"list_team_channels",
"authentication.permissions.list_team_channels.name",
"authentication.permissions.list_team_channels.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_JOIN_PUBLIC_CHANNELS = &Permission{
"join_public_channels",
"authentication.permissions.join_public_channels.name",
"authentication.permissions.join_public_channels.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_DELETE_PUBLIC_CHANNEL = &Permission{
"delete_public_channel",
"authentication.permissions.delete_public_channel.name",
"authentication.permissions.delete_public_channel.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_DELETE_PRIVATE_CHANNEL = &Permission{
"delete_private_channel",
"authentication.permissions.delete_private_channel.name",
"authentication.permissions.delete_private_channel.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_EDIT_OTHER_USERS = &Permission{
"edit_other_users",
"authentication.permissions.edit_other_users.name",
"authentication.permissions.edit_other_users.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_READ_CHANNEL = &Permission{
"read_channel",
"authentication.permissions.read_channel.name",
"authentication.permissions.read_channel.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_READ_PUBLIC_CHANNEL = &Permission{
"read_public_channel",
"authentication.permissions.read_public_channel.name",
"authentication.permissions.read_public_channel.description",
+ PERMISSION_SCOPE_TEAM,
+ }
+ PERMISSION_ADD_REACTION = &Permission{
+ "add_reaction",
+ "authentication.permissions.add_reaction.name",
+ "authentication.permissions.add_reaction.description",
+ PERMISSION_SCOPE_CHANNEL,
+ }
+ PERMISSION_REMOVE_REACTION = &Permission{
+ "remove_reaction",
+ "authentication.permissions.remove_reaction.name",
+ "authentication.permissions.remove_reaction.description",
+ PERMISSION_SCOPE_CHANNEL,
+ }
+ PERMISSION_REMOVE_OTHERS_REACTIONS = &Permission{
+ "remove_others_reactions",
+ "authentication.permissions.remove_others_reactions.name",
+ "authentication.permissions.remove_others_reactions.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_PERMANENT_DELETE_USER = &Permission{
"permanent_delete_user",
"authentication.permissions.permanent_delete_user.name",
"authentication.permissions.permanent_delete_user.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_UPLOAD_FILE = &Permission{
"upload_file",
"authentication.permissions.upload_file.name",
"authentication.permissions.upload_file.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_GET_PUBLIC_LINK = &Permission{
"get_public_link",
"authentication.permissions.get_public_link.name",
"authentication.permissions.get_public_link.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_MANAGE_WEBHOOKS = &Permission{
"manage_webhooks",
"authentication.permissions.manage_webhooks.name",
"authentication.permissions.manage_webhooks.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_MANAGE_OTHERS_WEBHOOKS = &Permission{
"manage_others_webhooks",
"authentication.permissions.manage_others_webhooks.name",
"authentication.permissions.manage_others_webhooks.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_MANAGE_OAUTH = &Permission{
"manage_oauth",
"authentication.permissions.manage_oauth.name",
"authentication.permissions.manage_oauth.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH = &Permission{
- "manage_sytem_wide_oauth",
- "authentication.permissions.manage_sytem_wide_oauth.name",
- "authentication.permissions.manage_sytem_wide_oauth.description",
+ "manage_system_wide_oauth",
+ "authentication.permissions.manage_system_wide_oauth.name",
+ "authentication.permissions.manage_system_wide_oauth.description",
+ PERMISSION_SCOPE_SYSTEM,
+ }
+ PERMISSION_MANAGE_EMOJIS = &Permission{
+ "manage_emojis",
+ "authentication.permissions.manage_emojis.name",
+ "authentication.permissions.manage_emojis.description",
+ PERMISSION_SCOPE_TEAM,
+ }
+ PERMISSION_MANAGE_OTHERS_EMOJIS = &Permission{
+ "manage_others_emojis",
+ "authentication.permissions.manage_others_emojis.name",
+ "authentication.permissions.manage_others_emojis.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_CREATE_POST = &Permission{
"create_post",
"authentication.permissions.create_post.name",
"authentication.permissions.create_post.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_CREATE_POST_PUBLIC = &Permission{
"create_post_public",
"authentication.permissions.create_post_public.name",
"authentication.permissions.create_post_public.description",
+ PERMISSION_SCOPE_CHANNEL,
+ }
+ PERMISSION_CREATE_POST_EPHEMERAL = &Permission{
+ "create_post_ephemeral",
+ "authentication.permissions.create_post_ephemeral.name",
+ "authentication.permissions.create_post_ephemeral.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_EDIT_POST = &Permission{
"edit_post",
"authentication.permissions.edit_post.name",
"authentication.permissions.edit_post.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_EDIT_OTHERS_POSTS = &Permission{
"edit_others_posts",
"authentication.permissions.edit_others_posts.name",
"authentication.permissions.edit_others_posts.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_DELETE_POST = &Permission{
"delete_post",
"authentication.permissions.delete_post.name",
"authentication.permissions.delete_post.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_DELETE_OTHERS_POSTS = &Permission{
"delete_others_posts",
"authentication.permissions.delete_others_posts.name",
"authentication.permissions.delete_others_posts.description",
+ PERMISSION_SCOPE_CHANNEL,
}
PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{
"remove_user_from_team",
"authentication.permissions.remove_user_from_team.name",
"authentication.permissions.remove_user_from_team.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_CREATE_TEAM = &Permission{
"create_team",
"authentication.permissions.create_team.name",
"authentication.permissions.create_team.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_MANAGE_TEAM = &Permission{
"manage_team",
"authentication.permissions.manage_team.name",
"authentication.permissions.manage_team.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_IMPORT_TEAM = &Permission{
"import_team",
"authentication.permissions.import_team.name",
"authentication.permissions.import_team.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_VIEW_TEAM = &Permission{
"view_team",
"authentication.permissions.view_team.name",
"authentication.permissions.view_team.description",
+ PERMISSION_SCOPE_TEAM,
}
PERMISSION_LIST_USERS_WITHOUT_TEAM = &Permission{
"list_users_without_team",
"authentication.permissions.list_users_without_team.name",
"authentication.permissions.list_users_without_team.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_CREATE_USER_ACCESS_TOKEN = &Permission{
"create_user_access_token",
"authentication.permissions.create_user_access_token.name",
"authentication.permissions.create_user_access_token.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_READ_USER_ACCESS_TOKEN = &Permission{
"read_user_access_token",
"authentication.permissions.read_user_access_token.name",
"authentication.permissions.read_user_access_token.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_REVOKE_USER_ACCESS_TOKEN = &Permission{
"revoke_user_access_token",
"authentication.permissions.revoke_user_access_token.name",
"authentication.permissions.revoke_user_access_token.description",
+ PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_MANAGE_JOBS = &Permission{
"manage_jobs",
"authentication.permisssions.manage_jobs.name",
"authentication.permisssions.manage_jobs.description",
- }
-}
-
-var DefaultRoles map[string]*Role
-
-func initializeDefaultRoles() {
- DefaultRoles = make(map[string]*Role)
-
- DefaultRoles[CHANNEL_USER_ROLE_ID] = &Role{
- "channel_user",
- "authentication.roles.channel_user.name",
- "authentication.roles.channel_user.description",
- []string{
- PERMISSION_READ_CHANNEL.Id,
- PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
- PERMISSION_UPLOAD_FILE.Id,
- PERMISSION_GET_PUBLIC_LINK.Id,
- PERMISSION_CREATE_POST.Id,
- PERMISSION_EDIT_POST.Id,
- PERMISSION_USE_SLASH_COMMANDS.Id,
- },
- }
-
- DefaultRoles[CHANNEL_ADMIN_ROLE_ID] = &Role{
- "channel_admin",
- "authentication.roles.channel_admin.name",
- "authentication.roles.channel_admin.description",
- []string{
- PERMISSION_MANAGE_CHANNEL_ROLES.Id,
- },
- }
-
- DefaultRoles[CHANNEL_GUEST_ROLE_ID] = &Role{
- "guest",
- "authentication.roles.global_guest.name",
- "authentication.roles.global_guest.description",
- []string{},
- }
-
- DefaultRoles[TEAM_USER_ROLE_ID] = &Role{
- "team_user",
- "authentication.roles.team_user.name",
- "authentication.roles.team_user.description",
- []string{
- PERMISSION_LIST_TEAM_CHANNELS.Id,
- PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
- PERMISSION_READ_PUBLIC_CHANNEL.Id,
- PERMISSION_VIEW_TEAM.Id,
- },
+ PERMISSION_SCOPE_SYSTEM,
}
- DefaultRoles[TEAM_POST_ALL_ROLE_ID] = &Role{
- "team_post_all",
- "authentication.roles.team_post_all.name",
- "authentication.roles.team_post_all.description",
- []string{
- PERMISSION_CREATE_POST.Id,
- },
+ ALL_PERMISSIONS = []*Permission{
+ PERMISSION_INVITE_USER,
+ PERMISSION_ADD_USER_TO_TEAM,
+ PERMISSION_USE_SLASH_COMMANDS,
+ PERMISSION_MANAGE_SLASH_COMMANDS,
+ PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS,
+ PERMISSION_CREATE_PUBLIC_CHANNEL,
+ PERMISSION_CREATE_PRIVATE_CHANNEL,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS,
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS,
+ PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE,
+ PERMISSION_MANAGE_ROLES,
+ PERMISSION_MANAGE_TEAM_ROLES,
+ PERMISSION_MANAGE_CHANNEL_ROLES,
+ PERMISSION_CREATE_DIRECT_CHANNEL,
+ PERMISSION_CREATE_GROUP_CHANNEL,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES,
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES,
+ PERMISSION_LIST_TEAM_CHANNELS,
+ PERMISSION_JOIN_PUBLIC_CHANNELS,
+ PERMISSION_DELETE_PUBLIC_CHANNEL,
+ PERMISSION_DELETE_PRIVATE_CHANNEL,
+ PERMISSION_EDIT_OTHER_USERS,
+ PERMISSION_READ_CHANNEL,
+ PERMISSION_READ_PUBLIC_CHANNEL,
+ PERMISSION_ADD_REACTION,
+ PERMISSION_REMOVE_REACTION,
+ PERMISSION_REMOVE_OTHERS_REACTIONS,
+ PERMISSION_PERMANENT_DELETE_USER,
+ PERMISSION_UPLOAD_FILE,
+ PERMISSION_GET_PUBLIC_LINK,
+ PERMISSION_MANAGE_WEBHOOKS,
+ PERMISSION_MANAGE_OTHERS_WEBHOOKS,
+ PERMISSION_MANAGE_OAUTH,
+ PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH,
+ PERMISSION_MANAGE_EMOJIS,
+ PERMISSION_MANAGE_OTHERS_EMOJIS,
+ PERMISSION_CREATE_POST,
+ PERMISSION_CREATE_POST_PUBLIC,
+ PERMISSION_CREATE_POST_EPHEMERAL,
+ PERMISSION_EDIT_POST,
+ PERMISSION_EDIT_OTHERS_POSTS,
+ PERMISSION_DELETE_POST,
+ PERMISSION_DELETE_OTHERS_POSTS,
+ PERMISSION_REMOVE_USER_FROM_TEAM,
+ PERMISSION_CREATE_TEAM,
+ PERMISSION_MANAGE_TEAM,
+ PERMISSION_IMPORT_TEAM,
+ PERMISSION_VIEW_TEAM,
+ PERMISSION_LIST_USERS_WITHOUT_TEAM,
+ PERMISSION_MANAGE_JOBS,
+ PERMISSION_CREATE_USER_ACCESS_TOKEN,
+ PERMISSION_READ_USER_ACCESS_TOKEN,
+ PERMISSION_REVOKE_USER_ACCESS_TOKEN,
+ PERMISSION_MANAGE_SYSTEM,
}
-
- DefaultRoles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
- "team_post_all_public",
- "authentication.roles.team_post_all_public.name",
- "authentication.roles.team_post_all_public.description",
- []string{
- PERMISSION_CREATE_POST_PUBLIC.Id,
- },
- }
-
- DefaultRoles[TEAM_ADMIN_ROLE_ID] = &Role{
- "team_admin",
- "authentication.roles.team_admin.name",
- "authentication.roles.team_admin.description",
- []string{
- PERMISSION_EDIT_OTHERS_POSTS.Id,
- PERMISSION_REMOVE_USER_FROM_TEAM.Id,
- PERMISSION_MANAGE_TEAM.Id,
- PERMISSION_IMPORT_TEAM.Id,
- PERMISSION_MANAGE_TEAM_ROLES.Id,
- PERMISSION_MANAGE_CHANNEL_ROLES.Id,
- PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
- PERMISSION_MANAGE_SLASH_COMMANDS.Id,
- PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
- PERMISSION_MANAGE_WEBHOOKS.Id,
- },
- }
-
- DefaultRoles[SYSTEM_USER_ROLE_ID] = &Role{
- "system_user",
- "authentication.roles.global_user.name",
- "authentication.roles.global_user.description",
- []string{
- PERMISSION_CREATE_DIRECT_CHANNEL.Id,
- PERMISSION_CREATE_GROUP_CHANNEL.Id,
- PERMISSION_PERMANENT_DELETE_USER.Id,
- },
- }
-
- DefaultRoles[SYSTEM_POST_ALL_ROLE_ID] = &Role{
- "system_post_all",
- "authentication.roles.system_post_all.name",
- "authentication.roles.system_post_all.description",
- []string{
- PERMISSION_CREATE_POST.Id,
- },
- }
-
- DefaultRoles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
- "system_post_all_public",
- "authentication.roles.system_post_all_public.name",
- "authentication.roles.system_post_all_public.description",
- []string{
- PERMISSION_CREATE_POST_PUBLIC.Id,
- },
- }
-
- DefaultRoles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{
- "system_user_access_token",
- "authentication.roles.system_user_access_token.name",
- "authentication.roles.system_user_access_token.description",
- []string{
- PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
- PERMISSION_READ_USER_ACCESS_TOKEN.Id,
- PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
- },
- }
-
- DefaultRoles[SYSTEM_ADMIN_ROLE_ID] = &Role{
- "system_admin",
- "authentication.roles.global_admin.name",
- "authentication.roles.global_admin.description",
- // System admins can do anything channel and team admins can do
- // plus everything members of teams and channels can do to all teams
- // and channels on the system
- append(
- append(
- append(
- append(
- []string{
- PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
- PERMISSION_MANAGE_SYSTEM.Id,
- PERMISSION_MANAGE_ROLES.Id,
- PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
- PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
- PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
- PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
- PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
- PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
- PERMISSION_DELETE_PRIVATE_CHANNEL.Id,
- PERMISSION_CREATE_PRIVATE_CHANNEL.Id,
- PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
- PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
- PERMISSION_EDIT_OTHER_USERS.Id,
- PERMISSION_MANAGE_OAUTH.Id,
- PERMISSION_INVITE_USER.Id,
- PERMISSION_DELETE_POST.Id,
- PERMISSION_DELETE_OTHERS_POSTS.Id,
- PERMISSION_CREATE_TEAM.Id,
- PERMISSION_ADD_USER_TO_TEAM.Id,
- PERMISSION_LIST_USERS_WITHOUT_TEAM.Id,
- PERMISSION_MANAGE_JOBS.Id,
- PERMISSION_CREATE_POST_PUBLIC.Id,
- PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
- PERMISSION_READ_USER_ACCESS_TOKEN.Id,
- PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
- },
- DefaultRoles[TEAM_USER_ROLE_ID].Permissions...,
- ),
- DefaultRoles[CHANNEL_USER_ROLE_ID].Permissions...,
- ),
- DefaultRoles[TEAM_ADMIN_ROLE_ID].Permissions...,
- ),
- DefaultRoles[CHANNEL_ADMIN_ROLE_ID].Permissions...,
- ),
- }
-}
-
-func RoleIdsToString(roles []string) string {
- output := ""
- for _, role := range roles {
- output += role + ", "
- }
-
- if output == "" {
- return "[<NO ROLES>]"
- }
-
- return output[:len(output)-1]
}
func init() {
initializePermissions()
- initializeDefaultRoles()
}
diff --git a/vendor/github.com/mattermost/platform/model/plugin_key_value.go b/vendor/github.com/mattermost/mattermost-server/model/plugin_key_value.go
index b7a7731c..b7a7731c 100644
--- a/vendor/github.com/mattermost/platform/model/plugin_key_value.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/plugin_key_value.go
diff --git a/vendor/github.com/mattermost/mattermost-server/model/plugin_status.go b/vendor/github.com/mattermost/mattermost-server/model/plugin_status.go
new file mode 100644
index 00000000..db276402
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/plugin_status.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+const (
+ PluginStateNotRunning = 0
+ PluginStateStarting = 1 // unused by server
+ PluginStateRunning = 2
+ PluginStateFailedToStart = 3
+ PluginStateFailedToStayRunning = 4 // unused by server
+ PluginStateStopping = 5 // unused by server
+)
+
+// PluginStatus provides a cluster-aware view of installed plugins.
+type PluginStatus struct {
+ PluginId string `json:"plugin_id"`
+ ClusterId string `json:"cluster_id"`
+ PluginPath string `json:"plugin_path"`
+ State int `json:"state"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Version string `json:"version"`
+}
+
+type PluginStatuses []*PluginStatus
+
+func (m *PluginStatuses) ToJson() string {
+ b, _ := json.Marshal(m)
+ return string(b)
+}
+
+func PluginStatusesFromJson(data io.Reader) PluginStatuses {
+ var m PluginStatuses
+ json.NewDecoder(data).Decode(&m)
+ return m
+}
diff --git a/vendor/github.com/mattermost/platform/model/plugins_response.go b/vendor/github.com/mattermost/mattermost-server/model/plugins_response.go
index 74c89af2..177cfe6c 100644
--- a/vendor/github.com/mattermost/platform/model/plugins_response.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/plugins_response.go
@@ -10,7 +10,6 @@ import (
type PluginInfo struct {
Manifest
- Prepackaged bool `json:"prepackaged"`
}
type PluginsResponse struct {
@@ -19,21 +18,12 @@ type PluginsResponse struct {
}
func (m *PluginsResponse) ToJson() string {
- b, err := json.Marshal(m)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(m)
+ return string(b)
}
func PluginsResponseFromJson(data io.Reader) *PluginsResponse {
- decoder := json.NewDecoder(data)
- var m PluginsResponse
- err := decoder.Decode(&m)
- if err == nil {
- return &m
- } else {
- return nil
- }
+ var m *PluginsResponse
+ json.NewDecoder(data).Decode(&m)
+ return m
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/post.go b/vendor/github.com/mattermost/mattermost-server/model/post.go
new file mode 100644
index 00000000..5d2438fc
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/post.go
@@ -0,0 +1,587 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+ "net/http"
+ "sort"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/mattermost/mattermost-server/utils/markdown"
+)
+
+const (
+ POST_SYSTEM_MESSAGE_PREFIX = "system_"
+ POST_DEFAULT = ""
+ POST_SLACK_ATTACHMENT = "slack_attachment"
+ POST_SYSTEM_GENERIC = "system_generic"
+ POST_JOIN_LEAVE = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead
+ POST_JOIN_CHANNEL = "system_join_channel"
+ POST_LEAVE_CHANNEL = "system_leave_channel"
+ POST_JOIN_TEAM = "system_join_team"
+ POST_LEAVE_TEAM = "system_leave_team"
+ POST_AUTO_RESPONDER = "system_auto_responder"
+ POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead
+ POST_ADD_TO_CHANNEL = "system_add_to_channel"
+ POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel"
+ POST_MOVE_CHANNEL = "system_move_channel"
+ POST_ADD_TO_TEAM = "system_add_to_team"
+ POST_REMOVE_FROM_TEAM = "system_remove_from_team"
+ POST_HEADER_CHANGE = "system_header_change"
+ POST_DISPLAYNAME_CHANGE = "system_displayname_change"
+ POST_CONVERT_CHANNEL = "system_convert_channel"
+ POST_PURPOSE_CHANGE = "system_purpose_change"
+ POST_CHANNEL_DELETED = "system_channel_deleted"
+ POST_EPHEMERAL = "system_ephemeral"
+ POST_CHANGE_CHANNEL_PRIVACY = "system_change_chan_privacy"
+ POST_FILEIDS_MAX_RUNES = 150
+ POST_FILENAMES_MAX_RUNES = 4000
+ POST_HASHTAGS_MAX_RUNES = 1000
+ POST_MESSAGE_MAX_RUNES_V1 = 4000
+ POST_MESSAGE_MAX_BYTES_V2 = 65535 // Maximum size of a TEXT column in MySQL
+ POST_MESSAGE_MAX_RUNES_V2 = POST_MESSAGE_MAX_BYTES_V2 / 4 // Assume a worst-case representation
+ POST_PROPS_MAX_RUNES = 8000
+ POST_PROPS_MAX_USER_RUNES = POST_PROPS_MAX_RUNES - 400 // Leave some room for system / pre-save modifications
+ POST_CUSTOM_TYPE_PREFIX = "custom_"
+ PROPS_ADD_CHANNEL_MEMBER = "add_channel_member"
+ POST_PROPS_ADDED_USER_ID = "addedUserId"
+ POST_PROPS_DELETE_BY = "deleteBy"
+ POST_ACTION_TYPE_BUTTON = "button"
+ POST_ACTION_TYPE_SELECT = "select"
+)
+
+type Post struct {
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ EditAt int64 `json:"edit_at"`
+ DeleteAt int64 `json:"delete_at"`
+ IsPinned bool `json:"is_pinned"`
+ UserId string `json:"user_id"`
+ ChannelId string `json:"channel_id"`
+ RootId string `json:"root_id"`
+ ParentId string `json:"parent_id"`
+ OriginalId string `json:"original_id"`
+
+ Message string `json:"message"`
+
+ // MessageSource will contain the message as submitted by the user if Message has been modified
+ // by Mattermost for presentation (e.g if an image proxy is being used). It should be used to
+ // populate edit boxes if present.
+ MessageSource string `json:"message_source,omitempty" db:"-"`
+
+ Type string `json:"type"`
+ Props StringInterface `json:"props"`
+ Hashtags string `json:"hashtags"`
+ Filenames StringArray `json:"filenames,omitempty"` // Deprecated, do not use this field any more
+ FileIds StringArray `json:"file_ids,omitempty"`
+ PendingPostId string `json:"pending_post_id" db:"-"`
+ HasReactions bool `json:"has_reactions,omitempty"`
+}
+
+type PostEphemeral struct {
+ UserID string `json:"user_id"`
+ Post *Post `json:"post"`
+}
+
+type PostPatch struct {
+ IsPinned *bool `json:"is_pinned"`
+ Message *string `json:"message"`
+ Props *StringInterface `json:"props"`
+ FileIds *StringArray `json:"file_ids"`
+ HasReactions *bool `json:"has_reactions"`
+}
+
+type SearchParameter struct {
+ Terms *string `json:"terms"`
+ IsOrSearch *bool `json:"is_or_search"`
+ TimeZoneOffset *int `json:"time_zone_offset"`
+ Page *int `json:"page"`
+ PerPage *int `json:"per_page"`
+ IncludeDeletedChannels *bool `json:"include_deleted_channels"`
+}
+
+func (o *PostPatch) WithRewrittenImageURLs(f func(string) string) *PostPatch {
+ copy := *o
+ if copy.Message != nil {
+ *copy.Message = RewriteImageURLs(*o.Message, f)
+ }
+ return &copy
+}
+
+type PostForExport struct {
+ Post
+ TeamName string
+ ChannelName string
+ Username string
+ ReplyCount int
+}
+
+type ReplyForExport struct {
+ Post
+ Username string
+}
+
+type PostForIndexing struct {
+ Post
+ TeamId string `json:"team_id"`
+ ParentCreateAt *int64 `json:"parent_create_at"`
+}
+
+type DoPostActionRequest struct {
+ SelectedOption string `json:"selected_option"`
+}
+
+type PostAction struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ DataSource string `json:"data_source"`
+ Options []*PostActionOptions `json:"options"`
+ Integration *PostActionIntegration `json:"integration,omitempty"`
+}
+
+type PostActionOptions struct {
+ Text string `json:"text"`
+ Value string `json:"value"`
+}
+
+type PostActionIntegration struct {
+ URL string `json:"url,omitempty"`
+ Context StringInterface `json:"context,omitempty"`
+}
+
+type PostActionIntegrationRequest struct {
+ UserId string `json:"user_id"`
+ ChannelId string `json:"channel_id"`
+ TeamId string `json:"team_id"`
+ PostId string `json:"post_id"`
+ Type string `json:"type"`
+ DataSource string `json:"data_source"`
+ Context StringInterface `json:"context,omitempty"`
+}
+
+type PostActionIntegrationResponse struct {
+ Update *Post `json:"update"`
+ EphemeralText string `json:"ephemeral_text"`
+}
+
+func (o *Post) ToJson() string {
+ copy := *o
+ copy.StripActionIntegrations()
+ b, _ := json.Marshal(&copy)
+ return string(b)
+}
+
+func (o *Post) ToUnsanitizedJson() string {
+ b, _ := json.Marshal(o)
+ return string(b)
+}
+
+func PostFromJson(data io.Reader) *Post {
+ var o *Post
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
+
+func (o *Post) Etag() string {
+ return Etag(o.Id, o.UpdateAt)
+}
+
+func (o *Post) IsValid(maxPostSize int) *AppError {
+
+ if len(o.Id) != 26 {
+ return NewAppError("Post.IsValid", "model.post.is_valid.id.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if o.CreateAt == 0 {
+ return NewAppError("Post.IsValid", "model.post.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
+ }
+
+ if o.UpdateAt == 0 {
+ return NewAppError("Post.IsValid", "model.post.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
+ }
+
+ if len(o.UserId) != 26 {
+ return NewAppError("Post.IsValid", "model.post.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if len(o.ChannelId) != 26 {
+ return NewAppError("Post.IsValid", "model.post.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if !(len(o.RootId) == 26 || len(o.RootId) == 0) {
+ return NewAppError("Post.IsValid", "model.post.is_valid.root_id.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if !(len(o.ParentId) == 26 || len(o.ParentId) == 0) {
+ return NewAppError("Post.IsValid", "model.post.is_valid.parent_id.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if len(o.ParentId) == 26 && len(o.RootId) == 0 {
+ return NewAppError("Post.IsValid", "model.post.is_valid.root_parent.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if !(len(o.OriginalId) == 26 || len(o.OriginalId) == 0) {
+ return NewAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "", http.StatusBadRequest)
+ }
+
+ if utf8.RuneCountInString(o.Message) > maxPostSize {
+ return NewAppError("Post.IsValid", "model.post.is_valid.msg.app_error", nil, "id="+o.Id, http.StatusBadRequest)
+ }
+
+ if utf8.RuneCountInString(o.Hashtags) > POST_HASHTAGS_MAX_RUNES {
+ return NewAppError("Post.IsValid", "model.post.is_valid.hashtags.app_error", nil, "id="+o.Id, http.StatusBadRequest)
+ }
+
+ switch o.Type {
+ case
+ POST_DEFAULT,
+ POST_JOIN_LEAVE,
+ POST_AUTO_RESPONDER,
+ POST_ADD_REMOVE,
+ POST_JOIN_CHANNEL,
+ POST_LEAVE_CHANNEL,
+ POST_JOIN_TEAM,
+ POST_LEAVE_TEAM,
+ POST_ADD_TO_CHANNEL,
+ POST_REMOVE_FROM_CHANNEL,
+ POST_MOVE_CHANNEL,
+ POST_ADD_TO_TEAM,
+ POST_REMOVE_FROM_TEAM,
+ POST_SLACK_ATTACHMENT,
+ POST_HEADER_CHANGE,
+ POST_PURPOSE_CHANGE,
+ POST_DISPLAYNAME_CHANGE,
+ POST_CONVERT_CHANNEL,
+ POST_CHANNEL_DELETED,
+ POST_CHANGE_CHANNEL_PRIVACY:
+ default:
+ if !strings.HasPrefix(o.Type, POST_CUSTOM_TYPE_PREFIX) {
+ return NewAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type, http.StatusBadRequest)
+ }
+ }
+
+ if utf8.RuneCountInString(ArrayToJson(o.Filenames)) > POST_FILENAMES_MAX_RUNES {
+ return NewAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id, http.StatusBadRequest)
+ }
+
+ if utf8.RuneCountInString(ArrayToJson(o.FileIds)) > POST_FILEIDS_MAX_RUNES {
+ return NewAppError("Post.IsValid", "model.post.is_valid.file_ids.app_error", nil, "id="+o.Id, http.StatusBadRequest)
+ }
+
+ if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > POST_PROPS_MAX_RUNES {
+ return NewAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id, http.StatusBadRequest)
+ }
+
+ return nil
+}
+
+func (o *Post) SanitizeProps() {
+ membersToSanitize := []string{
+ PROPS_ADD_CHANNEL_MEMBER,
+ }
+
+ for _, member := range membersToSanitize {
+ if _, ok := o.Props[member]; ok {
+ delete(o.Props, member)
+ }
+ }
+}
+
+func (o *Post) PreSave() {
+ if o.Id == "" {
+ o.Id = NewId()
+ }
+
+ o.OriginalId = ""
+
+ if o.CreateAt == 0 {
+ o.CreateAt = GetMillis()
+ }
+
+ o.UpdateAt = o.CreateAt
+ o.PreCommit()
+}
+
+func (o *Post) PreCommit() {
+ if o.Props == nil {
+ o.Props = make(map[string]interface{})
+ }
+
+ if o.Filenames == nil {
+ o.Filenames = []string{}
+ }
+
+ if o.FileIds == nil {
+ o.FileIds = []string{}
+ }
+
+ o.GenerateActionIds()
+}
+
+func (o *Post) MakeNonNil() {
+ if o.Props == nil {
+ o.Props = make(map[string]interface{})
+ }
+}
+
+func (o *Post) AddProp(key string, value interface{}) {
+
+ o.MakeNonNil()
+
+ o.Props[key] = value
+}
+
+func (o *Post) IsSystemMessage() bool {
+ return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
+}
+
+func (p *Post) Patch(patch *PostPatch) {
+ if patch.IsPinned != nil {
+ p.IsPinned = *patch.IsPinned
+ }
+
+ if patch.Message != nil {
+ p.Message = *patch.Message
+ }
+
+ if patch.Props != nil {
+ p.Props = *patch.Props
+ }
+
+ if patch.FileIds != nil {
+ p.FileIds = *patch.FileIds
+ }
+
+ if patch.HasReactions != nil {
+ p.HasReactions = *patch.HasReactions
+ }
+}
+
+func (o *PostPatch) ToJson() string {
+ b, err := json.Marshal(o)
+ if err != nil {
+ return ""
+ }
+
+ return string(b)
+}
+
+func PostPatchFromJson(data io.Reader) *PostPatch {
+ decoder := json.NewDecoder(data)
+ var post PostPatch
+ err := decoder.Decode(&post)
+ if err != nil {
+ return nil
+ }
+
+ return &post
+}
+
+func (o *SearchParameter) SearchParameterToJson() string {
+ b, err := json.Marshal(o)
+ if err != nil {
+ return ""
+ }
+
+ return string(b)
+}
+
+func SearchParameterFromJson(data io.Reader) *SearchParameter {
+ decoder := json.NewDecoder(data)
+ var searchParam SearchParameter
+ err := decoder.Decode(&searchParam)
+ if err != nil {
+ return nil
+ }
+
+ return &searchParam
+}
+
+func (o *Post) ChannelMentions() []string {
+ return ChannelMentions(o.Message)
+}
+
+func (r *PostActionIntegrationRequest) ToJson() string {
+ b, _ := json.Marshal(r)
+ return string(b)
+}
+
+func PostActionIntegrationRequesteFromJson(data io.Reader) *PostActionIntegrationRequest {
+ var o *PostActionIntegrationRequest
+ err := json.NewDecoder(data).Decode(&o)
+ if err != nil {
+ return nil
+ }
+ return o
+}
+
+func (r *PostActionIntegrationResponse) ToJson() string {
+ b, _ := json.Marshal(r)
+ return string(b)
+}
+
+func PostActionIntegrationResponseFromJson(data io.Reader) *PostActionIntegrationResponse {
+ var o *PostActionIntegrationResponse
+ err := json.NewDecoder(data).Decode(&o)
+ if err != nil {
+ return nil
+ }
+ return o
+}
+
+func (o *Post) Attachments() []*SlackAttachment {
+ if attachments, ok := o.Props["attachments"].([]*SlackAttachment); ok {
+ return attachments
+ }
+ var ret []*SlackAttachment
+ if attachments, ok := o.Props["attachments"].([]interface{}); ok {
+ for _, attachment := range attachments {
+ if enc, err := json.Marshal(attachment); err == nil {
+ var decoded SlackAttachment
+ if json.Unmarshal(enc, &decoded) == nil {
+ ret = append(ret, &decoded)
+ }
+ }
+ }
+ }
+ return ret
+}
+
+func (o *Post) StripActionIntegrations() {
+ attachments := o.Attachments()
+ if o.Props["attachments"] != nil {
+ o.Props["attachments"] = attachments
+ }
+ for _, attachment := range attachments {
+ for _, action := range attachment.Actions {
+ action.Integration = nil
+ }
+ }
+}
+
+func (o *Post) GetAction(id string) *PostAction {
+ for _, attachment := range o.Attachments() {
+ for _, action := range attachment.Actions {
+ if action.Id == id {
+ return action
+ }
+ }
+ }
+ return nil
+}
+
+func (o *Post) GenerateActionIds() {
+ if o.Props["attachments"] != nil {
+ o.Props["attachments"] = o.Attachments()
+ }
+ if attachments, ok := o.Props["attachments"].([]*SlackAttachment); ok {
+ for _, attachment := range attachments {
+ for _, action := range attachment.Actions {
+ if action.Id == "" {
+ action.Id = NewId()
+ }
+ }
+ }
+ }
+}
+
+var markdownDestinationEscaper = strings.NewReplacer(
+ `\`, `\\`,
+ `<`, `\<`,
+ `>`, `\>`,
+ `(`, `\(`,
+ `)`, `\)`,
+)
+
+// WithRewrittenImageURLs returns a new shallow copy of the post where the message has been
+// rewritten via RewriteImageURLs.
+func (o *Post) WithRewrittenImageURLs(f func(string) string) *Post {
+ copy := *o
+ copy.Message = RewriteImageURLs(o.Message, f)
+ if copy.MessageSource == "" && copy.Message != o.Message {
+ copy.MessageSource = o.Message
+ }
+ return &copy
+}
+
+func (o *PostEphemeral) ToUnsanitizedJson() string {
+ b, _ := json.Marshal(o)
+ return string(b)
+}
+
+func DoPostActionRequestFromJson(data io.Reader) *DoPostActionRequest {
+ var o *DoPostActionRequest
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
+
+// RewriteImageURLs takes a message and returns a copy that has all of the image URLs replaced
+// according to the function f. For each image URL, f will be invoked, and the resulting markdown
+// will contain the URL returned by that invocation instead.
+//
+// Image URLs are destination URLs used in inline images or reference definitions that are used
+// anywhere in the input markdown as an image.
+func RewriteImageURLs(message string, f func(string) string) string {
+ if !strings.Contains(message, "![") {
+ return message
+ }
+
+ var ranges []markdown.Range
+
+ markdown.Inspect(message, func(blockOrInline interface{}) bool {
+ switch v := blockOrInline.(type) {
+ case *markdown.ReferenceImage:
+ ranges = append(ranges, v.ReferenceDefinition.RawDestination)
+ case *markdown.InlineImage:
+ ranges = append(ranges, v.RawDestination)
+ default:
+ return true
+ }
+ return true
+ })
+
+ if ranges == nil {
+ return message
+ }
+
+ sort.Slice(ranges, func(i, j int) bool {
+ return ranges[i].Position < ranges[j].Position
+ })
+
+ copyRanges := make([]markdown.Range, 0, len(ranges))
+ urls := make([]string, 0, len(ranges))
+ resultLength := len(message)
+
+ start := 0
+ for i, r := range ranges {
+ switch {
+ case i == 0:
+ case r.Position != ranges[i-1].Position:
+ start = ranges[i-1].End
+ default:
+ continue
+ }
+ original := message[r.Position:r.End]
+ replacement := markdownDestinationEscaper.Replace(f(markdown.Unescape(original)))
+ resultLength += len(replacement) - len(original)
+ copyRanges = append(copyRanges, markdown.Range{Position: start, End: r.Position})
+ urls = append(urls, replacement)
+ }
+
+ result := make([]byte, resultLength)
+
+ offset := 0
+ for i, r := range copyRanges {
+ offset += copy(result[offset:], message[r.Position:r.End])
+ offset += copy(result[offset:], urls[i])
+ }
+ copy(result[offset:], message[ranges[len(ranges)-1].End:])
+
+ return string(result)
+}
diff --git a/vendor/github.com/mattermost/platform/model/post_list.go b/vendor/github.com/mattermost/mattermost-server/model/post_list.go
index 018f7d14..27c22e7b 100644
--- a/vendor/github.com/mattermost/platform/model/post_list.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/post_list.go
@@ -21,6 +21,15 @@ func NewPostList() *PostList {
}
}
+func (o *PostList) WithRewrittenImageURLs(f func(string) string) *PostList {
+ copy := *o
+ copy.Posts = make(map[string]*Post)
+ for id, post := range o.Posts {
+ copy.Posts[id] = post.WithRewrittenImageURLs(f)
+ }
+ return &copy
+}
+
func (o *PostList) StripActionIntegrations() {
posts := o.Posts
o.Posts = make(map[string]*Post)
@@ -123,12 +132,7 @@ func (o *PostList) IsChannelId(channelId string) bool {
}
func PostListFromJson(data io.Reader) *PostList {
- decoder := json.NewDecoder(data)
- var o PostList
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *PostList
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/post_search_results.go b/vendor/github.com/mattermost/mattermost-server/model/post_search_results.go
new file mode 100644
index 00000000..2317f183
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/post_search_results.go
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type PostSearchMatches map[string][]string
+
+type PostSearchResults struct {
+ *PostList
+ Matches PostSearchMatches `json:"matches"`
+}
+
+func MakePostSearchResults(posts *PostList, matches PostSearchMatches) *PostSearchResults {
+ return &PostSearchResults{
+ posts,
+ matches,
+ }
+}
+
+func (o *PostSearchResults) ToJson() string {
+ copy := *o
+ copy.PostList.StripActionIntegrations()
+ b, err := json.Marshal(&copy)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+func PostSearchResultsFromJson(data io.Reader) *PostSearchResults {
+ var o *PostSearchResults
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
diff --git a/vendor/github.com/mattermost/platform/model/preference.go b/vendor/github.com/mattermost/mattermost-server/model/preference.go
index 5d462de8..6f13c38e 100644
--- a/vendor/github.com/mattermost/platform/model/preference.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/preference.go
@@ -18,9 +18,14 @@ const (
PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings"
PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post"
PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel"
+ PREFERENCE_CATEGORY_SIDEBAR_SETTINGS = "sidebar_settings"
PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings"
+ PREFERENCE_NAME_CHANNEL_DISPLAY_MODE = "channel_display_mode"
PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews"
+ PREFERENCE_NAME_MESSAGE_DISPLAY = "message_display"
+ PREFERENCE_NAME_NAME_FORMAT = "name_format"
+ PREFERENCE_NAME_USE_MILITARY_TIME = "use_military_time"
PREFERENCE_CATEGORY_THEME = "theme"
// the name for theme props is the team id
@@ -47,23 +52,14 @@ type Preference struct {
}
func (o *Preference) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func PreferenceFromJson(data io.Reader) *Preference {
- decoder := json.NewDecoder(data)
- var o Preference
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Preference
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *Preference) IsValid() *AppError {
diff --git a/vendor/github.com/mattermost/platform/model/preferences.go b/vendor/github.com/mattermost/mattermost-server/model/preferences.go
index b982e309..172e1aa8 100644
--- a/vendor/github.com/mattermost/platform/model/preferences.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/preferences.go
@@ -11,12 +11,8 @@ import (
type Preferences []Preference
func (o *Preferences) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func PreferencesFromJson(data io.Reader) (Preferences, error) {
diff --git a/vendor/github.com/mattermost/platform/model/push_notification.go b/vendor/github.com/mattermost/mattermost-server/model/push_notification.go
index 69719e74..5268a98f 100644
--- a/vendor/github.com/mattermost/platform/model/push_notification.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/push_notification.go
@@ -17,6 +17,7 @@ const (
PUSH_TYPE_MESSAGE = "message"
PUSH_TYPE_CLEAR = "clear"
+ PUSH_MESSAGE_V2 = "v2"
// The category is set to handle a set of interactive Actions
// with the push notifications
@@ -44,15 +45,12 @@ type PushNotification struct {
OverrideUsername string `json:"override_username"`
OverrideIconUrl string `json:"override_icon_url"`
FromWebhook string `json:"from_webhook"`
+ Version string `json:"version"`
}
func (me *PushNotification) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
@@ -66,12 +64,7 @@ func (me *PushNotification) SetDeviceIdAndPlatform(deviceId string) {
}
func PushNotificationFromJson(data io.Reader) *PushNotification {
- decoder := json.NewDecoder(data)
- var me PushNotification
- err := decoder.Decode(&me)
- if err == nil {
- return &me
- } else {
- return nil
- }
+ var me *PushNotification
+ json.NewDecoder(data).Decode(&me)
+ return me
}
diff --git a/vendor/github.com/mattermost/platform/model/push_response.go b/vendor/github.com/mattermost/mattermost-server/model/push_response.go
index 095986f3..1434a2b1 100644
--- a/vendor/github.com/mattermost/platform/model/push_response.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/push_response.go
@@ -38,11 +38,8 @@ func NewErrorPushResponse(message string) PushResponse {
}
func (me *PushResponse) ToJson() string {
- if b, err := json.Marshal(me); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func PushResponseFromJson(data io.Reader) PushResponse {
diff --git a/vendor/github.com/mattermost/platform/model/reaction.go b/vendor/github.com/mattermost/mattermost-server/model/reaction.go
index 4b72dd44..c1b9c499 100644
--- a/vendor/github.com/mattermost/platform/model/reaction.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/reaction.go
@@ -18,11 +18,8 @@ type Reaction struct {
}
func (o *Reaction) ToJson() string {
- if b, err := json.Marshal(o); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ReactionFromJson(data io.Reader) *Reaction {
@@ -36,11 +33,8 @@ func ReactionFromJson(data io.Reader) *Reaction {
}
func ReactionsToJson(o []*Reaction) string {
- if b, err := json.Marshal(o); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func ReactionsFromJson(data io.Reader) []*Reaction {
@@ -64,7 +58,7 @@ func (o *Reaction) IsValid() *AppError {
validName := regexp.MustCompile(`^[a-zA-Z0-9\-\+_]+$`)
- if len(o.EmojiName) == 0 || len(o.EmojiName) > 64 || !validName.MatchString(o.EmojiName) {
+ if len(o.EmojiName) == 0 || len(o.EmojiName) > EMOJI_NAME_MAX_LENGTH || !validName.MatchString(o.EmojiName) {
return NewAppError("Reaction.IsValid", "model.reaction.is_valid.emoji_name.app_error", nil, "emoji_name="+o.EmojiName, http.StatusBadRequest)
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/role.go b/vendor/github.com/mattermost/mattermost-server/model/role.go
new file mode 100644
index 00000000..27b32ed6
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/role.go
@@ -0,0 +1,363 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+ "strings"
+)
+
+const (
+ SYSTEM_USER_ROLE_ID = "system_user"
+ SYSTEM_ADMIN_ROLE_ID = "system_admin"
+ SYSTEM_POST_ALL_ROLE_ID = "system_post_all"
+ SYSTEM_POST_ALL_PUBLIC_ROLE_ID = "system_post_all_public"
+ SYSTEM_USER_ACCESS_TOKEN_ROLE_ID = "system_user_access_token"
+
+ TEAM_USER_ROLE_ID = "team_user"
+ TEAM_ADMIN_ROLE_ID = "team_admin"
+ TEAM_POST_ALL_ROLE_ID = "team_post_all"
+ TEAM_POST_ALL_PUBLIC_ROLE_ID = "team_post_all_public"
+
+ CHANNEL_USER_ROLE_ID = "channel_user"
+ CHANNEL_ADMIN_ROLE_ID = "channel_admin"
+
+ ROLE_NAME_MAX_LENGTH = 64
+ ROLE_DISPLAY_NAME_MAX_LENGTH = 128
+ ROLE_DESCRIPTION_MAX_LENGTH = 1024
+)
+
+type Role struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ DisplayName string `json:"display_name"`
+ Description string `json:"description"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ Permissions []string `json:"permissions"`
+ SchemeManaged bool `json:"scheme_managed"`
+ BuiltIn bool `json:"built_in"`
+}
+
+type RolePatch struct {
+ Permissions *[]string `json:"permissions"`
+}
+
+func (role *Role) ToJson() string {
+ b, _ := json.Marshal(role)
+ return string(b)
+}
+
+func RoleFromJson(data io.Reader) *Role {
+ var role *Role
+ json.NewDecoder(data).Decode(&role)
+ return role
+}
+
+func RoleListToJson(r []*Role) string {
+ b, _ := json.Marshal(r)
+ return string(b)
+}
+
+func RoleListFromJson(data io.Reader) []*Role {
+ var roles []*Role
+ json.NewDecoder(data).Decode(&roles)
+ return roles
+}
+
+func (r *RolePatch) ToJson() string {
+ b, _ := json.Marshal(r)
+ return string(b)
+}
+
+func RolePatchFromJson(data io.Reader) *RolePatch {
+ var rolePatch *RolePatch
+ json.NewDecoder(data).Decode(&rolePatch)
+ return rolePatch
+}
+
+func (o *Role) Patch(patch *RolePatch) {
+ if patch.Permissions != nil {
+ o.Permissions = *patch.Permissions
+ }
+}
+
+// Returns an array of permissions that are in either role.Permissions
+// or patch.Permissions, but not both.
+func PermissionsChangedByPatch(role *Role, patch *RolePatch) []string {
+ var result []string
+
+ if patch.Permissions == nil {
+ return result
+ }
+
+ roleMap := make(map[string]bool)
+ patchMap := make(map[string]bool)
+
+ for _, permission := range role.Permissions {
+ roleMap[permission] = true
+ }
+
+ for _, permission := range *patch.Permissions {
+ patchMap[permission] = true
+ }
+
+ for _, permission := range role.Permissions {
+ if !patchMap[permission] {
+ result = append(result, permission)
+ }
+ }
+
+ for _, permission := range *patch.Permissions {
+ if !roleMap[permission] {
+ result = append(result, permission)
+ }
+ }
+
+ return result
+}
+
+func (role *Role) IsValid() bool {
+ if len(role.Id) != 26 {
+ return false
+ }
+
+ return role.IsValidWithoutId()
+}
+
+func (role *Role) IsValidWithoutId() bool {
+ if !IsValidRoleName(role.Name) {
+ return false
+ }
+
+ if len(role.DisplayName) == 0 || len(role.DisplayName) > ROLE_DISPLAY_NAME_MAX_LENGTH {
+ return false
+ }
+
+ if len(role.Description) > ROLE_DESCRIPTION_MAX_LENGTH {
+ return false
+ }
+
+ for _, permission := range role.Permissions {
+ permissionValidated := false
+ for _, p := range ALL_PERMISSIONS {
+ if permission == p.Id {
+ permissionValidated = true
+ break
+ }
+ }
+
+ if !permissionValidated {
+ return false
+ }
+ }
+
+ return true
+}
+
+func IsValidRoleName(roleName string) bool {
+ if len(roleName) <= 0 || len(roleName) > ROLE_NAME_MAX_LENGTH {
+ return false
+ }
+
+ if strings.TrimLeft(roleName, "abcdefghijklmnopqrstuvwxyz0123456789_") != "" {
+ return false
+ }
+
+ return true
+}
+
+func MakeDefaultRoles() map[string]*Role {
+ roles := make(map[string]*Role)
+
+ roles[CHANNEL_USER_ROLE_ID] = &Role{
+ Name: "channel_user",
+ DisplayName: "authentication.roles.channel_user.name",
+ Description: "authentication.roles.channel_user.description",
+ Permissions: []string{
+ PERMISSION_READ_CHANNEL.Id,
+ PERMISSION_ADD_REACTION.Id,
+ PERMISSION_REMOVE_REACTION.Id,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
+ PERMISSION_UPLOAD_FILE.Id,
+ PERMISSION_GET_PUBLIC_LINK.Id,
+ PERMISSION_CREATE_POST.Id,
+ PERMISSION_USE_SLASH_COMMANDS.Id,
+ },
+ SchemeManaged: true,
+ BuiltIn: true,
+ }
+
+ roles[CHANNEL_ADMIN_ROLE_ID] = &Role{
+ Name: "channel_admin",
+ DisplayName: "authentication.roles.channel_admin.name",
+ Description: "authentication.roles.channel_admin.description",
+ Permissions: []string{
+ PERMISSION_MANAGE_CHANNEL_ROLES.Id,
+ },
+ SchemeManaged: true,
+ BuiltIn: true,
+ }
+
+ roles[TEAM_USER_ROLE_ID] = &Role{
+ Name: "team_user",
+ DisplayName: "authentication.roles.team_user.name",
+ Description: "authentication.roles.team_user.description",
+ Permissions: []string{
+ PERMISSION_LIST_TEAM_CHANNELS.Id,
+ PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
+ PERMISSION_READ_PUBLIC_CHANNEL.Id,
+ PERMISSION_VIEW_TEAM.Id,
+ },
+ SchemeManaged: true,
+ BuiltIn: true,
+ }
+
+ roles[TEAM_POST_ALL_ROLE_ID] = &Role{
+ Name: "team_post_all",
+ DisplayName: "authentication.roles.team_post_all.name",
+ Description: "authentication.roles.team_post_all.description",
+ Permissions: []string{
+ PERMISSION_CREATE_POST.Id,
+ },
+ SchemeManaged: false,
+ BuiltIn: true,
+ }
+
+ roles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
+ Name: "team_post_all_public",
+ DisplayName: "authentication.roles.team_post_all_public.name",
+ Description: "authentication.roles.team_post_all_public.description",
+ Permissions: []string{
+ PERMISSION_CREATE_POST_PUBLIC.Id,
+ },
+ SchemeManaged: false,
+ BuiltIn: true,
+ }
+
+ roles[TEAM_ADMIN_ROLE_ID] = &Role{
+ Name: "team_admin",
+ DisplayName: "authentication.roles.team_admin.name",
+ Description: "authentication.roles.team_admin.description",
+ Permissions: []string{
+ PERMISSION_REMOVE_USER_FROM_TEAM.Id,
+ PERMISSION_MANAGE_TEAM.Id,
+ PERMISSION_IMPORT_TEAM.Id,
+ PERMISSION_MANAGE_TEAM_ROLES.Id,
+ PERMISSION_MANAGE_CHANNEL_ROLES.Id,
+ PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
+ PERMISSION_MANAGE_SLASH_COMMANDS.Id,
+ PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
+ PERMISSION_MANAGE_WEBHOOKS.Id,
+ },
+ SchemeManaged: true,
+ BuiltIn: true,
+ }
+
+ roles[SYSTEM_USER_ROLE_ID] = &Role{
+ Name: "system_user",
+ DisplayName: "authentication.roles.global_user.name",
+ Description: "authentication.roles.global_user.description",
+ Permissions: []string{
+ PERMISSION_CREATE_DIRECT_CHANNEL.Id,
+ PERMISSION_CREATE_GROUP_CHANNEL.Id,
+ PERMISSION_PERMANENT_DELETE_USER.Id,
+ },
+ SchemeManaged: true,
+ BuiltIn: true,
+ }
+
+ roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{
+ Name: "system_post_all",
+ DisplayName: "authentication.roles.system_post_all.name",
+ Description: "authentication.roles.system_post_all.description",
+ Permissions: []string{
+ PERMISSION_CREATE_POST.Id,
+ },
+ SchemeManaged: false,
+ BuiltIn: true,
+ }
+
+ roles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
+ Name: "system_post_all_public",
+ DisplayName: "authentication.roles.system_post_all_public.name",
+ Description: "authentication.roles.system_post_all_public.description",
+ Permissions: []string{
+ PERMISSION_CREATE_POST_PUBLIC.Id,
+ },
+ SchemeManaged: false,
+ BuiltIn: true,
+ }
+
+ roles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{
+ Name: "system_user_access_token",
+ DisplayName: "authentication.roles.system_user_access_token.name",
+ Description: "authentication.roles.system_user_access_token.description",
+ Permissions: []string{
+ PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
+ PERMISSION_READ_USER_ACCESS_TOKEN.Id,
+ PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
+ },
+ SchemeManaged: false,
+ BuiltIn: true,
+ }
+
+ roles[SYSTEM_ADMIN_ROLE_ID] = &Role{
+ Name: "system_admin",
+ DisplayName: "authentication.roles.global_admin.name",
+ Description: "authentication.roles.global_admin.description",
+ // System admins can do anything channel and team admins can do
+ // plus everything members of teams and channels can do to all teams
+ // and channels on the system
+ Permissions: append(
+ append(
+ append(
+ append(
+ []string{
+ PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
+ PERMISSION_MANAGE_SYSTEM.Id,
+ PERMISSION_MANAGE_ROLES.Id,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
+ PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
+ PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
+ PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
+ PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
+ PERMISSION_DELETE_PRIVATE_CHANNEL.Id,
+ PERMISSION_CREATE_PRIVATE_CHANNEL.Id,
+ PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
+ PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
+ PERMISSION_EDIT_OTHER_USERS.Id,
+ PERMISSION_EDIT_OTHERS_POSTS.Id,
+ PERMISSION_MANAGE_OAUTH.Id,
+ PERMISSION_INVITE_USER.Id,
+ PERMISSION_DELETE_POST.Id,
+ PERMISSION_DELETE_OTHERS_POSTS.Id,
+ PERMISSION_CREATE_TEAM.Id,
+ PERMISSION_ADD_USER_TO_TEAM.Id,
+ PERMISSION_LIST_USERS_WITHOUT_TEAM.Id,
+ PERMISSION_MANAGE_JOBS.Id,
+ PERMISSION_CREATE_POST_PUBLIC.Id,
+ PERMISSION_CREATE_POST_EPHEMERAL.Id,
+ PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
+ PERMISSION_READ_USER_ACCESS_TOKEN.Id,
+ PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
+ PERMISSION_REMOVE_OTHERS_REACTIONS.Id,
+ },
+ roles[TEAM_USER_ROLE_ID].Permissions...,
+ ),
+ roles[CHANNEL_USER_ROLE_ID].Permissions...,
+ ),
+ roles[TEAM_ADMIN_ROLE_ID].Permissions...,
+ ),
+ roles[CHANNEL_ADMIN_ROLE_ID].Permissions...,
+ ),
+ SchemeManaged: true,
+ BuiltIn: true,
+ }
+
+ return roles
+}
diff --git a/vendor/github.com/mattermost/platform/model/saml.go b/vendor/github.com/mattermost/mattermost-server/model/saml.go
index f5826a95..528ac45c 100644
--- a/vendor/github.com/mattermost/platform/model/saml.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/saml.go
@@ -11,9 +11,6 @@ import (
const (
USER_AUTH_SERVICE_SAML = "saml"
USER_AUTH_SERVICE_SAML_TEXT = "With SAML"
- SAML_IDP_CERTIFICATE = 1
- SAML_PRIVATE_KEY = 2
- SAML_PUBLIC_CERT = 3
)
type SamlAuthRequest struct {
@@ -29,21 +26,12 @@ type SamlCertificateStatus struct {
}
func (s *SamlCertificateStatus) ToJson() string {
- b, err := json.Marshal(s)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(s)
+ return string(b)
}
func SamlCertificateStatusFromJson(data io.Reader) *SamlCertificateStatus {
- decoder := json.NewDecoder(data)
- var status SamlCertificateStatus
- err := decoder.Decode(&status)
- if err == nil {
- return &status
- } else {
- return nil
- }
+ var status *SamlCertificateStatus
+ json.NewDecoder(data).Decode(&status)
+ return status
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/scheduled_task.go b/vendor/github.com/mattermost/mattermost-server/model/scheduled_task.go
new file mode 100644
index 00000000..f3529ded
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/scheduled_task.go
@@ -0,0 +1,77 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "fmt"
+ "time"
+)
+
+type TaskFunc func()
+
+type ScheduledTask struct {
+ Name string `json:"name"`
+ Interval time.Duration `json:"interval"`
+ Recurring bool `json:"recurring"`
+ function func()
+ cancel chan struct{}
+ cancelled chan struct{}
+}
+
+func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask {
+ return createTask(name, function, timeToExecution, false)
+}
+
+func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask {
+ return createTask(name, function, interval, true)
+}
+
+func createTask(name string, function TaskFunc, interval time.Duration, recurring bool) *ScheduledTask {
+ task := &ScheduledTask{
+ Name: name,
+ Interval: interval,
+ Recurring: recurring,
+ function: function,
+ cancel: make(chan struct{}),
+ cancelled: make(chan struct{}),
+ }
+
+ go func() {
+ defer close(task.cancelled)
+
+ ticker := time.NewTicker(interval)
+ defer func() {
+ ticker.Stop()
+ }()
+
+ for {
+ select {
+ case <-ticker.C:
+ function()
+ case <-task.cancel:
+ return
+ }
+
+ if !task.Recurring {
+ break
+ }
+ }
+ }()
+
+ return task
+}
+
+func (task *ScheduledTask) Cancel() {
+ close(task.cancel)
+ <-task.cancelled
+}
+
+func (task *ScheduledTask) String() string {
+ return fmt.Sprintf(
+ "%s\nInterval: %s\nRecurring: %t\n",
+ task.Name,
+ task.Interval.String(),
+ task.Recurring,
+ )
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/scheme.go b/vendor/github.com/mattermost/mattermost-server/model/scheme.go
new file mode 100644
index 00000000..0c38b560
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/scheme.go
@@ -0,0 +1,208 @@
+// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "regexp"
+)
+
+const (
+ SCHEME_DISPLAY_NAME_MAX_LENGTH = 128
+ SCHEME_NAME_MAX_LENGTH = 64
+ SCHEME_DESCRIPTION_MAX_LENGTH = 1024
+ SCHEME_SCOPE_TEAM = "team"
+ SCHEME_SCOPE_CHANNEL = "channel"
+)
+
+type Scheme struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ DisplayName string `json:"display_name"`
+ Description string `json:"description"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ Scope string `json:"scope"`
+ DefaultTeamAdminRole string `json:"default_team_admin_role"`
+ DefaultTeamUserRole string `json:"default_team_user_role"`
+ DefaultChannelAdminRole string `json:"default_channel_admin_role"`
+ DefaultChannelUserRole string `json:"default_channel_user_role"`
+}
+
+type SchemePatch struct {
+ Name *string `json:"name"`
+ DisplayName *string `json:"display_name"`
+ Description *string `json:"description"`
+}
+
+type SchemeIDPatch struct {
+ SchemeID *string `json:"scheme_id"`
+}
+
+// SchemeConveyor is used for importing and exporting a Scheme and its associated Roles.
+type SchemeConveyor struct {
+ Name string `json:"name"`
+ DisplayName string `json:"display_name"`
+ Description string `json:"description"`
+ Scope string `json:"scope"`
+ TeamAdmin string `json:"default_team_admin_role"`
+ TeamUser string `json:"default_team_user_role"`
+ ChannelAdmin string `json:"default_channel_admin_role"`
+ ChannelUser string `json:"default_channel_user_role"`
+ Roles []*Role `json:"roles"`
+}
+
+func (sc *SchemeConveyor) Scheme() *Scheme {
+ return &Scheme{
+ DisplayName: sc.DisplayName,
+ Name: sc.Name,
+ Description: sc.Description,
+ Scope: sc.Scope,
+ DefaultTeamAdminRole: sc.TeamAdmin,
+ DefaultTeamUserRole: sc.TeamUser,
+ DefaultChannelAdminRole: sc.ChannelAdmin,
+ DefaultChannelUserRole: sc.ChannelUser,
+ }
+}
+
+type SchemeRoles struct {
+ SchemeAdmin bool `json:"scheme_admin"`
+ SchemeUser bool `json:"scheme_user"`
+}
+
+func (scheme *Scheme) ToJson() string {
+ b, _ := json.Marshal(scheme)
+ return string(b)
+}
+
+func SchemeFromJson(data io.Reader) *Scheme {
+ var scheme *Scheme
+ json.NewDecoder(data).Decode(&scheme)
+ return scheme
+}
+
+func SchemesToJson(schemes []*Scheme) string {
+ b, _ := json.Marshal(schemes)
+ return string(b)
+}
+
+func SchemesFromJson(data io.Reader) []*Scheme {
+ var schemes []*Scheme
+ if err := json.NewDecoder(data).Decode(&schemes); err == nil {
+ return schemes
+ } else {
+ return nil
+ }
+}
+
+func (scheme *Scheme) IsValid() bool {
+ if len(scheme.Id) != 26 {
+ return false
+ }
+
+ return scheme.IsValidForCreate()
+}
+
+func (scheme *Scheme) IsValidForCreate() bool {
+ if len(scheme.DisplayName) == 0 || len(scheme.DisplayName) > SCHEME_DISPLAY_NAME_MAX_LENGTH {
+ return false
+ }
+
+ if !IsValidSchemeName(scheme.Name) {
+ return false
+ }
+
+ if len(scheme.Description) > SCHEME_DESCRIPTION_MAX_LENGTH {
+ return false
+ }
+
+ switch scheme.Scope {
+ case SCHEME_SCOPE_TEAM, SCHEME_SCOPE_CHANNEL:
+ default:
+ return false
+ }
+
+ if !IsValidRoleName(scheme.DefaultChannelAdminRole) {
+ return false
+ }
+
+ if !IsValidRoleName(scheme.DefaultChannelUserRole) {
+ return false
+ }
+
+ if scheme.Scope == SCHEME_SCOPE_TEAM {
+ if !IsValidRoleName(scheme.DefaultTeamAdminRole) {
+ return false
+ }
+
+ if !IsValidRoleName(scheme.DefaultTeamUserRole) {
+ return false
+ }
+ }
+
+ if scheme.Scope == SCHEME_SCOPE_CHANNEL {
+ if len(scheme.DefaultTeamAdminRole) != 0 {
+ return false
+ }
+
+ if len(scheme.DefaultTeamUserRole) != 0 {
+ return false
+ }
+ }
+
+ return true
+}
+
+func (scheme *Scheme) Patch(patch *SchemePatch) {
+ if patch.DisplayName != nil {
+ scheme.DisplayName = *patch.DisplayName
+ }
+ if patch.Name != nil {
+ scheme.Name = *patch.Name
+ }
+ if patch.Description != nil {
+ scheme.Description = *patch.Description
+ }
+}
+
+func (patch *SchemePatch) ToJson() string {
+ b, _ := json.Marshal(patch)
+ return string(b)
+}
+
+func SchemePatchFromJson(data io.Reader) *SchemePatch {
+ var patch *SchemePatch
+ json.NewDecoder(data).Decode(&patch)
+ return patch
+}
+
+func SchemeIDFromJson(data io.Reader) *string {
+ var p *SchemeIDPatch
+ json.NewDecoder(data).Decode(&p)
+ return p.SchemeID
+}
+
+func (p *SchemeIDPatch) ToJson() string {
+ b, _ := json.Marshal(p)
+ return string(b)
+}
+
+func IsValidSchemeName(name string) bool {
+ re := regexp.MustCompile(fmt.Sprintf("^[a-z0-9_]{2,%d}$", SCHEME_NAME_MAX_LENGTH))
+ return re.MatchString(name)
+}
+
+func (schemeRoles *SchemeRoles) ToJson() string {
+ b, _ := json.Marshal(schemeRoles)
+ return string(b)
+}
+
+func SchemeRolesFromJson(data io.Reader) *SchemeRoles {
+ var schemeRoles *SchemeRoles
+ json.NewDecoder(data).Decode(&schemeRoles)
+ return schemeRoles
+}
diff --git a/vendor/github.com/mattermost/platform/model/search_params.go b/vendor/github.com/mattermost/mattermost-server/model/search_params.go
index 2feea8da..65358066 100644
--- a/vendor/github.com/mattermost/platform/model/search_params.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/search_params.go
@@ -4,32 +4,64 @@
package model
import (
- "encoding/json"
"regexp"
"strings"
+ "time"
)
var searchTermPuncStart = regexp.MustCompile(`^[^\pL\d\s#"]+`)
var searchTermPuncEnd = regexp.MustCompile(`[^\pL\d\s*"]+$`)
type SearchParams struct {
- Terms string
- IsHashtag bool
- InChannels []string
- FromUsers []string
- OrTerms bool
+ Terms string
+ IsHashtag bool
+ InChannels []string
+ FromUsers []string
+ AfterDate string
+ BeforeDate string
+ OnDate string
+ OrTerms bool
+ IncludeDeletedChannels bool
+ TimeZoneOffset int
}
-func (o *SearchParams) ToJson() string {
- b, err := json.Marshal(o)
+// Returns the epoch timestamp of the start of the day specified by SearchParams.AfterDate
+func (p *SearchParams) GetAfterDateMillis() int64 {
+ date, err := time.Parse("2006-01-02", PadDateStringZeros(p.AfterDate))
if err != nil {
- return ""
- } else {
- return string(b)
+ date = time.Now()
}
+
+ // travel forward 1 day
+ oneDay := time.Hour * 24
+ afterDate := date.Add(oneDay)
+ return GetStartOfDayMillis(afterDate, p.TimeZoneOffset)
+}
+
+// Returns the epoch timestamp of the end of the day specified by SearchParams.BeforeDate
+func (p *SearchParams) GetBeforeDateMillis() int64 {
+ date, err := time.Parse("2006-01-02", PadDateStringZeros(p.BeforeDate))
+ if err != nil {
+ return 0
+ }
+
+ // travel back 1 day
+ oneDay := time.Hour * -24
+ beforeDate := date.Add(oneDay)
+ return GetEndOfDayMillis(beforeDate, p.TimeZoneOffset)
+}
+
+// Returns the epoch timestamps of the start and end of the day specified by SearchParams.OnDate
+func (p *SearchParams) GetOnDateMillis() (int64, int64) {
+ date, err := time.Parse("2006-01-02", PadDateStringZeros(p.OnDate))
+ if err != nil {
+ return 0, 0
+ }
+
+ return GetStartOfDayMillis(date, p.TimeZoneOffset), GetEndOfDayMillis(date, p.TimeZoneOffset)
}
-var searchFlags = [...]string{"from", "channel", "in"}
+var searchFlags = [...]string{"from", "channel", "in", "before", "after", "on"}
func splitWords(text string) []string {
words := []string{}
@@ -110,7 +142,7 @@ func parseSearchFlags(input []string) ([]string, [][2]string) {
return words, flags
}
-func ParseSearchParams(text string) []*SearchParams {
+func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
words, flags := parseSearchFlags(splitWords(text))
hashtagTermList := []string{}
@@ -129,6 +161,9 @@ func ParseSearchParams(text string) []*SearchParams {
inChannels := []string{}
fromUsers := []string{}
+ afterDate := ""
+ beforeDate := ""
+ onDate := ""
for _, flagPair := range flags {
flag := flagPair[0]
@@ -138,6 +173,12 @@ func ParseSearchParams(text string) []*SearchParams {
inChannels = append(inChannels, value)
} else if flag == "from" {
fromUsers = append(fromUsers, value)
+ } else if flag == "after" {
+ afterDate = value
+ } else if flag == "before" {
+ beforeDate = value
+ } else if flag == "on" {
+ onDate = value
}
}
@@ -145,29 +186,41 @@ func ParseSearchParams(text string) []*SearchParams {
if len(plainTerms) > 0 {
paramsList = append(paramsList, &SearchParams{
- Terms: plainTerms,
- IsHashtag: false,
- InChannels: inChannels,
- FromUsers: fromUsers,
+ Terms: plainTerms,
+ IsHashtag: false,
+ InChannels: inChannels,
+ FromUsers: fromUsers,
+ AfterDate: afterDate,
+ BeforeDate: beforeDate,
+ OnDate: onDate,
+ TimeZoneOffset: timeZoneOffset,
})
}
if len(hashtagTerms) > 0 {
paramsList = append(paramsList, &SearchParams{
- Terms: hashtagTerms,
- IsHashtag: true,
- InChannels: inChannels,
- FromUsers: fromUsers,
+ Terms: hashtagTerms,
+ IsHashtag: true,
+ InChannels: inChannels,
+ FromUsers: fromUsers,
+ AfterDate: afterDate,
+ BeforeDate: beforeDate,
+ OnDate: onDate,
+ TimeZoneOffset: timeZoneOffset,
})
}
// special case for when no terms are specified but we still have a filter
- if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0) {
+ if len(plainTerms) == 0 && len(hashtagTerms) == 0 && (len(inChannels) != 0 || len(fromUsers) != 0 || len(afterDate) != 0 || len(beforeDate) != 0 || len(onDate) != 0) {
paramsList = append(paramsList, &SearchParams{
- Terms: "",
- IsHashtag: false,
- InChannels: inChannels,
- FromUsers: fromUsers,
+ Terms: "",
+ IsHashtag: false,
+ InChannels: inChannels,
+ FromUsers: fromUsers,
+ AfterDate: afterDate,
+ BeforeDate: beforeDate,
+ OnDate: onDate,
+ TimeZoneOffset: timeZoneOffset,
})
}
diff --git a/vendor/github.com/mattermost/platform/model/security_bulletin.go b/vendor/github.com/mattermost/mattermost-server/model/security_bulletin.go
index b8c1dc48..958b9c9e 100644
--- a/vendor/github.com/mattermost/platform/model/security_bulletin.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/security_bulletin.go
@@ -16,23 +16,14 @@ type SecurityBulletin struct {
type SecurityBulletins []SecurityBulletin
func (me *SecurityBulletin) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func SecurityBulletinFromJson(data io.Reader) *SecurityBulletin {
- decoder := json.NewDecoder(data)
- var o SecurityBulletin
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *SecurityBulletin
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (me SecurityBulletins) ToJson() string {
@@ -44,12 +35,7 @@ func (me SecurityBulletins) ToJson() string {
}
func SecurityBulletinsFromJson(data io.Reader) SecurityBulletins {
- decoder := json.NewDecoder(data)
var o SecurityBulletins
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/session.go b/vendor/github.com/mattermost/mattermost-server/model/session.go
index 704af067..d59e9b18 100644
--- a/vendor/github.com/mattermost/platform/model/session.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/session.go
@@ -38,28 +38,32 @@ type Session struct {
}
func (me *Session) DeepCopy() *Session {
- copy := *me
- return &copy
+ copySession := *me
+
+ if me.Props != nil {
+ copySession.Props = CopyStringMap(me.Props)
+ }
+
+ if me.TeamMembers != nil {
+ copySession.TeamMembers = make([]*TeamMember, len(me.TeamMembers))
+ for index, tm := range me.TeamMembers {
+ copySession.TeamMembers[index] = new(TeamMember)
+ *copySession.TeamMembers[index] = *tm
+ }
+ }
+
+ return &copySession
}
func (me *Session) ToJson() string {
- b, err := json.Marshal(me)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(me)
+ return string(b)
}
func SessionFromJson(data io.Reader) *Session {
- decoder := json.NewDecoder(data)
- var me Session
- err := decoder.Decode(&me)
- if err == nil {
- return &me
- } else {
- return nil
- }
+ var me *Session
+ json.NewDecoder(data).Decode(&me)
+ return me
}
func (me *Session) PreSave() {
@@ -131,6 +135,20 @@ func (me *Session) GetUserRoles() []string {
return strings.Fields(me.Roles)
}
+func (me *Session) GenerateCSRF() string {
+ token := NewId()
+ me.AddProp("csrf", token)
+ return token
+}
+
+func (me *Session) GetCSRF() string {
+ if me.Props == nil {
+ return ""
+ }
+
+ return me.Props["csrf"]
+}
+
func SessionsToJson(o []*Session) string {
if b, err := json.Marshal(o); err != nil {
return "[]"
@@ -140,12 +158,7 @@ func SessionsToJson(o []*Session) string {
}
func SessionsFromJson(data io.Reader) []*Session {
- decoder := json.NewDecoder(data)
var o []*Session
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/slack_attachment.go b/vendor/github.com/mattermost/mattermost-server/model/slack_attachment.go
index 197d3f0f..827bf35b 100644
--- a/vendor/github.com/mattermost/platform/model/slack_attachment.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/slack_attachment.go
@@ -5,8 +5,11 @@ package model
import (
"fmt"
+ "regexp"
)
+var linkWithTextRegex = regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
+
type SlackAttachment struct {
Id int64 `json:"id"`
Fallback string `json:"fallback"`
@@ -57,3 +60,25 @@ func StringifySlackFieldValue(a []*SlackAttachment) []*SlackAttachment {
}
return nonNilAttachments
}
+
+// This method only parses and processes the attachments,
+// all else should be set in the post which is passed
+func ParseSlackAttachment(post *Post, attachments []*SlackAttachment) {
+ post.Type = POST_SLACK_ATTACHMENT
+
+ for _, attachment := range attachments {
+ attachment.Text = ParseSlackLinksToMarkdown(attachment.Text)
+ attachment.Pretext = ParseSlackLinksToMarkdown(attachment.Pretext)
+
+ for _, field := range attachment.Fields {
+ if value, ok := field.Value.(string); ok {
+ field.Value = ParseSlackLinksToMarkdown(value)
+ }
+ }
+ }
+ post.AddProp("attachments", attachments)
+}
+
+func ParseSlackLinksToMarkdown(text string) string {
+ return linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})")
+}
diff --git a/vendor/github.com/mattermost/platform/model/status.go b/vendor/github.com/mattermost/mattermost-server/model/status.go
index 6da6161e..7888c60a 100644
--- a/vendor/github.com/mattermost/platform/model/status.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/status.go
@@ -9,6 +9,7 @@ import (
)
const (
+ STATUS_OUT_OF_OFFICE = "ooo"
STATUS_OFFLINE = "offline"
STATUS_AWAY = "away"
STATUS_DND = "dnd"
@@ -23,47 +24,48 @@ type Status struct {
Status string `json:"status"`
Manual bool `json:"manual"`
LastActivityAt int64 `json:"last_activity_at"`
- ActiveChannel string `json:"-" db:"-"`
+ ActiveChannel string `json:"active_channel,omitempty" db:"-"`
}
func (o *Status) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ tempChannelId := o.ActiveChannel
+ o.ActiveChannel = ""
+ b, _ := json.Marshal(o)
+ o.ActiveChannel = tempChannelId
+ return string(b)
+}
+
+func (o *Status) ToClusterJson() string {
+ b, _ := json.Marshal(o)
+ return string(b)
}
func StatusFromJson(data io.Reader) *Status {
- decoder := json.NewDecoder(data)
- var o Status
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Status
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func StatusListToJson(u []*Status) string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
+ activeChannels := make([]string, len(u))
+ for index, s := range u {
+ activeChannels[index] = s.ActiveChannel
+ s.ActiveChannel = ""
}
+
+ b, _ := json.Marshal(u)
+
+ for index, s := range u {
+ s.ActiveChannel = activeChannels[index]
+ }
+
+ return string(b)
}
func StatusListFromJson(data io.Reader) []*Status {
- decoder := json.NewDecoder(data)
var statuses []*Status
- err := decoder.Decode(&statuses)
- if err == nil {
- return statuses
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&statuses)
+ return statuses
}
func StatusMapToInterfaceMap(statusMap map[string]*Status) map[string]interface{} {
diff --git a/vendor/github.com/mattermost/platform/model/suggest_command.go b/vendor/github.com/mattermost/mattermost-server/model/suggest_command.go
index 3d066499..44f46bf7 100644
--- a/vendor/github.com/mattermost/platform/model/suggest_command.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/suggest_command.go
@@ -14,21 +14,12 @@ type SuggestCommand struct {
}
func (o *SuggestCommand) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func SuggestCommandFromJson(data io.Reader) *SuggestCommand {
- decoder := json.NewDecoder(data)
- var o SuggestCommand
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *SuggestCommand
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/switch_request.go b/vendor/github.com/mattermost/mattermost-server/model/switch_request.go
index 10039294..2a522f49 100644
--- a/vendor/github.com/mattermost/platform/model/switch_request.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/switch_request.go
@@ -15,27 +15,18 @@ type SwitchRequest struct {
Password string `json:"password"`
NewPassword string `json:"new_password"`
MfaCode string `json:"mfa_code"`
- LdapId string `json:"ldap_id"`
+ LdapLoginId string `json:"ldap_id"`
}
func (o *SwitchRequest) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func SwitchRequestFromJson(data io.Reader) *SwitchRequest {
- decoder := json.NewDecoder(data)
- var o SwitchRequest
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *SwitchRequest
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *SwitchRequest) EmailToOAuth() bool {
diff --git a/vendor/github.com/mattermost/mattermost-server/model/system.go b/vendor/github.com/mattermost/mattermost-server/model/system.go
new file mode 100644
index 00000000..4228516d
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/system.go
@@ -0,0 +1,47 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+ "math/big"
+)
+
+const (
+ SYSTEM_DIAGNOSTIC_ID = "DiagnosticId"
+ SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
+ SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
+ SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
+ SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
+ SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey"
+ SYSTEM_INSTALLATION_DATE_KEY = "InstallationDate"
+)
+
+type System struct {
+ Name string `json:"name"`
+ Value string `json:"value"`
+}
+
+func (o *System) ToJson() string {
+ b, _ := json.Marshal(o)
+ return string(b)
+}
+
+func SystemFromJson(data io.Reader) *System {
+ var o *System
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
+
+type SystemAsymmetricSigningKey struct {
+ ECDSAKey *SystemECDSAKey `json:"ecdsa_key,omitempty"`
+}
+
+type SystemECDSAKey struct {
+ Curve string `json:"curve"`
+ X *big.Int `json:"x"`
+ Y *big.Int `json:"y"`
+ D *big.Int `json:"d,omitempty"`
+}
diff --git a/vendor/github.com/mattermost/platform/model/team.go b/vendor/github.com/mattermost/mattermost-server/model/team.go
index 4fe03f2f..eadd0522 100644
--- a/vendor/github.com/mattermost/platform/model/team.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/team.go
@@ -26,42 +26,45 @@ const (
)
type Team struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- DeleteAt int64 `json:"delete_at"`
- DisplayName string `json:"display_name"`
- Name string `json:"name"`
- Description string `json:"description"`
- Email string `json:"email"`
- Type string `json:"type"`
- CompanyName string `json:"company_name"`
- AllowedDomains string `json:"allowed_domains"`
- InviteId string `json:"invite_id"`
- AllowOpenInvite bool `json:"allow_open_invite"`
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ DisplayName string `json:"display_name"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Email string `json:"email"`
+ Type string `json:"type"`
+ CompanyName string `json:"company_name"`
+ AllowedDomains string `json:"allowed_domains"`
+ InviteId string `json:"invite_id"`
+ AllowOpenInvite bool `json:"allow_open_invite"`
+ LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
+ SchemeId *string `json:"scheme_id"`
}
type TeamPatch struct {
DisplayName *string `json:"display_name"`
Description *string `json:"description"`
CompanyName *string `json:"company_name"`
+ AllowedDomains *string `json:"allowed_domains"`
InviteId *string `json:"invite_id"`
AllowOpenInvite *bool `json:"allow_open_invite"`
}
+type TeamForExport struct {
+ Team
+ SchemeName *string
+}
+
type Invites struct {
Invites []map[string]string `json:"invites"`
}
func InvitesFromJson(data io.Reader) *Invites {
- decoder := json.NewDecoder(data)
- var o Invites
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Invites
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *Invites) ToEmailList() []string {
@@ -73,72 +76,41 @@ func (o *Invites) ToEmailList() []string {
}
func (o *Invites) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func (o *Team) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func TeamFromJson(data io.Reader) *Team {
- decoder := json.NewDecoder(data)
- var o Team
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *Team
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func TeamMapToJson(u map[string]*Team) string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(u)
+ return string(b)
}
func TeamMapFromJson(data io.Reader) map[string]*Team {
- decoder := json.NewDecoder(data)
var teams map[string]*Team
- err := decoder.Decode(&teams)
- if err == nil {
- return teams
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&teams)
+ return teams
}
func TeamListToJson(t []*Team) string {
- b, err := json.Marshal(t)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(t)
+ return string(b)
}
func TeamListFromJson(data io.Reader) []*Team {
- decoder := json.NewDecoder(data)
var teams []*Team
- err := decoder.Decode(&teams)
- if err == nil {
- return teams
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&teams)
+ return teams
}
func (o *Team) Etag() string {
@@ -275,16 +247,6 @@ func CleanTeamName(s string) string {
func (o *Team) Sanitize() {
o.Email = ""
- o.AllowedDomains = ""
-}
-
-func (o *Team) SanitizeForNotLoggedIn() {
- o.Email = ""
- o.AllowedDomains = ""
- o.CompanyName = ""
- if !o.AllowOpenInvite {
- o.InviteId = ""
- }
}
func (t *Team) Patch(patch *TeamPatch) {
@@ -300,6 +262,10 @@ func (t *Team) Patch(patch *TeamPatch) {
t.CompanyName = *patch.CompanyName
}
+ if patch.AllowedDomains != nil {
+ t.AllowedDomains = *patch.AllowedDomains
+ }
+
if patch.InviteId != nil {
t.InviteId = *patch.InviteId
}
diff --git a/vendor/github.com/mattermost/platform/model/team_member.go b/vendor/github.com/mattermost/mattermost-server/model/team_member.go
index 6c70b75e..3bae3d7e 100644
--- a/vendor/github.com/mattermost/platform/model/team_member.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/team_member.go
@@ -11,10 +11,13 @@ import (
)
type TeamMember struct {
- TeamId string `json:"team_id"`
- UserId string `json:"user_id"`
- Roles string `json:"roles"`
- DeleteAt int64 `json:"delete_at"`
+ TeamId string `json:"team_id"`
+ UserId string `json:"user_id"`
+ Roles string `json:"roles"`
+ DeleteAt int64 `json:"delete_at"`
+ SchemeUser bool `json:"scheme_user"`
+ SchemeAdmin bool `json:"scheme_admin"`
+ ExplicitRoles string `json:"explicit_roles"`
}
type TeamUnread struct {
@@ -23,44 +26,31 @@ type TeamUnread struct {
MentionCount int64 `json:"mention_count"`
}
+type TeamMemberForExport struct {
+ TeamMember
+ TeamName string
+}
+
func (o *TeamMember) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func (o *TeamUnread) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func TeamMemberFromJson(data io.Reader) *TeamMember {
- decoder := json.NewDecoder(data)
- var o TeamMember
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *TeamMember
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func TeamUnreadFromJson(data io.Reader) *TeamUnread {
- decoder := json.NewDecoder(data)
- var o TeamUnread
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *TeamUnread
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func TeamMembersToJson(o []*TeamMember) string {
@@ -72,14 +62,9 @@ func TeamMembersToJson(o []*TeamMember) string {
}
func TeamMembersFromJson(data io.Reader) []*TeamMember {
- decoder := json.NewDecoder(data)
var o []*TeamMember
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func TeamsUnreadToJson(o []*TeamUnread) string {
@@ -91,14 +76,9 @@ func TeamsUnreadToJson(o []*TeamUnread) string {
}
func TeamsUnreadFromJson(data io.Reader) []*TeamUnread {
- decoder := json.NewDecoder(data)
var o []*TeamUnread
- err := decoder.Decode(&o)
- if err == nil {
- return o
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *TeamMember) IsValid() *AppError {
diff --git a/vendor/github.com/mattermost/platform/model/team_search.go b/vendor/github.com/mattermost/mattermost-server/model/team_search.go
index e0676022..e0676022 100644
--- a/vendor/github.com/mattermost/platform/model/team_search.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/team_search.go
diff --git a/vendor/github.com/mattermost/platform/model/team_stats.go b/vendor/github.com/mattermost/mattermost-server/model/team_stats.go
index e94ed37b..0d688b80 100644
--- a/vendor/github.com/mattermost/platform/model/team_stats.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/team_stats.go
@@ -15,21 +15,12 @@ type TeamStats struct {
}
func (o *TeamStats) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func TeamStatsFromJson(data io.Reader) *TeamStats {
- decoder := json.NewDecoder(data)
- var o TeamStats
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *TeamStats
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/terms_of_service.go b/vendor/github.com/mattermost/mattermost-server/model/terms_of_service.go
new file mode 100644
index 00000000..c99a7856
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/terms_of_service.go
@@ -0,0 +1,70 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "unicode/utf8"
+)
+
+// we only ever need the latest version of terms of service
+const TERMS_OF_SERVICE_CACHE_SIZE = 1
+
+type TermsOfService struct {
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at"`
+ UserId string `json:"user_id"`
+ Text string `json:"text"`
+}
+
+func (t *TermsOfService) IsValid() *AppError {
+ if len(t.Id) != 26 {
+ return InvalidTermsOfServiceError("id", "")
+ }
+
+ if t.CreateAt == 0 {
+ return InvalidTermsOfServiceError("create_at", t.Id)
+ }
+
+ if len(t.UserId) != 26 {
+ return InvalidTermsOfServiceError("user_id", t.Id)
+ }
+
+ if utf8.RuneCountInString(t.Text) > POST_MESSAGE_MAX_RUNES_V2 {
+ return InvalidTermsOfServiceError("text", t.Id)
+ }
+
+ return nil
+}
+
+func (t *TermsOfService) ToJson() string {
+ b, _ := json.Marshal(t)
+ return string(b)
+}
+
+func TermsOfServiceFromJson(data io.Reader) *TermsOfService {
+ var termsOfService *TermsOfService
+ json.NewDecoder(data).Decode(&termsOfService)
+ return termsOfService
+}
+
+func InvalidTermsOfServiceError(fieldName string, termsOfServiceId string) *AppError {
+ id := fmt.Sprintf("model.terms_of_service.is_valid.%s.app_error", fieldName)
+ details := ""
+ if termsOfServiceId != "" {
+ details = "terms_of_service_id=" + termsOfServiceId
+ }
+ return NewAppError("TermsOfServiceStore.IsValid", id, map[string]interface{}{"MaxLength": POST_MESSAGE_MAX_RUNES_V2}, details, http.StatusBadRequest)
+}
+
+func (t *TermsOfService) PreSave() {
+ if t.Id == "" {
+ t.Id = NewId()
+ }
+
+ t.CreateAt = GetMillis()
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/timezone.go b/vendor/github.com/mattermost/mattermost-server/model/timezone.go
new file mode 100644
index 00000000..420b9d2e
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/timezone.go
@@ -0,0 +1,628 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type SupportedTimezones []string
+
+func TimezonesToJson(timezoneList []string) string {
+ b, _ := json.Marshal(timezoneList)
+ return string(b)
+}
+
+func TimezonesFromJson(data io.Reader) SupportedTimezones {
+ var timezones SupportedTimezones
+ json.NewDecoder(data).Decode(&timezones)
+ return timezones
+}
+
+func DefaultUserTimezone() map[string]string {
+ defaultTimezone := make(map[string]string)
+ defaultTimezone["useAutomaticTimezone"] = "true"
+ defaultTimezone["automaticTimezone"] = ""
+ defaultTimezone["manualTimezone"] = ""
+
+ return defaultTimezone
+}
+
+var DefaultSupportedTimezones = []string{
+ "Africa/Abidjan",
+ "Africa/Accra",
+ "Africa/Addis_Ababa",
+ "Africa/Algiers",
+ "Africa/Asmara",
+ "Africa/Asmera",
+ "Africa/Bamako",
+ "Africa/Bangui",
+ "Africa/Banjul",
+ "Africa/Bissau",
+ "Africa/Blantyre",
+ "Africa/Brazzaville",
+ "Africa/Bujumbura",
+ "Africa/Cairo",
+ "Africa/Casablanca",
+ "Africa/Ceuta",
+ "Africa/Conakry",
+ "Africa/Dakar",
+ "Africa/Dar_es_Salaam",
+ "Africa/Djibouti",
+ "Africa/Douala",
+ "Africa/El_Aaiun",
+ "Africa/Freetown",
+ "Africa/Gaborone",
+ "Africa/Harare",
+ "Africa/Johannesburg",
+ "Africa/Juba",
+ "Africa/Kampala",
+ "Africa/Khartoum",
+ "Africa/Kigali",
+ "Africa/Kinshasa",
+ "Africa/Lagos",
+ "Africa/Libreville",
+ "Africa/Lome",
+ "Africa/Luanda",
+ "Africa/Lubumbashi",
+ "Africa/Lusaka",
+ "Africa/Malabo",
+ "Africa/Maputo",
+ "Africa/Maseru",
+ "Africa/Mbabane",
+ "Africa/Mogadishu",
+ "Africa/Monrovia",
+ "Africa/Nairobi",
+ "Africa/Ndjamena",
+ "Africa/Niamey",
+ "Africa/Nouakchott",
+ "Africa/Ouagadougou",
+ "Africa/Porto-Novo",
+ "Africa/Sao_Tome",
+ "Africa/Timbuktu",
+ "Africa/Tripoli",
+ "Africa/Tunis",
+ "Africa/Windhoek",
+ "America/Adak",
+ "America/Anchorage",
+ "America/Anguilla",
+ "America/Antigua",
+ "America/Araguaina",
+ "America/Argentina/Buenos_Aires",
+ "America/Argentina/Catamarca",
+ "America/Argentina/ComodRivadavia",
+ "America/Argentina/Cordoba",
+ "America/Argentina/Jujuy",
+ "America/Argentina/La_Rioja",
+ "America/Argentina/Mendoza",
+ "America/Argentina/Rio_Gallegos",
+ "America/Argentina/Salta",
+ "America/Argentina/San_Juan",
+ "America/Argentina/San_Luis",
+ "America/Argentina/Tucuman",
+ "America/Argentina/Ushuaia",
+ "America/Aruba",
+ "America/Asuncion",
+ "America/Atikokan",
+ "America/Atka",
+ "America/Bahia",
+ "America/Bahia_Banderas",
+ "America/Barbados",
+ "America/Belem",
+ "America/Belize",
+ "America/Blanc-Sablon",
+ "America/Boa_Vista",
+ "America/Bogota",
+ "America/Boise",
+ "America/Buenos_Aires",
+ "America/Cambridge_Bay",
+ "America/Campo_Grande",
+ "America/Cancun",
+ "America/Caracas",
+ "America/Catamarca",
+ "America/Cayenne",
+ "America/Cayman",
+ "America/Chicago",
+ "America/Chihuahua",
+ "America/Coral_Harbour",
+ "America/Cordoba",
+ "America/Costa_Rica",
+ "America/Creston",
+ "America/Cuiaba",
+ "America/Curacao",
+ "America/Danmarkshavn",
+ "America/Dawson",
+ "America/Dawson_Creek",
+ "America/Denver",
+ "America/Detroit",
+ "America/Dominica",
+ "America/Edmonton",
+ "America/Eirunepe",
+ "America/El_Salvador",
+ "America/Ensenada",
+ "America/Fort_Nelson",
+ "America/Fort_Wayne",
+ "America/Fortaleza",
+ "America/Glace_Bay",
+ "America/Godthab",
+ "America/Goose_Bay",
+ "America/Grand_Turk",
+ "America/Grenada",
+ "America/Guadeloupe",
+ "America/Guatemala",
+ "America/Guayaquil",
+ "America/Guyana",
+ "America/Halifax",
+ "America/Havana",
+ "America/Hermosillo",
+ "America/Indiana/Indianapolis",
+ "America/Indiana/Knox",
+ "America/Indiana/Marengo",
+ "America/Indiana/Petersburg",
+ "America/Indiana/Tell_City",
+ "America/Indiana/Vevay",
+ "America/Indiana/Vincennes",
+ "America/Indiana/Winamac",
+ "America/Indianapolis",
+ "America/Inuvik",
+ "America/Iqaluit",
+ "America/Jamaica",
+ "America/Jujuy",
+ "America/Juneau",
+ "America/Kentucky/Louisville",
+ "America/Kentucky/Monticello",
+ "America/Knox_IN",
+ "America/Kralendijk",
+ "America/La_Paz",
+ "America/Lima",
+ "America/Los_Angeles",
+ "America/Louisville",
+ "America/Lower_Princes",
+ "America/Maceio",
+ "America/Managua",
+ "America/Manaus",
+ "America/Marigot",
+ "America/Martinique",
+ "America/Matamoros",
+ "America/Mazatlan",
+ "America/Mendoza",
+ "America/Menominee",
+ "America/Merida",
+ "America/Metlakatla",
+ "America/Mexico_City",
+ "America/Miquelon",
+ "America/Moncton",
+ "America/Monterrey",
+ "America/Montevideo",
+ "America/Montreal",
+ "America/Montserrat",
+ "America/Nassau",
+ "America/New_York",
+ "America/Nipigon",
+ "America/Nome",
+ "America/Noronha",
+ "America/North_Dakota/Beulah",
+ "America/North_Dakota/Center",
+ "America/North_Dakota/New_Salem",
+ "America/Ojinaga",
+ "America/Panama",
+ "America/Pangnirtung",
+ "America/Paramaribo",
+ "America/Phoenix",
+ "America/Port-au-Prince",
+ "America/Port_of_Spain",
+ "America/Porto_Acre",
+ "America/Porto_Velho",
+ "America/Puerto_Rico",
+ "America/Punta_Arenas",
+ "America/Rainy_River",
+ "America/Rankin_Inlet",
+ "America/Recife",
+ "America/Regina",
+ "America/Resolute",
+ "America/Rio_Branco",
+ "America/Rosario",
+ "America/Santa_Isabel",
+ "America/Santarem",
+ "America/Santiago",
+ "America/Santo_Domingo",
+ "America/Sao_Paulo",
+ "America/Scoresbysund",
+ "America/Shiprock",
+ "America/Sitka",
+ "America/St_Barthelemy",
+ "America/St_Johns",
+ "America/St_Kitts",
+ "America/St_Lucia",
+ "America/St_Thomas",
+ "America/St_Vincent",
+ "America/Swift_Current",
+ "America/Tegucigalpa",
+ "America/Thule",
+ "America/Thunder_Bay",
+ "America/Tijuana",
+ "America/Toronto",
+ "America/Tortola",
+ "America/Vancouver",
+ "America/Virgin",
+ "America/Whitehorse",
+ "America/Winnipeg",
+ "America/Yakutat",
+ "America/Yellowknife",
+ "Antarctica/Casey",
+ "Antarctica/Davis",
+ "Antarctica/DumontDUrville",
+ "Antarctica/Macquarie",
+ "Antarctica/Mawson",
+ "Antarctica/McMurdo",
+ "Antarctica/Palmer",
+ "Antarctica/Rothera",
+ "Antarctica/South_Pole",
+ "Antarctica/Syowa",
+ "Antarctica/Troll",
+ "Antarctica/Vostok",
+ "Arctic/Longyearbyen",
+ "Asia/Aden",
+ "Asia/Almaty",
+ "Asia/Amman",
+ "Asia/Anadyr",
+ "Asia/Aqtau",
+ "Asia/Aqtobe",
+ "Asia/Ashgabat",
+ "Asia/Ashkhabad",
+ "Asia/Atyrau",
+ "Asia/Baghdad",
+ "Asia/Bahrain",
+ "Asia/Baku",
+ "Asia/Bangkok",
+ "Asia/Barnaul",
+ "Asia/Beirut",
+ "Asia/Bishkek",
+ "Asia/Brunei",
+ "Asia/Calcutta",
+ "Asia/Chita",
+ "Asia/Choibalsan",
+ "Asia/Chongqing",
+ "Asia/Chungking",
+ "Asia/Colombo",
+ "Asia/Dacca",
+ "Asia/Damascus",
+ "Asia/Dhaka",
+ "Asia/Dili",
+ "Asia/Dubai",
+ "Asia/Dushanbe",
+ "Asia/Famagusta",
+ "Asia/Gaza",
+ "Asia/Harbin",
+ "Asia/Hebron",
+ "Asia/Ho_Chi_Minh",
+ "Asia/Hong_Kong",
+ "Asia/Hovd",
+ "Asia/Irkutsk",
+ "Asia/Istanbul",
+ "Asia/Jakarta",
+ "Asia/Jayapura",
+ "Asia/Jerusalem",
+ "Asia/Kabul",
+ "Asia/Kamchatka",
+ "Asia/Karachi",
+ "Asia/Kashgar",
+ "Asia/Kathmandu",
+ "Asia/Katmandu",
+ "Asia/Khandyga",
+ "Asia/Kolkata",
+ "Asia/Krasnoyarsk",
+ "Asia/Kuala_Lumpur",
+ "Asia/Kuching",
+ "Asia/Kuwait",
+ "Asia/Macao",
+ "Asia/Macau",
+ "Asia/Magadan",
+ "Asia/Makassar",
+ "Asia/Manila",
+ "Asia/Muscat",
+ "Asia/Nicosia",
+ "Asia/Novokuznetsk",
+ "Asia/Novosibirsk",
+ "Asia/Omsk",
+ "Asia/Oral",
+ "Asia/Phnom_Penh",
+ "Asia/Pontianak",
+ "Asia/Pyongyang",
+ "Asia/Qatar",
+ "Asia/Qyzylorda",
+ "Asia/Rangoon",
+ "Asia/Riyadh",
+ "Asia/Saigon",
+ "Asia/Sakhalin",
+ "Asia/Samarkand",
+ "Asia/Seoul",
+ "Asia/Shanghai",
+ "Asia/Singapore",
+ "Asia/Srednekolymsk",
+ "Asia/Taipei",
+ "Asia/Tashkent",
+ "Asia/Tbilisi",
+ "Asia/Tehran",
+ "Asia/Tel_Aviv",
+ "Asia/Thimbu",
+ "Asia/Thimphu",
+ "Asia/Tokyo",
+ "Asia/Tomsk",
+ "Asia/Ujung_Pandang",
+ "Asia/Ulaanbaatar",
+ "Asia/Ulan_Bator",
+ "Asia/Urumqi",
+ "Asia/Ust-Nera",
+ "Asia/Vientiane",
+ "Asia/Vladivostok",
+ "Asia/Yakutsk",
+ "Asia/Yangon",
+ "Asia/Yekaterinburg",
+ "Asia/Yerevan",
+ "Atlantic/Azores",
+ "Atlantic/Bermuda",
+ "Atlantic/Canary",
+ "Atlantic/Cape_Verde",
+ "Atlantic/Faeroe",
+ "Atlantic/Faroe",
+ "Atlantic/Jan_Mayen",
+ "Atlantic/Madeira",
+ "Atlantic/Reykjavik",
+ "Atlantic/South_Georgia",
+ "Atlantic/St_Helena",
+ "Atlantic/Stanley",
+ "Australia/ACT",
+ "Australia/Adelaide",
+ "Australia/Brisbane",
+ "Australia/Broken_Hill",
+ "Australia/Canberra",
+ "Australia/Currie",
+ "Australia/Darwin",
+ "Australia/Eucla",
+ "Australia/Hobart",
+ "Australia/LHI",
+ "Australia/Lindeman",
+ "Australia/Lord_Howe",
+ "Australia/Melbourne",
+ "Australia/NSW",
+ "Australia/North",
+ "Australia/Perth",
+ "Australia/Queensland",
+ "Australia/South",
+ "Australia/Sydney",
+ "Australia/Tasmania",
+ "Australia/Victoria",
+ "Australia/West",
+ "Australia/Yancowinna",
+ "Brazil/Acre",
+ "Brazil/DeNoronha",
+ "Brazil/East",
+ "Brazil/West",
+ "CET",
+ "CST6CDT",
+ "Canada/Atlantic",
+ "Canada/Central",
+ "Canada/East-Saskatchewan",
+ "Canada/Eastern",
+ "Canada/Mountain",
+ "Canada/Newfoundland",
+ "Canada/Pacific",
+ "Canada/Saskatchewan",
+ "Canada/Yukon",
+ "Chile/Continental",
+ "Chile/EasterIsland",
+ "Cuba",
+ "EET",
+ "EST",
+ "EST5EDT",
+ "Egypt",
+ "Eire",
+ "Etc/GMT",
+ "Etc/GMT+0",
+ "Etc/GMT+1",
+ "Etc/GMT+10",
+ "Etc/GMT+11",
+ "Etc/GMT+12",
+ "Etc/GMT+2",
+ "Etc/GMT+3",
+ "Etc/GMT+4",
+ "Etc/GMT+5",
+ "Etc/GMT+6",
+ "Etc/GMT+7",
+ "Etc/GMT+8",
+ "Etc/GMT+9",
+ "Etc/GMT-0",
+ "Etc/GMT-1",
+ "Etc/GMT-10",
+ "Etc/GMT-11",
+ "Etc/GMT-12",
+ "Etc/GMT-13",
+ "Etc/GMT-14",
+ "Etc/GMT-2",
+ "Etc/GMT-3",
+ "Etc/GMT-4",
+ "Etc/GMT-5",
+ "Etc/GMT-6",
+ "Etc/GMT-7",
+ "Etc/GMT-8",
+ "Etc/GMT-9",
+ "Etc/GMT0",
+ "Etc/Greenwich",
+ "Etc/UCT",
+ "Etc/UTC",
+ "Etc/Universal",
+ "Etc/Zulu",
+ "Europe/Amsterdam",
+ "Europe/Andorra",
+ "Europe/Astrakhan",
+ "Europe/Athens",
+ "Europe/Belfast",
+ "Europe/Belgrade",
+ "Europe/Berlin",
+ "Europe/Bratislava",
+ "Europe/Brussels",
+ "Europe/Bucharest",
+ "Europe/Budapest",
+ "Europe/Busingen",
+ "Europe/Chisinau",
+ "Europe/Copenhagen",
+ "Europe/Dublin",
+ "Europe/Gibraltar",
+ "Europe/Guernsey",
+ "Europe/Helsinki",
+ "Europe/Isle_of_Man",
+ "Europe/Istanbul",
+ "Europe/Jersey",
+ "Europe/Kaliningrad",
+ "Europe/Kiev",
+ "Europe/Kirov",
+ "Europe/Lisbon",
+ "Europe/Ljubljana",
+ "Europe/London",
+ "Europe/Luxembourg",
+ "Europe/Madrid",
+ "Europe/Malta",
+ "Europe/Mariehamn",
+ "Europe/Minsk",
+ "Europe/Monaco",
+ "Europe/Moscow",
+ "Europe/Nicosia",
+ "Europe/Oslo",
+ "Europe/Paris",
+ "Europe/Podgorica",
+ "Europe/Prague",
+ "Europe/Riga",
+ "Europe/Rome",
+ "Europe/Samara",
+ "Europe/San_Marino",
+ "Europe/Sarajevo",
+ "Europe/Saratov",
+ "Europe/Simferopol",
+ "Europe/Skopje",
+ "Europe/Sofia",
+ "Europe/Stockholm",
+ "Europe/Tallinn",
+ "Europe/Tirane",
+ "Europe/Tiraspol",
+ "Europe/Ulyanovsk",
+ "Europe/Uzhgorod",
+ "Europe/Vaduz",
+ "Europe/Vatican",
+ "Europe/Vienna",
+ "Europe/Vilnius",
+ "Europe/Volgograd",
+ "Europe/Warsaw",
+ "Europe/Zagreb",
+ "Europe/Zaporozhye",
+ "Europe/Zurich",
+ "Factory",
+ "GB",
+ "GB-Eire",
+ "GMT",
+ "GMT+0",
+ "GMT-0",
+ "GMT0",
+ "Greenwich",
+ "HST",
+ "Hongkong",
+ "Iceland",
+ "Indian/Antananarivo",
+ "Indian/Chagos",
+ "Indian/Christmas",
+ "Indian/Cocos",
+ "Indian/Comoro",
+ "Indian/Kerguelen",
+ "Indian/Mahe",
+ "Indian/Maldives",
+ "Indian/Mauritius",
+ "Indian/Mayotte",
+ "Indian/Reunion",
+ "Iran",
+ "Israel",
+ "Jamaica",
+ "Japan",
+ "Kwajalein",
+ "Libya",
+ "MET",
+ "MST",
+ "MST7MDT",
+ "Mexico/BajaNorte",
+ "Mexico/BajaSur",
+ "Mexico/General",
+ "NZ",
+ "NZ-CHAT",
+ "Navajo",
+ "PRC",
+ "PST8PDT",
+ "Pacific/Apia",
+ "Pacific/Auckland",
+ "Pacific/Bougainville",
+ "Pacific/Chatham",
+ "Pacific/Chuuk",
+ "Pacific/Easter",
+ "Pacific/Efate",
+ "Pacific/Enderbury",
+ "Pacific/Fakaofo",
+ "Pacific/Fiji",
+ "Pacific/Funafuti",
+ "Pacific/Galapagos",
+ "Pacific/Gambier",
+ "Pacific/Guadalcanal",
+ "Pacific/Guam",
+ "Pacific/Honolulu",
+ "Pacific/Johnston",
+ "Pacific/Kiritimati",
+ "Pacific/Kosrae",
+ "Pacific/Kwajalein",
+ "Pacific/Majuro",
+ "Pacific/Marquesas",
+ "Pacific/Midway",
+ "Pacific/Nauru",
+ "Pacific/Niue",
+ "Pacific/Norfolk",
+ "Pacific/Noumea",
+ "Pacific/Pago_Pago",
+ "Pacific/Palau",
+ "Pacific/Pitcairn",
+ "Pacific/Pohnpei",
+ "Pacific/Ponape",
+ "Pacific/Port_Moresby",
+ "Pacific/Rarotonga",
+ "Pacific/Saipan",
+ "Pacific/Samoa",
+ "Pacific/Tahiti",
+ "Pacific/Tarawa",
+ "Pacific/Tongatapu",
+ "Pacific/Truk",
+ "Pacific/Wake",
+ "Pacific/Wallis",
+ "Pacific/Yap",
+ "Poland",
+ "Portugal",
+ "ROC",
+ "ROK",
+ "Singapore",
+ "Turkey",
+ "UCT",
+ "US/Alaska",
+ "US/Aleutian",
+ "US/Arizona",
+ "US/Central",
+ "US/East-Indiana",
+ "US/Eastern",
+ "US/Hawaii",
+ "US/Indiana-Starke",
+ "US/Michigan",
+ "US/Mountain",
+ "US/Pacific",
+ "US/Pacific-New",
+ "US/Samoa",
+ "UTC",
+ "Universal",
+ "W-SU",
+ "WET",
+ "Zulu",
+}
diff --git a/vendor/github.com/mattermost/platform/model/token.go b/vendor/github.com/mattermost/mattermost-server/model/token.go
index a4d10c7f..a4d10c7f 100644
--- a/vendor/github.com/mattermost/platform/model/token.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/token.go
diff --git a/vendor/github.com/mattermost/platform/model/user.go b/vendor/github.com/mattermost/mattermost-server/model/user.go
index 7e767fd5..51f54c1b 100644
--- a/vendor/github.com/mattermost/platform/model/user.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/user.go
@@ -22,7 +22,6 @@ const (
USER_NOTIFY_NONE = "none"
DESKTOP_NOTIFY_PROP = "desktop"
DESKTOP_SOUND_NOTIFY_PROP = "desktop_sound"
- DESKTOP_DURATION_NOTIFY_PROP = "desktop_duration"
MARK_UNREAD_NOTIFY_PROP = "mark_unread"
PUSH_NOTIFY_PROP = "push"
PUSH_STATUS_NOTIFY_PROP = "push_status"
@@ -39,7 +38,7 @@ const (
USER_EMAIL_MAX_LENGTH = 128
USER_NICKNAME_MAX_RUNES = 64
- USER_POSITION_MAX_RUNES = 64
+ USER_POSITION_MAX_RUNES = 128
USER_FIRST_NAME_MAX_RUNES = 64
USER_LAST_NAME_MAX_RUNES = 64
USER_AUTH_DATA_MAX_LENGTH = 128
@@ -49,31 +48,33 @@ const (
)
type User struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at,omitempty"`
- UpdateAt int64 `json:"update_at,omitempty"`
- DeleteAt int64 `json:"delete_at"`
- Username string `json:"username"`
- Password string `json:"password,omitempty"`
- AuthData *string `json:"auth_data,omitempty"`
- AuthService string `json:"auth_service"`
- Email string `json:"email"`
- EmailVerified bool `json:"email_verified,omitempty"`
- Nickname string `json:"nickname"`
- FirstName string `json:"first_name"`
- LastName string `json:"last_name"`
- Position string `json:"position"`
- Roles string `json:"roles"`
- AllowMarketing bool `json:"allow_marketing,omitempty"`
- Props StringMap `json:"props,omitempty"`
- NotifyProps StringMap `json:"notify_props,omitempty"`
- LastPasswordUpdate int64 `json:"last_password_update,omitempty"`
- LastPictureUpdate int64 `json:"last_picture_update,omitempty"`
- FailedAttempts int `json:"failed_attempts,omitempty"`
- Locale string `json:"locale"`
- MfaActive bool `json:"mfa_active,omitempty"`
- MfaSecret string `json:"mfa_secret,omitempty"`
- LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"`
+ Id string `json:"id"`
+ CreateAt int64 `json:"create_at,omitempty"`
+ UpdateAt int64 `json:"update_at,omitempty"`
+ DeleteAt int64 `json:"delete_at"`
+ Username string `json:"username"`
+ Password string `json:"password,omitempty"`
+ AuthData *string `json:"auth_data,omitempty"`
+ AuthService string `json:"auth_service"`
+ Email string `json:"email"`
+ EmailVerified bool `json:"email_verified,omitempty"`
+ Nickname string `json:"nickname"`
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"`
+ Position string `json:"position"`
+ Roles string `json:"roles"`
+ AllowMarketing bool `json:"allow_marketing,omitempty"`
+ Props StringMap `json:"props,omitempty"`
+ NotifyProps StringMap `json:"notify_props,omitempty"`
+ LastPasswordUpdate int64 `json:"last_password_update,omitempty"`
+ LastPictureUpdate int64 `json:"last_picture_update,omitempty"`
+ FailedAttempts int `json:"failed_attempts,omitempty"`
+ Locale string `json:"locale"`
+ Timezone StringMap `json:"timezone"`
+ MfaActive bool `json:"mfa_active,omitempty"`
+ MfaSecret string `json:"mfa_secret,omitempty"`
+ LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"`
+ AcceptedTermsOfServiceId string `json:"accepted_terms_of_service_id,omitempty"` // TODO remove this field when new TOS user action table is created
}
type UserPatch struct {
@@ -86,6 +87,7 @@ type UserPatch struct {
Props StringMap `json:"props,omitempty"`
NotifyProps StringMap `json:"notify_props,omitempty"`
Locale *string `json:"locale"`
+ Timezone StringMap `json:"timezone"`
}
type UserAuth struct {
@@ -94,6 +96,23 @@ type UserAuth struct {
AuthService string `json:"auth_service,omitempty"`
}
+func (u *User) DeepCopy() *User {
+ copyUser := *u
+ if u.AuthData != nil {
+ copyUser.AuthData = NewString(*u.AuthData)
+ }
+ if u.Props != nil {
+ copyUser.Props = CopyStringMap(u.Props)
+ }
+ if u.NotifyProps != nil {
+ copyUser.NotifyProps = CopyStringMap(u.NotifyProps)
+ }
+ if u.Timezone != nil {
+ copyUser.Timezone = CopyStringMap(u.Timezone)
+ }
+ return &copyUser
+}
+
// IsValid validates the user and returns an error if it isn't configured
// correctly.
func (u *User) IsValid() *AppError {
@@ -114,7 +133,7 @@ func (u *User) IsValid() *AppError {
return InvalidUserError("username", u.Id)
}
- if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 {
+ if len(u.Email) > USER_EMAIL_MAX_LENGTH || len(u.Email) == 0 || !IsValidEmail(u.Email) {
return InvalidUserError("email", u.Id)
}
@@ -162,6 +181,14 @@ func InvalidUserError(fieldName string, userId string) *AppError {
return NewAppError("User.IsValid", id, nil, details, http.StatusBadRequest)
}
+func NormalizeUsername(username string) string {
+ return strings.ToLower(username)
+}
+
+func NormalizeEmail(email string) string {
+ return strings.ToLower(email)
+}
+
// PreSave will set the Id and Username if missing. It will also fill
// in the CreateAt, UpdateAt times. It will also hash the password. It should
// be run before saving the user to the db.
@@ -178,8 +205,8 @@ func (u *User) PreSave() {
u.AuthData = nil
}
- u.Username = strings.ToLower(u.Username)
- u.Email = strings.ToLower(u.Email)
+ u.Username = NormalizeUsername(u.Username)
+ u.Email = NormalizeEmail(u.Email)
u.CreateAt = GetMillis()
u.UpdateAt = u.CreateAt
@@ -200,6 +227,10 @@ func (u *User) PreSave() {
u.SetDefaultNotifications()
}
+ if u.Timezone == nil {
+ u.Timezone = DefaultUserTimezone()
+ }
+
if len(u.Password) > 0 {
u.Password = HashPassword(u.Password)
}
@@ -207,8 +238,8 @@ func (u *User) PreSave() {
// PreUpdate should be run before updating the user in the db.
func (u *User) PreUpdate() {
- u.Username = strings.ToLower(u.Username)
- u.Email = strings.ToLower(u.Email)
+ u.Username = NormalizeUsername(u.Username)
+ u.Email = NormalizeEmail(u.Email)
u.UpdateAt = GetMillis()
if u.AuthData != nil && *u.AuthData == "" {
@@ -294,34 +325,26 @@ func (u *User) Patch(patch *UserPatch) {
if patch.Locale != nil {
u.Locale = *patch.Locale
}
+
+ if patch.Timezone != nil {
+ u.Timezone = patch.Timezone
+ }
}
// ToJson convert a User to a json string
func (u *User) ToJson() string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(u)
+ return string(b)
}
func (u *UserPatch) ToJson() string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(u)
+ return string(b)
}
func (u *UserAuth) ToJson() string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(u)
+ return string(b)
}
// Generate a valid strong etag so the browser can cache the results
@@ -377,12 +400,6 @@ func (u *User) MakeNonNil() {
}
}
-func (u *User) AddProp(key string, value string) {
- u.MakeNonNil()
-
- u.Props[key] = value
-}
-
func (u *User) AddNotifyProp(key string, value string) {
u.MakeNonNil()
@@ -390,11 +407,11 @@ func (u *User) AddNotifyProp(key string, value string) {
}
func (u *User) GetFullName() string {
- if u.FirstName != "" && u.LastName != "" {
+ if len(u.FirstName) > 0 && len(u.LastName) > 0 {
return u.FirstName + " " + u.LastName
- } else if u.FirstName != "" {
+ } else if len(u.FirstName) > 0 {
return u.FirstName
- } else if u.LastName != "" {
+ } else if len(u.LastName) > 0 {
return u.LastName
} else {
return ""
@@ -405,13 +422,13 @@ func (u *User) GetDisplayName(nameFormat string) string {
displayName := u.Username
if nameFormat == SHOW_NICKNAME_FULLNAME {
- if u.Nickname != "" {
+ if len(u.Nickname) > 0 {
displayName = u.Nickname
- } else if fullName := u.GetFullName(); fullName != "" {
+ } else if fullName := u.GetFullName(); len(fullName) > 0 {
displayName = fullName
}
} else if nameFormat == SHOW_FULLNAME {
- if fullName := u.GetFullName(); fullName != "" {
+ if fullName := u.GetFullName(); len(fullName) > 0 {
displayName = fullName
}
}
@@ -432,7 +449,7 @@ func IsValidUserRoles(userRoles string) bool {
roles := strings.Fields(userRoles)
for _, r := range roles {
- if !isValidRole(r) {
+ if !IsValidRoleName(r) {
return false
}
}
@@ -445,11 +462,6 @@ func IsValidUserRoles(userRoles string) bool {
return true
}
-func isValidRole(roleId string) bool {
- _, ok := DefaultRoles[roleId]
- return ok
-}
-
// Make sure you acually want to use this function. In context.go there are functions to check permissions
// This function should not be used to check permissions.
func (u *User) IsInRole(inRole string) bool {
@@ -486,78 +498,53 @@ func (u *User) IsSAMLUser() bool {
return u.AuthService == USER_AUTH_SERVICE_SAML
}
+func (u *User) GetPreferredTimezone() string {
+ if u.Timezone["useAutomaticTimezone"] == "true" {
+ return u.Timezone["automaticTimezone"]
+ }
+
+ return u.Timezone["manualTimezone"]
+}
+
// UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User {
- decoder := json.NewDecoder(data)
- var user User
- err := decoder.Decode(&user)
- if err == nil {
- return &user
- } else {
- return nil
- }
+ var user *User
+ json.NewDecoder(data).Decode(&user)
+ return user
}
func UserPatchFromJson(data io.Reader) *UserPatch {
- decoder := json.NewDecoder(data)
- var user UserPatch
- err := decoder.Decode(&user)
- if err == nil {
- return &user
- } else {
- return nil
- }
+ var user *UserPatch
+ json.NewDecoder(data).Decode(&user)
+ return user
}
func UserAuthFromJson(data io.Reader) *UserAuth {
- decoder := json.NewDecoder(data)
- var user UserAuth
- err := decoder.Decode(&user)
- if err == nil {
- return &user
- } else {
- return nil
- }
+ var user *UserAuth
+ json.NewDecoder(data).Decode(&user)
+ return user
}
func UserMapToJson(u map[string]*User) string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(u)
+ return string(b)
}
func UserMapFromJson(data io.Reader) map[string]*User {
- decoder := json.NewDecoder(data)
var users map[string]*User
- err := decoder.Decode(&users)
- if err == nil {
- return users
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&users)
+ return users
}
func UserListToJson(u []*User) string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(u)
+ return string(b)
}
func UserListFromJson(data io.Reader) []*User {
- decoder := json.NewDecoder(data)
var users []*User
- err := decoder.Decode(&users)
- if err == nil {
- return users
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&users)
+ return users
}
// HashPassword generates a hash using the bcrypt.GenerateFromPassword
@@ -587,6 +574,7 @@ var restrictedUsernames = []string{
"all",
"channel",
"matterbot",
+ "system",
}
func IsValidUsername(s string) bool {
@@ -608,7 +596,7 @@ func IsValidUsername(s string) bool {
}
func CleanUsername(s string) string {
- s = strings.ToLower(strings.Replace(s, " ", "-", -1))
+ s = NormalizeUsername(strings.Replace(s, " ", "-", -1))
for _, value := range reservedName {
if s == value {
diff --git a/vendor/github.com/mattermost/platform/model/user_access_token.go b/vendor/github.com/mattermost/mattermost-server/model/user_access_token.go
index e189ec23..bffd9fcb 100644
--- a/vendor/github.com/mattermost/platform/model/user_access_token.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/user_access_token.go
@@ -43,41 +43,23 @@ func (t *UserAccessToken) PreSave() {
}
func (t *UserAccessToken) ToJson() string {
- b, err := json.Marshal(t)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(t)
+ return string(b)
}
func UserAccessTokenFromJson(data io.Reader) *UserAccessToken {
- decoder := json.NewDecoder(data)
- var t UserAccessToken
- err := decoder.Decode(&t)
- if err == nil {
- return &t
- } else {
- return nil
- }
+ var t *UserAccessToken
+ json.NewDecoder(data).Decode(&t)
+ return t
}
func UserAccessTokenListToJson(t []*UserAccessToken) string {
- b, err := json.Marshal(t)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(t)
+ return string(b)
}
func UserAccessTokenListFromJson(data io.Reader) []*UserAccessToken {
- decoder := json.NewDecoder(data)
var t []*UserAccessToken
- err := decoder.Decode(&t)
- if err == nil {
- return t
- } else {
- return nil
- }
+ json.NewDecoder(data).Decode(&t)
+ return t
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/user_access_token_search.go b/vendor/github.com/mattermost/mattermost-server/model/user_access_token_search.go
new file mode 100644
index 00000000..1b0146ed
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/user_access_token_search.go
@@ -0,0 +1,35 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type UserAccessTokenSearch struct {
+ Term string `json:"term"`
+}
+
+// ToJson convert a UserAccessTokenSearch to json string
+func (c *UserAccessTokenSearch) ToJson() string {
+ b, err := json.Marshal(c)
+ if err != nil {
+ return ""
+ }
+
+ return string(b)
+}
+
+// UserAccessTokenSearchJson decodes the input and returns a UserAccessTokenSearch
+func UserAccessTokenSearchFromJson(data io.Reader) *UserAccessTokenSearch {
+ decoder := json.NewDecoder(data)
+ var cs UserAccessTokenSearch
+ err := decoder.Decode(&cs)
+ if err == nil {
+ return &cs
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/mattermost/platform/model/user_autocomplete.go b/vendor/github.com/mattermost/mattermost-server/model/user_autocomplete.go
index 43c030b8..b5edb45b 100644
--- a/vendor/github.com/mattermost/platform/model/user_autocomplete.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/user_autocomplete.go
@@ -23,12 +23,8 @@ type UserAutocomplete struct {
}
func (o *UserAutocomplete) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func UserAutocompleteFromJson(data io.Reader) *UserAutocomplete {
@@ -43,41 +39,23 @@ func UserAutocompleteFromJson(data io.Reader) *UserAutocomplete {
}
func (o *UserAutocompleteInChannel) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func UserAutocompleteInChannelFromJson(data io.Reader) *UserAutocompleteInChannel {
- decoder := json.NewDecoder(data)
- var o UserAutocompleteInChannel
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *UserAutocompleteInChannel
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *UserAutocompleteInTeam) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func UserAutocompleteInTeamFromJson(data io.Reader) *UserAutocompleteInTeam {
- decoder := json.NewDecoder(data)
- var o UserAutocompleteInTeam
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *UserAutocompleteInTeam
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/user_search.go b/vendor/github.com/mattermost/mattermost-server/model/user_search.go
index 6213b16e..94596bdc 100644
--- a/vendor/github.com/mattermost/platform/model/user_search.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/user_search.go
@@ -20,22 +20,13 @@ type UserSearch struct {
// ToJson convert a User to a json string
func (u *UserSearch) ToJson() string {
- b, err := json.Marshal(u)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(u)
+ return string(b)
}
// UserSearchFromJson will decode the input and return a User
func UserSearchFromJson(data io.Reader) *UserSearch {
- decoder := json.NewDecoder(data)
- var us UserSearch
- err := decoder.Decode(&us)
- if err == nil {
- return &us
- } else {
- return nil
- }
+ var us *UserSearch
+ json.NewDecoder(data).Decode(&us)
+ return us
}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/users_stats.go b/vendor/github.com/mattermost/mattermost-server/model/users_stats.go
new file mode 100644
index 00000000..49c882e3
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/users_stats.go
@@ -0,0 +1,24 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type UsersStats struct {
+ TotalUsersCount int64 `json:"total_users_count"`
+}
+
+func (o *UsersStats) ToJson() string {
+ b, _ := json.Marshal(o)
+ return string(b)
+}
+
+func UsersStatsFromJson(data io.Reader) *UsersStats {
+ var o *UsersStats
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
diff --git a/vendor/github.com/mattermost/platform/model/utils.go b/vendor/github.com/mattermost/mattermost-server/model/utils.go
index e84d44f7..172b7824 100644
--- a/vendor/github.com/mattermost/platform/model/utils.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/utils.go
@@ -36,6 +36,12 @@ type StringInterface map[string]interface{}
type StringMap map[string]string
type StringArray []string
+var translateFunc goi18n.TranslateFunc = nil
+
+func AppErrorInit(t goi18n.TranslateFunc) {
+ translateFunc = t
+}
+
type AppError struct {
Id string `json:"id"`
Message string `json:"message"` // Message to be display to the end user without debugging information
@@ -52,6 +58,11 @@ func (er *AppError) Error() string {
}
func (er *AppError) Translate(T goi18n.TranslateFunc) {
+ if T == nil {
+ er.Message = er.Id
+ return
+ }
+
if er.params == nil {
er.Message = T(er.Id)
} else {
@@ -68,12 +79,8 @@ func (er *AppError) SystemMessage(T goi18n.TranslateFunc) string {
}
func (er *AppError) ToJson() string {
- b, err := json.Marshal(er)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(er)
+ return string(b)
}
// AppErrorFromJson will decode the input and return an AppError
@@ -105,6 +112,7 @@ func NewAppError(where string, id string, params map[string]interface{}, details
ap.DetailedError = details
ap.StatusCode = status
ap.IsOAuth = false
+ ap.Translate(translateFunc)
return ap
}
@@ -133,27 +141,60 @@ func NewRandomString(length int) string {
return b.String()
}
-// GetMillis is a convience method to get milliseconds since epoch.
+// GetMillis is a convenience method to get milliseconds since epoch.
func GetMillis() int64 {
return time.Now().UnixNano() / int64(time.Millisecond)
}
+// GetMillisForTime is a convenience method to get milliseconds since epoch for provided Time.
+func GetMillisForTime(thisTime time.Time) int64 {
+ return thisTime.UnixNano() / int64(time.Millisecond)
+}
+
+// PadDateStringZeros is a convenience method to pad 2 digit date parts with zeros to meet ISO 8601 format
+func PadDateStringZeros(dateString string) string {
+ parts := strings.Split(dateString, "-")
+ for index, part := range parts {
+ if len(part) == 1 {
+ parts[index] = "0" + part
+ }
+ }
+ dateString = strings.Join(parts[:], "-")
+ return dateString
+}
+
+// GetStartOfDayMillis is a convenience method to get milliseconds since epoch for provided date's start of day
+func GetStartOfDayMillis(thisTime time.Time, timeZoneOffset int) int64 {
+ localSearchTimeZone := time.FixedZone("Local Search Time Zone", timeZoneOffset)
+ resultTime := time.Date(thisTime.Year(), thisTime.Month(), thisTime.Day(), 0, 0, 0, 0, localSearchTimeZone)
+ return GetMillisForTime(resultTime)
+}
+
+// GetEndOfDayMillis is a convenience method to get milliseconds since epoch for provided date's end of day
+func GetEndOfDayMillis(thisTime time.Time, timeZoneOffset int) int64 {
+ localSearchTimeZone := time.FixedZone("Local Search Time Zone", timeZoneOffset)
+ resultTime := time.Date(thisTime.Year(), thisTime.Month(), thisTime.Day(), 23, 59, 59, 999999999, localSearchTimeZone)
+ return GetMillisForTime(resultTime)
+}
+
+func CopyStringMap(originalMap map[string]string) map[string]string {
+ copyMap := make(map[string]string)
+ for k, v := range originalMap {
+ copyMap[k] = v
+ }
+ return copyMap
+}
+
// MapToJson converts a map to a json string
func MapToJson(objmap map[string]string) string {
- if b, err := json.Marshal(objmap); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(objmap)
+ return string(b)
}
// MapToJson converts a map to a json string
func MapBoolToJson(objmap map[string]bool) string {
- if b, err := json.Marshal(objmap); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(objmap)
+ return string(b)
}
// MapFromJson will decode the key/value pair map
@@ -181,11 +222,8 @@ func MapBoolFromJson(data io.Reader) map[string]bool {
}
func ArrayToJson(objmap []string) string {
- if b, err := json.Marshal(objmap); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(objmap)
+ return string(b)
}
func ArrayFromJson(data io.Reader) []string {
@@ -217,11 +255,8 @@ func ArrayFromInterface(data interface{}) []string {
}
func StringInterfaceToJson(objmap map[string]interface{}) string {
- if b, err := json.Marshal(objmap); err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(objmap)
+ return string(b)
}
func StringInterfaceFromJson(data io.Reader) map[string]interface{} {
@@ -236,12 +271,8 @@ func StringInterfaceFromJson(data io.Reader) map[string]interface{} {
}
func StringToJson(s string) string {
- b, err := json.Marshal(s)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(s)
+ return string(b)
}
func StringFromJson(data io.Reader) string {
@@ -261,7 +292,7 @@ func GetServerIpAddress() string {
} else {
for _, addr := range addrs {
- if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() {
+ if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() && !ip.IP.IsLinkLocalUnicast() && !ip.IP.IsLinkLocalMulticast() {
if ip.IP.To4() != nil {
return ip.IP.String()
}
@@ -277,16 +308,18 @@ func IsLower(s string) bool {
}
func IsValidEmail(email string) bool {
-
if !IsLower(email) {
return false
}
- if _, err := mail.ParseAddress(email); err == nil {
- return true
+ if addr, err := mail.ParseAddress(email); err != nil {
+ return false
+ } else if addr.Name != "" {
+ // mail.ParseAddress accepts input of the form "Billy Bob <billy@example.com>" which we don't allow
+ return false
}
- return false
+ return true
}
var reservedName = []string{
@@ -402,9 +435,6 @@ func ClearMentionTags(post string) string {
return post
}
-var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&amp;]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
-var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
-
func IsValidHttpUrl(rawUrl string) bool {
if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
return false
@@ -417,18 +447,6 @@ func IsValidHttpUrl(rawUrl string) bool {
return true
}
-func IsValidHttpsUrl(rawUrl string) bool {
- if strings.Index(rawUrl, "https://") != 0 {
- return false
- }
-
- if _, err := url.ParseRequestURI(rawUrl); err != nil {
- return false
- }
-
- return true
-}
-
func IsValidTurnOrStunServer(rawUri string) bool {
if strings.Index(rawUri, "turn:") != 0 && strings.Index(rawUri, "stun:") != 0 {
return false
@@ -492,3 +510,57 @@ func IsValidId(value string) bool {
return true
}
+
+// Copied from https://golang.org/src/net/dnsclient.go#L119
+func IsDomainName(s string) bool {
+ // See RFC 1035, RFC 3696.
+ // Presentation format has dots before every label except the first, and the
+ // terminal empty label is optional here because we assume fully-qualified
+ // (absolute) input. We must therefore reserve space for the first and last
+ // labels' length octets in wire format, where they are necessary and the
+ // maximum total length is 255.
+ // So our _effective_ maximum is 253, but 254 is not rejected if the last
+ // character is a dot.
+ l := len(s)
+ if l == 0 || l > 254 || l == 254 && s[l-1] != '.' {
+ return false
+ }
+
+ last := byte('.')
+ ok := false // Ok once we've seen a letter.
+ partlen := 0
+ for i := 0; i < len(s); i++ {
+ c := s[i]
+ switch {
+ default:
+ return false
+ case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_':
+ ok = true
+ partlen++
+ case '0' <= c && c <= '9':
+ // fine
+ partlen++
+ case c == '-':
+ // Byte before dash cannot be dot.
+ if last == '.' {
+ return false
+ }
+ partlen++
+ case c == '.':
+ // Byte before dot cannot be dot, dash.
+ if last == '.' || last == '-' {
+ return false
+ }
+ if partlen > 63 || partlen == 0 {
+ return false
+ }
+ partlen = 0
+ }
+ last = c
+ }
+ if last == '-' || partlen > 63 {
+ return false
+ }
+
+ return ok
+}
diff --git a/vendor/github.com/mattermost/platform/model/version.go b/vendor/github.com/mattermost/mattermost-server/model/version.go
index 430924ee..1b09b0ad 100644
--- a/vendor/github.com/mattermost/platform/model/version.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/version.go
@@ -9,10 +9,23 @@ import (
"strings"
)
-// This is a list of all the current viersions including any patches.
-// It should be maitained in chronological order with most current
+// This is a list of all the current versions including any patches.
+// It should be maintained in chronological order with most current
// release at the front of the list.
var versions = []string{
+ "5.5.0",
+ "5.4.0",
+ "5.3.0",
+ "5.2.0",
+ "5.1.0",
+ "5.0.0",
+ "4.10.0",
+ "4.9.0",
+ "4.8.1",
+ "4.8.0",
+ "4.7.2",
+ "4.7.1",
+ "4.7.0",
"4.6.0",
"4.5.0",
"4.4.0",
@@ -104,10 +117,6 @@ func GetPreviousVersion(version string) string {
return ""
}
-func IsOfficalBuild() bool {
- return BuildNumber != "_BUILD_NUMBER_"
-}
-
func IsCurrentVersion(versionToCheck string) bool {
currentMajor, currentMinor, _ := SplitVersion(CurrentVersion)
toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck)
diff --git a/vendor/github.com/mattermost/platform/model/webrtc.go b/vendor/github.com/mattermost/mattermost-server/model/webrtc.go
index fa15a4b7..59797a5b 100644
--- a/vendor/github.com/mattermost/platform/model/webrtc.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/webrtc.go
@@ -22,32 +22,18 @@ type GatewayResponse struct {
}
func GatewayResponseFromJson(data io.Reader) *GatewayResponse {
- decoder := json.NewDecoder(data)
- var o GatewayResponse
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *GatewayResponse
+ json.NewDecoder(data).Decode(&o)
+ return o
}
func (o *WebrtcInfoResponse) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func WebrtcInfoResponseFromJson(data io.Reader) *WebrtcInfoResponse {
- decoder := json.NewDecoder(data)
- var o WebrtcInfoResponse
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *WebrtcInfoResponse
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/platform/model/websocket_client.go b/vendor/github.com/mattermost/mattermost-server/model/websocket_client.go
index e5c44dde..4e6c1d8c 100644
--- a/vendor/github.com/mattermost/platform/model/websocket_client.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/websocket_client.go
@@ -6,83 +6,90 @@ package model
import (
"encoding/json"
"net/http"
+ "time"
"github.com/gorilla/websocket"
)
const (
- SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB
+ SOCKET_MAX_MESSAGE_SIZE_KB = 8 * 1024 // 8KB
+ PING_TIMEOUT_BUFFER_SECONDS = 5
)
type WebSocketClient struct {
- Url string // The location of the server like "ws://localhost:8065"
- ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3"
- ConnectUrl string // The websocket URL to connect to like "ws://localhost:8065/api/v3/path/to/websocket"
- Conn *websocket.Conn // The WebSocket connection
- AuthToken string // The token used to open the WebSocket
- Sequence int64 // The ever-incrementing sequence attached to each WebSocket action
- EventChannel chan *WebSocketEvent
- ResponseChannel chan *WebSocketResponse
- ListenError *AppError
-}
-
-// NewWebSocketClient constructs a new WebSocket client with convienence
+ Url string // The location of the server like "ws://localhost:8065"
+ ApiUrl string // The api location of the server like "ws://localhost:8065/api/v3"
+ ConnectUrl string // The websocket URL to connect to like "ws://localhost:8065/api/v3/path/to/websocket"
+ Conn *websocket.Conn // The WebSocket connection
+ AuthToken string // The token used to open the WebSocket
+ Sequence int64 // The ever-incrementing sequence attached to each WebSocket action
+ PingTimeoutChannel chan bool // The channel used to signal ping timeouts
+ EventChannel chan *WebSocketEvent
+ ResponseChannel chan *WebSocketResponse
+ ListenError *AppError
+ pingTimeoutTimer *time.Timer
+}
+
+// NewWebSocketClient constructs a new WebSocket client with convenience
// methods for talking to the server.
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
- conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX_V3+"/users/websocket", nil)
+ return NewWebSocketClientWithDialer(websocket.DefaultDialer, url, authToken)
+}
+
+// NewWebSocketClientWithDialer constructs a new WebSocket client with convenience
+// methods for talking to the server using a custom dialer.
+func NewWebSocketClientWithDialer(dialer *websocket.Dialer, url, authToken string) (*WebSocketClient, *AppError) {
+ conn, _, err := dialer.Dial(url+API_URL_SUFFIX+"/websocket", nil)
if err != nil {
return nil, NewAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError)
}
client := &WebSocketClient{
url,
- url + API_URL_SUFFIX_V3,
- url + API_URL_SUFFIX_V3 + "/users/websocket",
+ url + API_URL_SUFFIX,
+ url + API_URL_SUFFIX + "/websocket",
conn,
authToken,
1,
+ make(chan bool, 1),
make(chan *WebSocketEvent, 100),
make(chan *WebSocketResponse, 100),
nil,
+ nil,
}
+ client.configurePingHandling()
+
client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken})
return client, nil
}
-// NewWebSocketClient4 constructs a new WebSocket client with convienence
+// NewWebSocketClient4 constructs a new WebSocket client with convenience
// methods for talking to the server. Uses the v4 endpoint.
func NewWebSocketClient4(url, authToken string) (*WebSocketClient, *AppError) {
- conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/websocket", nil)
- if err != nil {
- return nil, NewAppError("NewWebSocketClient4", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError)
- }
-
- client := &WebSocketClient{
- url,
- url + API_URL_SUFFIX,
- url + API_URL_SUFFIX + "/websocket",
- conn,
- authToken,
- 1,
- make(chan *WebSocketEvent, 100),
- make(chan *WebSocketResponse, 100),
- nil,
- }
-
- client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken})
+ return NewWebSocketClient4WithDialer(websocket.DefaultDialer, url, authToken)
+}
- return client, nil
+// NewWebSocketClient4WithDialer constructs a new WebSocket client with convenience
+// methods for talking to the server using a custom dialer. Uses the v4 endpoint.
+func NewWebSocketClient4WithDialer(dialer *websocket.Dialer, url, authToken string) (*WebSocketClient, *AppError) {
+ return NewWebSocketClientWithDialer(dialer, url, authToken)
}
func (wsc *WebSocketClient) Connect() *AppError {
+ return wsc.ConnectWithDialer(websocket.DefaultDialer)
+}
+
+func (wsc *WebSocketClient) ConnectWithDialer(dialer *websocket.Dialer) *AppError {
var err error
- wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ConnectUrl, nil)
+ wsc.Conn, _, err = dialer.Dial(wsc.ConnectUrl, nil)
if err != nil {
return NewAppError("Connect", "model.websocket_client.connect_fail.app_error", nil, err.Error(), http.StatusInternalServerError)
}
+ wsc.configurePingHandling()
+
wsc.EventChannel = make(chan *WebSocketEvent, 100)
wsc.ResponseChannel = make(chan *WebSocketResponse, 100)
@@ -165,3 +172,24 @@ func (wsc *WebSocketClient) GetStatusesByIds(userIds []string) {
}
wsc.SendMessage("get_statuses_by_ids", data)
}
+
+func (wsc *WebSocketClient) configurePingHandling() {
+ wsc.Conn.SetPingHandler(wsc.pingHandler)
+ wsc.pingTimeoutTimer = time.NewTimer(time.Second * (60 + PING_TIMEOUT_BUFFER_SECONDS))
+ go wsc.pingWatchdog()
+}
+
+func (wsc *WebSocketClient) pingHandler(appData string) error {
+ if !wsc.pingTimeoutTimer.Stop() {
+ <-wsc.pingTimeoutTimer.C
+ }
+
+ wsc.pingTimeoutTimer.Reset(time.Second * (60 + PING_TIMEOUT_BUFFER_SECONDS))
+ wsc.Conn.WriteMessage(websocket.PongMessage, []byte{})
+ return nil
+}
+
+func (wsc *WebSocketClient) pingWatchdog() {
+ <-wsc.pingTimeoutTimer.C
+ wsc.PingTimeoutChannel <- true
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/model/websocket_message.go b/vendor/github.com/mattermost/mattermost-server/model/websocket_message.go
new file mode 100644
index 00000000..ea8872d7
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/model/websocket_message.go
@@ -0,0 +1,165 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+)
+
+const (
+ WEBSOCKET_EVENT_TYPING = "typing"
+ WEBSOCKET_EVENT_POSTED = "posted"
+ WEBSOCKET_EVENT_POST_EDITED = "post_edited"
+ WEBSOCKET_EVENT_POST_DELETED = "post_deleted"
+ WEBSOCKET_EVENT_CHANNEL_CONVERTED = "channel_converted"
+ WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created"
+ WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted"
+ WEBSOCKET_EVENT_CHANNEL_UPDATED = "channel_updated"
+ WEBSOCKET_EVENT_CHANNEL_MEMBER_UPDATED = "channel_member_updated"
+ WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
+ WEBSOCKET_EVENT_GROUP_ADDED = "group_added"
+ WEBSOCKET_EVENT_NEW_USER = "new_user"
+ WEBSOCKET_EVENT_ADDED_TO_TEAM = "added_to_team"
+ WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
+ WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
+ WEBSOCKET_EVENT_DELETE_TEAM = "delete_team"
+ WEBSOCKET_EVENT_USER_ADDED = "user_added"
+ WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
+ WEBSOCKET_EVENT_USER_ROLE_UPDATED = "user_role_updated"
+ WEBSOCKET_EVENT_MEMBERROLE_UPDATED = "memberrole_updated"
+ WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
+ WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed"
+ WEBSOCKET_EVENT_PREFERENCES_CHANGED = "preferences_changed"
+ WEBSOCKET_EVENT_PREFERENCES_DELETED = "preferences_deleted"
+ WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message"
+ WEBSOCKET_EVENT_STATUS_CHANGE = "status_change"
+ WEBSOCKET_EVENT_HELLO = "hello"
+ WEBSOCKET_EVENT_WEBRTC = "webrtc"
+ WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge"
+ WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added"
+ WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed"
+ WEBSOCKET_EVENT_RESPONSE = "response"
+ WEBSOCKET_EVENT_EMOJI_ADDED = "emoji_added"
+ WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed"
+ WEBSOCKET_EVENT_PLUGIN_STATUSES_CHANGED = "plugin_statuses_changed"
+ WEBSOCKET_EVENT_PLUGIN_ENABLED = "plugin_enabled"
+ WEBSOCKET_EVENT_PLUGIN_DISABLED = "plugin_disabled"
+ WEBSOCKET_EVENT_ROLE_UPDATED = "role_updated"
+ WEBSOCKET_EVENT_LICENSE_CHANGED = "license_changed"
+ WEBSOCKET_EVENT_CONFIG_CHANGED = "config_changed"
+)
+
+type WebSocketMessage interface {
+ ToJson() string
+ IsValid() bool
+ EventType() string
+}
+
+type WebsocketBroadcast struct {
+ OmitUsers map[string]bool `json:"omit_users"` // broadcast is omitted for users listed here
+ UserId string `json:"user_id"` // broadcast only occurs for this user
+ ChannelId string `json:"channel_id"` // broadcast only occurs for users in this channel
+ TeamId string `json:"team_id"` // broadcast only occurs for users in this team
+ ContainsSanitizedData bool `json:"-"`
+ ContainsSensitiveData bool `json:"-"`
+}
+
+type precomputedWebSocketEventJSON struct {
+ Event json.RawMessage
+ Data json.RawMessage
+ Broadcast json.RawMessage
+}
+
+type WebSocketEvent struct {
+ Event string `json:"event"`
+ Data map[string]interface{} `json:"data"`
+ Broadcast *WebsocketBroadcast `json:"broadcast"`
+ Sequence int64 `json:"seq"`
+
+ precomputedJSON *precomputedWebSocketEventJSON
+}
+
+// PrecomputeJSON precomputes and stores the serialized JSON for all fields other than Sequence.
+// This makes ToJson much more efficient when sending the same event to multiple connections.
+func (m *WebSocketEvent) PrecomputeJSON() {
+ event, _ := json.Marshal(m.Event)
+ data, _ := json.Marshal(m.Data)
+ broadcast, _ := json.Marshal(m.Broadcast)
+ m.precomputedJSON = &precomputedWebSocketEventJSON{
+ Event: json.RawMessage(event),
+ Data: json.RawMessage(data),
+ Broadcast: json.RawMessage(broadcast),
+ }
+}
+
+func (m *WebSocketEvent) Add(key string, value interface{}) {
+ m.Data[key] = value
+}
+
+func NewWebSocketEvent(event, teamId, channelId, userId string, omitUsers map[string]bool) *WebSocketEvent {
+ return &WebSocketEvent{Event: event, Data: make(map[string]interface{}),
+ Broadcast: &WebsocketBroadcast{TeamId: teamId, ChannelId: channelId, UserId: userId, OmitUsers: omitUsers}}
+}
+
+func (o *WebSocketEvent) IsValid() bool {
+ return o.Event != ""
+}
+
+func (o *WebSocketEvent) EventType() string {
+ return o.Event
+}
+
+func (o *WebSocketEvent) ToJson() string {
+ if o.precomputedJSON != nil {
+ return fmt.Sprintf(`{"event": %s, "data": %s, "broadcast": %s, "seq": %d}`, o.precomputedJSON.Event, o.precomputedJSON.Data, o.precomputedJSON.Broadcast, o.Sequence)
+ }
+ b, _ := json.Marshal(o)
+ return string(b)
+}
+
+func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
+ var o *WebSocketEvent
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
+
+type WebSocketResponse struct {
+ Status string `json:"status"`
+ SeqReply int64 `json:"seq_reply,omitempty"`
+ Data map[string]interface{} `json:"data,omitempty"`
+ Error *AppError `json:"error,omitempty"`
+}
+
+func (m *WebSocketResponse) Add(key string, value interface{}) {
+ m.Data[key] = value
+}
+
+func NewWebSocketResponse(status string, seqReply int64, data map[string]interface{}) *WebSocketResponse {
+ return &WebSocketResponse{Status: status, SeqReply: seqReply, Data: data}
+}
+
+func NewWebSocketError(seqReply int64, err *AppError) *WebSocketResponse {
+ return &WebSocketResponse{Status: STATUS_FAIL, SeqReply: seqReply, Error: err}
+}
+
+func (o *WebSocketResponse) IsValid() bool {
+ return o.Status != ""
+}
+
+func (o *WebSocketResponse) EventType() string {
+ return WEBSOCKET_EVENT_RESPONSE
+}
+
+func (o *WebSocketResponse) ToJson() string {
+ b, _ := json.Marshal(o)
+ return string(b)
+}
+
+func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse {
+ var o *WebSocketResponse
+ json.NewDecoder(data).Decode(&o)
+ return o
+}
diff --git a/vendor/github.com/mattermost/platform/model/websocket_request.go b/vendor/github.com/mattermost/mattermost-server/model/websocket_request.go
index 7dc0b433..4da626e2 100644
--- a/vendor/github.com/mattermost/platform/model/websocket_request.go
+++ b/vendor/github.com/mattermost/mattermost-server/model/websocket_request.go
@@ -23,21 +23,12 @@ type WebSocketRequest struct {
}
func (o *WebSocketRequest) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
+ b, _ := json.Marshal(o)
+ return string(b)
}
func WebSocketRequestFromJson(data io.Reader) *WebSocketRequest {
- decoder := json.NewDecoder(data)
- var o WebSocketRequest
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
+ var o *WebSocketRequest
+ json.NewDecoder(data).Decode(&o)
+ return o
}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/jsonutils/json.go b/vendor/github.com/mattermost/mattermost-server/utils/jsonutils/json.go
new file mode 100644
index 00000000..da77a2b6
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/jsonutils/json.go
@@ -0,0 +1,56 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package jsonutils
+
+import (
+ "bytes"
+ "encoding/json"
+
+ "github.com/pkg/errors"
+)
+
+type HumanizedJsonError struct {
+ Err error
+ Line int
+ Character int
+}
+
+func (e *HumanizedJsonError) Error() string {
+ return e.Err.Error()
+}
+
+// HumanizeJsonError extracts error offsets and annotates the error with useful context
+func HumanizeJsonError(err error, data []byte) error {
+ if syntaxError, ok := err.(*json.SyntaxError); ok {
+ return NewHumanizedJsonError(syntaxError, data, syntaxError.Offset)
+ } else if unmarshalError, ok := err.(*json.UnmarshalTypeError); ok {
+ return NewHumanizedJsonError(unmarshalError, data, unmarshalError.Offset)
+ } else {
+ return err
+ }
+}
+
+func NewHumanizedJsonError(err error, data []byte, offset int64) *HumanizedJsonError {
+ if err == nil {
+ return nil
+ }
+
+ if offset < 0 || offset > int64(len(data)) {
+ return &HumanizedJsonError{
+ Err: errors.Wrapf(err, "invalid offset %d", offset),
+ }
+ }
+
+ lineSep := []byte{'\n'}
+
+ line := bytes.Count(data[:offset], lineSep) + 1
+ lastLineOffset := bytes.LastIndex(data[:offset], lineSep)
+ character := int(offset) - (lastLineOffset + 1) + 1
+
+ return &HumanizedJsonError{
+ Line: line,
+ Character: character,
+ Err: errors.Wrapf(err, "parsing error at line %d, character %d", line, character),
+ }
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/autolink.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/autolink.go
new file mode 100644
index 00000000..7f7d1117
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/autolink.go
@@ -0,0 +1,253 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "regexp"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// Based off of extensions/autolink.c from https://github.com/github/cmark
+
+var (
+ DefaultUrlSchemes = []string{"http", "https", "ftp", "mailto", "tel"}
+)
+
+// Given a string with a w at the given position, tries to parse and return a range containing a www link.
+// if one exists. If the text at the given position isn't a link, returns an empty string. Equivalent to
+// www_match from the reference code.
+func parseWWWAutolink(data string, position int) (Range, bool) {
+ // Check that this isn't part of another word
+ if position > 1 {
+ prevChar := data[position-1]
+
+ if !isWhitespaceByte(prevChar) && !isAllowedBeforeWWWLink(prevChar) {
+ return Range{}, false
+ }
+ }
+
+ // Check that this starts with www
+ if len(data)-position < 4 || !regexp.MustCompile(`^www\d{0,3}\.`).MatchString(data[position:]) {
+ return Range{}, false
+ }
+
+ end := checkDomain(data[position:], false)
+ if end == 0 {
+ return Range{}, false
+ }
+
+ end += position
+
+ // Grab all text until the end of the string or the next whitespace character
+ for end < len(data) && !isWhitespaceByte(data[end]) {
+ end += 1
+ }
+
+ // Trim trailing punctuation
+ end = trimTrailingCharactersFromLink(data, position, end)
+ if position == end {
+ return Range{}, false
+ }
+
+ return Range{position, end}, true
+}
+
+func isAllowedBeforeWWWLink(c byte) bool {
+ switch c {
+ case '*', '_', '~', ')':
+ return true
+ default:
+ return false
+ }
+}
+
+// Given a string with a : at the given position, tried to parse and return a range containing a URL scheme
+// if one exists. If the text around the given position isn't a link, returns an empty string. Equivalent to
+// url_match from the reference code.
+func parseURLAutolink(data string, position int) (Range, bool) {
+ // Check that a :// exists. This doesn't match the clients that treat the slashes as optional.
+ if len(data)-position < 4 || data[position+1] != '/' || data[position+2] != '/' {
+ return Range{}, false
+ }
+
+ start := position - 1
+ for start > 0 && isAlphanumericByte(data[start-1]) {
+ start -= 1
+ }
+
+ // Ensure that the URL scheme is allowed and that at least one character after the scheme is valid.
+ scheme := data[start:position]
+ if !isSchemeAllowed(scheme) || !isValidHostCharacter(data[position+3:]) {
+ return Range{}, false
+ }
+
+ end := checkDomain(data[position+3:], true)
+ if end == 0 {
+ return Range{}, false
+ }
+
+ end += position
+
+ // Grab all text until the end of the string or the next whitespace character
+ for end < len(data) && !isWhitespaceByte(data[end]) {
+ end += 1
+ }
+
+ // Trim trailing punctuation
+ end = trimTrailingCharactersFromLink(data, start, end)
+ if start == end {
+ return Range{}, false
+ }
+
+ return Range{start, end}, true
+}
+
+func isSchemeAllowed(scheme string) bool {
+ // Note that this doesn't support the custom URL schemes implemented by the client
+ for _, allowed := range DefaultUrlSchemes {
+ if strings.EqualFold(allowed, scheme) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Given a string starting with a URL, returns the number of valid characters that make up the URL's domain.
+// Returns 0 if the string doesn't start with a domain name. allowShort determines whether or not the domain
+// needs to contain a period to be considered valid. Equivalent to check_domain from the reference code.
+func checkDomain(data string, allowShort bool) int {
+ foundUnderscore := false
+ foundPeriod := false
+
+ i := 1
+ for ; i < len(data)-1; i++ {
+ if data[i] == '_' {
+ foundUnderscore = true
+ break
+ } else if data[i] == '.' {
+ foundPeriod = true
+ } else if !isValidHostCharacter(data[i:]) && data[i] != '-' {
+ break
+ }
+ }
+
+ if foundUnderscore {
+ return 0
+ }
+
+ if allowShort {
+ // If allowShort is set, accept any string of valid domain characters
+ return i
+ }
+
+ // If allowShort isn't set, a valid domain just requires at least a single period. Note that this
+ // logic isn't entirely necessary because we already know the string starts with "www." when
+ // this is called from parseWWWAutolink
+ if foundPeriod {
+ return i
+ } else {
+ return 0
+ }
+}
+
+// Returns true if the provided link starts with a valid character for a domain name. Equivalent to
+// is_valid_hostchar from the reference code.
+func isValidHostCharacter(link string) bool {
+ c, _ := utf8.DecodeRuneInString(link)
+ if c == utf8.RuneError {
+ return false
+ }
+
+ return !unicode.IsSpace(c) && !unicode.IsPunct(c)
+}
+
+// Removes any trailing characters such as punctuation or stray brackets that shouldn't be part of the link.
+// Returns a new end position for the link. Equivalent to autolink_delim from the reference code.
+func trimTrailingCharactersFromLink(markdown string, start int, end int) int {
+ runes := []rune(markdown[start:end])
+ linkEnd := len(runes)
+
+ // Cut off the link before an open angle bracket if it contains one
+ for i, c := range runes {
+ if c == '<' {
+ linkEnd = i
+ break
+ }
+ }
+
+ for linkEnd > 0 {
+ c := runes[linkEnd-1]
+
+ if !canEndAutolink(c) {
+ // Trim trailing quotes, periods, etc
+ linkEnd = linkEnd - 1
+ } else if c == ';' {
+ // Trim a trailing HTML entity
+ newEnd := linkEnd - 2
+
+ for newEnd > 0 && ((runes[newEnd] >= 'a' && runes[newEnd] <= 'z') || (runes[newEnd] >= 'A' && runes[newEnd] <= 'Z')) {
+ newEnd -= 1
+ }
+
+ if newEnd < linkEnd-2 && runes[newEnd] == '&' {
+ linkEnd = newEnd
+ } else {
+ // This isn't actually an HTML entity, so just trim the semicolon
+ linkEnd = linkEnd - 1
+ }
+ } else if c == ')' {
+ // Only allow an autolink ending with a bracket if that bracket is part of a matching pair of brackets.
+ // If there are more closing brackets than opening ones, remove the extra bracket
+
+ numClosing := 0
+ numOpening := 0
+
+ // Examples (input text => output linked portion):
+ //
+ // http://www.pokemon.com/Pikachu_(Electric)
+ // => http://www.pokemon.com/Pikachu_(Electric)
+ //
+ // http://www.pokemon.com/Pikachu_((Electric)
+ // => http://www.pokemon.com/Pikachu_((Electric)
+ //
+ // http://www.pokemon.com/Pikachu_(Electric))
+ // => http://www.pokemon.com/Pikachu_(Electric)
+ //
+ // http://www.pokemon.com/Pikachu_((Electric))
+ // => http://www.pokemon.com/Pikachu_((Electric))
+
+ for i := 0; i < linkEnd; i++ {
+ if runes[i] == '(' {
+ numOpening += 1
+ } else if runes[i] == ')' {
+ numClosing += 1
+ }
+ }
+
+ if numClosing <= numOpening {
+ // There's fewer or equal closing brackets, so we've found the end of the link
+ break
+ }
+
+ linkEnd -= 1
+ } else {
+ // There's no special characters at the end of the link, so we're at the end
+ break
+ }
+ }
+
+ return start + len(string(runes[:linkEnd]))
+}
+
+func canEndAutolink(c rune) bool {
+ switch c {
+ case '?', '!', '.', ',', ':', '*', '_', '~', '\'', '"':
+ return false
+ default:
+ return true
+ }
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/block_quote.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/block_quote.go
new file mode 100644
index 00000000..04a32461
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/block_quote.go
@@ -0,0 +1,62 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+type BlockQuote struct {
+ blockBase
+ markdown string
+
+ Children []Block
+}
+
+func (b *BlockQuote) Continuation(indentation int, r Range) *continuation {
+ if indentation > 3 {
+ return nil
+ }
+ s := b.markdown[r.Position:r.End]
+ if s == "" || s[0] != '>' {
+ return nil
+ }
+ remaining := Range{r.Position + 1, r.End}
+ indentation, indentationBytes := countIndentation(b.markdown, remaining)
+ if indentation > 0 {
+ indentation--
+ }
+ return &continuation{
+ Indentation: indentation,
+ Remaining: Range{remaining.Position + indentationBytes, remaining.End},
+ }
+}
+
+func (b *BlockQuote) AddChild(openBlocks []Block) []Block {
+ b.Children = append(b.Children, openBlocks[0])
+ return openBlocks
+}
+
+func blockQuoteStart(markdown string, indent int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ if indent > 3 {
+ return nil
+ }
+ s := markdown[r.Position:r.End]
+ if s == "" || s[0] != '>' {
+ return nil
+ }
+
+ block := &BlockQuote{
+ markdown: markdown,
+ }
+ r.Position++
+ if len(s) > 1 && s[1] == ' ' {
+ r.Position++
+ }
+
+ indent, bytes := countIndentation(markdown, r)
+
+ ret := []Block{block}
+ if descendants := blockStartOrParagraph(markdown, indent, Range{r.Position + bytes, r.End}, nil, nil); descendants != nil {
+ block.Children = append(block.Children, descendants[0])
+ ret = append(ret, descendants...)
+ }
+ return ret
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/blocks.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/blocks.go
new file mode 100644
index 00000000..14972f94
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/blocks.go
@@ -0,0 +1,153 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type continuation struct {
+ Indentation int
+ Remaining Range
+}
+
+type Block interface {
+ Continuation(indentation int, r Range) *continuation
+ AddLine(indentation int, r Range) bool
+ Close()
+ AllowsBlockStarts() bool
+ HasTrailingBlankLine() bool
+}
+
+type blockBase struct{}
+
+func (*blockBase) AddLine(indentation int, r Range) bool { return false }
+func (*blockBase) Close() {}
+func (*blockBase) AllowsBlockStarts() bool { return true }
+func (*blockBase) HasTrailingBlankLine() bool { return false }
+
+type ContainerBlock interface {
+ Block
+ AddChild(openBlocks []Block) []Block
+}
+
+type Range struct {
+ Position int
+ End int
+}
+
+func closeBlocks(blocks []Block, referenceDefinitions *[]*ReferenceDefinition) {
+ for _, block := range blocks {
+ block.Close()
+ if p, ok := block.(*Paragraph); ok && len(p.ReferenceDefinitions) > 0 {
+ *referenceDefinitions = append(*referenceDefinitions, p.ReferenceDefinitions...)
+ }
+ }
+}
+
+func ParseBlocks(markdown string, lines []Line) (*Document, []*ReferenceDefinition) {
+ document := &Document{}
+ var referenceDefinitions []*ReferenceDefinition
+
+ openBlocks := []Block{document}
+
+ for _, line := range lines {
+ r := line.Range
+ lastMatchIndex := 0
+
+ indentation, indentationBytes := countIndentation(markdown, r)
+ r = Range{r.Position + indentationBytes, r.End}
+
+ for i, block := range openBlocks {
+ if continuation := block.Continuation(indentation, r); continuation != nil {
+ indentation = continuation.Indentation
+ r = continuation.Remaining
+ additionalIndentation, additionalIndentationBytes := countIndentation(markdown, r)
+ r = Range{r.Position + additionalIndentationBytes, r.End}
+ indentation += additionalIndentation
+ lastMatchIndex = i
+ } else {
+ break
+ }
+ }
+
+ if openBlocks[lastMatchIndex].AllowsBlockStarts() {
+ if newBlocks := blockStart(markdown, indentation, r, openBlocks[:lastMatchIndex+1], openBlocks[lastMatchIndex+1:]); newBlocks != nil {
+ didAdd := false
+ for i := lastMatchIndex; i >= 0; i-- {
+ if container, ok := openBlocks[i].(ContainerBlock); ok {
+ if newBlocks := container.AddChild(newBlocks); newBlocks != nil {
+ closeBlocks(openBlocks[i+1:], &referenceDefinitions)
+ openBlocks = openBlocks[:i+1]
+ openBlocks = append(openBlocks, newBlocks...)
+ didAdd = true
+ break
+ }
+ }
+ }
+ if didAdd {
+ continue
+ }
+ }
+ }
+
+ isBlank := strings.TrimSpace(markdown[r.Position:r.End]) == ""
+ if paragraph, ok := openBlocks[len(openBlocks)-1].(*Paragraph); ok && !isBlank {
+ paragraph.Text = append(paragraph.Text, r)
+ continue
+ }
+
+ closeBlocks(openBlocks[lastMatchIndex+1:], &referenceDefinitions)
+ openBlocks = openBlocks[:lastMatchIndex+1]
+
+ if openBlocks[lastMatchIndex].AddLine(indentation, r) {
+ continue
+ }
+
+ if paragraph := newParagraph(markdown, r); paragraph != nil {
+ for i := lastMatchIndex; i >= 0; i-- {
+ if container, ok := openBlocks[i].(ContainerBlock); ok {
+ if newBlocks := container.AddChild([]Block{paragraph}); newBlocks != nil {
+ closeBlocks(openBlocks[i+1:], &referenceDefinitions)
+ openBlocks = openBlocks[:i+1]
+ openBlocks = append(openBlocks, newBlocks...)
+ break
+ }
+ }
+ }
+ }
+ }
+
+ closeBlocks(openBlocks, &referenceDefinitions)
+
+ return document, referenceDefinitions
+}
+
+func blockStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ if r.Position >= r.End {
+ return nil
+ }
+
+ if start := blockQuoteStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil {
+ return start
+ } else if start := listStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil {
+ return start
+ } else if start := indentedCodeStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil {
+ return start
+ } else if start := fencedCodeStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil {
+ return start
+ }
+
+ return nil
+}
+
+func blockStartOrParagraph(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ if start := blockStart(markdown, indentation, r, matchedBlocks, unmatchedBlocks); start != nil {
+ return start
+ }
+ if paragraph := newParagraph(markdown, r); paragraph != nil {
+ return []Block{paragraph}
+ }
+ return nil
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/document.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/document.go
new file mode 100644
index 00000000..224b5d21
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/document.go
@@ -0,0 +1,22 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+type Document struct {
+ blockBase
+
+ Children []Block
+}
+
+func (b *Document) Continuation(indentation int, r Range) *continuation {
+ return &continuation{
+ Indentation: indentation,
+ Remaining: r,
+ }
+}
+
+func (b *Document) AddChild(openBlocks []Block) []Block {
+ b.Children = append(b.Children, openBlocks[0])
+ return openBlocks
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/fenced_code.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/fenced_code.go
new file mode 100644
index 00000000..8b2ebd4f
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/fenced_code.go
@@ -0,0 +1,112 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type FencedCodeLine struct {
+ Indentation int
+ Range Range
+}
+
+type FencedCode struct {
+ blockBase
+ markdown string
+ didSeeClosingFence bool
+
+ Indentation int
+ OpeningFence Range
+ RawInfo Range
+ RawCode []FencedCodeLine
+}
+
+func (b *FencedCode) Code() (result string) {
+ for _, code := range b.RawCode {
+ result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
+ }
+ return
+}
+
+func (b *FencedCode) Info() string {
+ return Unescape(b.markdown[b.RawInfo.Position:b.RawInfo.End])
+}
+
+func (b *FencedCode) Continuation(indentation int, r Range) *continuation {
+ if b.didSeeClosingFence {
+ return nil
+ }
+ return &continuation{
+ Indentation: indentation,
+ Remaining: r,
+ }
+}
+
+func (b *FencedCode) AddLine(indentation int, r Range) bool {
+ s := b.markdown[r.Position:r.End]
+ if indentation <= 3 && strings.HasPrefix(s, b.markdown[b.OpeningFence.Position:b.OpeningFence.End]) {
+ suffix := strings.TrimSpace(s[b.OpeningFence.End-b.OpeningFence.Position:])
+ isClosingFence := true
+ for _, c := range suffix {
+ if c != rune(s[0]) {
+ isClosingFence = false
+ break
+ }
+ }
+ if isClosingFence {
+ b.didSeeClosingFence = true
+ return true
+ }
+ }
+
+ if indentation >= b.Indentation {
+ indentation -= b.Indentation
+ } else {
+ indentation = 0
+ }
+
+ b.RawCode = append(b.RawCode, FencedCodeLine{
+ Indentation: indentation,
+ Range: r,
+ })
+ return true
+}
+
+func (b *FencedCode) AllowsBlockStarts() bool {
+ return false
+}
+
+func fencedCodeStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ s := markdown[r.Position:r.End]
+
+ if !strings.HasPrefix(s, "```") && !strings.HasPrefix(s, "~~~") {
+ return nil
+ }
+
+ fenceCharacter := rune(s[0])
+ fenceLength := 3
+ for _, c := range s[3:] {
+ if c == fenceCharacter {
+ fenceLength++
+ } else {
+ break
+ }
+ }
+
+ for i := r.Position + fenceLength; i < r.End; i++ {
+ if markdown[i] == '`' {
+ return nil
+ }
+ }
+
+ return []Block{
+ &FencedCode{
+ markdown: markdown,
+ Indentation: indentation,
+ RawInfo: trimRightSpace(markdown, Range{r.Position + fenceLength, r.End}),
+ OpeningFence: Range{r.Position, r.Position + fenceLength},
+ },
+ }
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/html.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/html.go
new file mode 100644
index 00000000..afb72bff
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/html.go
@@ -0,0 +1,192 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "fmt"
+ "strings"
+)
+
+var htmlEscaper = strings.NewReplacer(
+ `&`, "&amp;",
+ `<`, "&lt;",
+ `>`, "&gt;",
+ `"`, "&quot;",
+)
+
+// RenderHTML produces HTML with the same behavior as the example renderer used in the CommonMark
+// reference materials except for one slight difference: for brevity, no unnecessary whitespace is
+// inserted between elements. The output is not defined by the CommonMark spec, and it exists
+// primarily as an aid in testing.
+func RenderHTML(markdown string) string {
+ return RenderBlockHTML(Parse(markdown))
+}
+
+func RenderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition) (result string) {
+ return renderBlockHTML(block, referenceDefinitions, false)
+}
+
+func renderBlockHTML(block Block, referenceDefinitions []*ReferenceDefinition, isTightList bool) (result string) {
+ switch v := block.(type) {
+ case *Document:
+ for _, block := range v.Children {
+ result += RenderBlockHTML(block, referenceDefinitions)
+ }
+ case *Paragraph:
+ if len(v.Text) == 0 {
+ return
+ }
+ if !isTightList {
+ result += "<p>"
+ }
+ for _, inline := range v.ParseInlines(referenceDefinitions) {
+ result += RenderInlineHTML(inline)
+ }
+ if !isTightList {
+ result += "</p>"
+ }
+ case *List:
+ if v.IsOrdered {
+ if v.OrderedStart != 1 {
+ result += fmt.Sprintf(`<ol start="%v">`, v.OrderedStart)
+ } else {
+ result += "<ol>"
+ }
+ } else {
+ result += "<ul>"
+ }
+ for _, block := range v.Children {
+ result += renderBlockHTML(block, referenceDefinitions, !v.IsLoose)
+ }
+ if v.IsOrdered {
+ result += "</ol>"
+ } else {
+ result += "</ul>"
+ }
+ case *ListItem:
+ result += "<li>"
+ for _, block := range v.Children {
+ result += renderBlockHTML(block, referenceDefinitions, isTightList)
+ }
+ result += "</li>"
+ case *BlockQuote:
+ result += "<blockquote>"
+ for _, block := range v.Children {
+ result += RenderBlockHTML(block, referenceDefinitions)
+ }
+ result += "</blockquote>"
+ case *FencedCode:
+ if info := v.Info(); info != "" {
+ language := strings.Fields(info)[0]
+ result += `<pre><code class="language-` + htmlEscaper.Replace(language) + `">`
+ } else {
+ result += "<pre><code>"
+ }
+ result += htmlEscaper.Replace(v.Code()) + "</code></pre>"
+ case *IndentedCode:
+ result += "<pre><code>" + htmlEscaper.Replace(v.Code()) + "</code></pre>"
+ default:
+ panic(fmt.Sprintf("missing case for type %T", v))
+ }
+ return
+}
+
+func escapeURL(url string) (result string) {
+ for i := 0; i < len(url); {
+ switch b := url[i]; b {
+ case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '-', '_', '.', '!', '~', '*', '\'', '(', ')', '#':
+ result += string(b)
+ i++
+ default:
+ if b == '%' && i+2 < len(url) && isHexByte(url[i+1]) && isHexByte(url[i+2]) {
+ result += url[i : i+3]
+ i += 3
+ } else if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') {
+ result += string(b)
+ i++
+ } else {
+ result += fmt.Sprintf("%%%0X", b)
+ i++
+ }
+ }
+ }
+ return
+}
+
+func RenderInlineHTML(inline Inline) (result string) {
+ switch v := inline.(type) {
+ case *Text:
+ return htmlEscaper.Replace(v.Text)
+ case *HardLineBreak:
+ return "<br />"
+ case *SoftLineBreak:
+ return "\n"
+ case *CodeSpan:
+ return "<code>" + htmlEscaper.Replace(v.Code) + "</code>"
+ case *InlineImage:
+ result += `<img src="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `" alt="` + htmlEscaper.Replace(renderImageAltText(v.Children)) + `"`
+ if title := v.Title(); title != "" {
+ result += ` title="` + htmlEscaper.Replace(title) + `"`
+ }
+ result += ` />`
+ case *ReferenceImage:
+ result += `<img src="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `" alt="` + htmlEscaper.Replace(renderImageAltText(v.Children)) + `"`
+ if title := v.Title(); title != "" {
+ result += ` title="` + htmlEscaper.Replace(title) + `"`
+ }
+ result += ` />`
+ case *InlineLink:
+ result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
+ if title := v.Title(); title != "" {
+ result += ` title="` + htmlEscaper.Replace(title) + `"`
+ }
+ result += `>`
+ for _, inline := range v.Children {
+ result += RenderInlineHTML(inline)
+ }
+ result += "</a>"
+ case *ReferenceLink:
+ result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `"`
+ if title := v.Title(); title != "" {
+ result += ` title="` + htmlEscaper.Replace(title) + `"`
+ }
+ result += `>`
+ for _, inline := range v.Children {
+ result += RenderInlineHTML(inline)
+ }
+ result += "</a>"
+ case *Autolink:
+ result += `<a href="` + htmlEscaper.Replace(escapeURL(v.Destination())) + `">`
+ for _, inline := range v.Children {
+ result += RenderInlineHTML(inline)
+ }
+ result += "</a>"
+ default:
+ panic(fmt.Sprintf("missing case for type %T", v))
+ }
+ return
+}
+
+func renderImageAltText(children []Inline) (result string) {
+ for _, inline := range children {
+ result += renderImageChildAltText(inline)
+ }
+ return
+}
+
+func renderImageChildAltText(inline Inline) (result string) {
+ switch v := inline.(type) {
+ case *Text:
+ return v.Text
+ case *InlineImage:
+ for _, inline := range v.Children {
+ result += renderImageChildAltText(inline)
+ }
+ case *InlineLink:
+ for _, inline := range v.Children {
+ result += renderImageChildAltText(inline)
+ }
+ }
+ return
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/html_entities.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/html_entities.go
new file mode 100644
index 00000000..8fe24811
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/html_entities.go
@@ -0,0 +1,2132 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+var htmlEntities = map[string]string{
+ "AElig": "\u00C6",
+ "AMP": "\u0026",
+ "Aacute": "\u00C1",
+ "Abreve": "\u0102",
+ "Acirc": "\u00C2",
+ "Acy": "\u0410",
+ "Afr": "\U0001D504",
+ "Agrave": "\u00C0",
+ "Alpha": "\u0391",
+ "Amacr": "\u0100",
+ "And": "\u2A53",
+ "Aogon": "\u0104",
+ "Aopf": "\U0001D538",
+ "ApplyFunction": "\u2061",
+ "Aring": "\u00C5",
+ "Ascr": "\U0001D49C",
+ "Assign": "\u2254",
+ "Atilde": "\u00C3",
+ "Auml": "\u00C4",
+ "Backslash": "\u2216",
+ "Barv": "\u2AE7",
+ "Barwed": "\u2306",
+ "Bcy": "\u0411",
+ "Because": "\u2235",
+ "Bernoullis": "\u212C",
+ "Beta": "\u0392",
+ "Bfr": "\U0001D505",
+ "Bopf": "\U0001D539",
+ "Breve": "\u02D8",
+ "Bscr": "\u212C",
+ "Bumpeq": "\u224E",
+ "CHcy": "\u0427",
+ "COPY": "\u00A9",
+ "Cacute": "\u0106",
+ "Cap": "\u22D2",
+ "CapitalDifferentialD": "\u2145",
+ "Cayleys": "\u212D",
+ "Ccaron": "\u010C",
+ "Ccedil": "\u00C7",
+ "Ccirc": "\u0108",
+ "Cconint": "\u2230",
+ "Cdot": "\u010A",
+ "Cedilla": "\u00B8",
+ "CenterDot": "\u00B7",
+ "Cfr": "\u212D",
+ "Chi": "\u03A7",
+ "CircleDot": "\u2299",
+ "CircleMinus": "\u2296",
+ "CirclePlus": "\u2295",
+ "CircleTimes": "\u2297",
+ "ClockwiseContourIntegral": "\u2232",
+ "CloseCurlyDoubleQuote": "\u201D",
+ "CloseCurlyQuote": "\u2019",
+ "Colon": "\u2237",
+ "Colone": "\u2A74",
+ "Congruent": "\u2261",
+ "Conint": "\u222F",
+ "ContourIntegral": "\u222E",
+ "Copf": "\u2102",
+ "Coproduct": "\u2210",
+ "CounterClockwiseContourIntegral": "\u2233",
+ "Cross": "\u2A2F",
+ "Cscr": "\U0001D49E",
+ "Cup": "\u22D3",
+ "CupCap": "\u224D",
+ "DD": "\u2145",
+ "DDotrahd": "\u2911",
+ "DJcy": "\u0402",
+ "DScy": "\u0405",
+ "DZcy": "\u040F",
+ "Dagger": "\u2021",
+ "Darr": "\u21A1",
+ "Dashv": "\u2AE4",
+ "Dcaron": "\u010E",
+ "Dcy": "\u0414",
+ "Del": "\u2207",
+ "Delta": "\u0394",
+ "Dfr": "\U0001D507",
+ "DiacriticalAcute": "\u00B4",
+ "DiacriticalDot": "\u02D9",
+ "DiacriticalDoubleAcute": "\u02DD",
+ "DiacriticalGrave": "\u0060",
+ "DiacriticalTilde": "\u02DC",
+ "Diamond": "\u22C4",
+ "DifferentialD": "\u2146",
+ "Dopf": "\U0001D53B",
+ "Dot": "\u00A8",
+ "DotDot": "\u20DC",
+ "DotEqual": "\u2250",
+ "DoubleContourIntegral": "\u222F",
+ "DoubleDot": "\u00A8",
+ "DoubleDownArrow": "\u21D3",
+ "DoubleLeftArrow": "\u21D0",
+ "DoubleLeftRightArrow": "\u21D4",
+ "DoubleLeftTee": "\u2AE4",
+ "DoubleLongLeftArrow": "\u27F8",
+ "DoubleLongLeftRightArrow": "\u27FA",
+ "DoubleLongRightArrow": "\u27F9",
+ "DoubleRightArrow": "\u21D2",
+ "DoubleRightTee": "\u22A8",
+ "DoubleUpArrow": "\u21D1",
+ "DoubleUpDownArrow": "\u21D5",
+ "DoubleVerticalBar": "\u2225",
+ "DownArrow": "\u2193",
+ "DownArrowBar": "\u2913",
+ "DownArrowUpArrow": "\u21F5",
+ "DownBreve": "\u0311",
+ "DownLeftRightVector": "\u2950",
+ "DownLeftTeeVector": "\u295E",
+ "DownLeftVector": "\u21BD",
+ "DownLeftVectorBar": "\u2956",
+ "DownRightTeeVector": "\u295F",
+ "DownRightVector": "\u21C1",
+ "DownRightVectorBar": "\u2957",
+ "DownTee": "\u22A4",
+ "DownTeeArrow": "\u21A7",
+ "Downarrow": "\u21D3",
+ "Dscr": "\U0001D49F",
+ "Dstrok": "\u0110",
+ "ENG": "\u014A",
+ "ETH": "\u00D0",
+ "Eacute": "\u00C9",
+ "Ecaron": "\u011A",
+ "Ecirc": "\u00CA",
+ "Ecy": "\u042D",
+ "Edot": "\u0116",
+ "Efr": "\U0001D508",
+ "Egrave": "\u00C8",
+ "Element": "\u2208",
+ "Emacr": "\u0112",
+ "EmptySmallSquare": "\u25FB",
+ "EmptyVerySmallSquare": "\u25AB",
+ "Eogon": "\u0118",
+ "Eopf": "\U0001D53C",
+ "Epsilon": "\u0395",
+ "Equal": "\u2A75",
+ "EqualTilde": "\u2242",
+ "Equilibrium": "\u21CC",
+ "Escr": "\u2130",
+ "Esim": "\u2A73",
+ "Eta": "\u0397",
+ "Euml": "\u00CB",
+ "Exists": "\u2203",
+ "ExponentialE": "\u2147",
+ "Fcy": "\u0424",
+ "Ffr": "\U0001D509",
+ "FilledSmallSquare": "\u25FC",
+ "FilledVerySmallSquare": "\u25AA",
+ "Fopf": "\U0001D53D",
+ "ForAll": "\u2200",
+ "Fouriertrf": "\u2131",
+ "Fscr": "\u2131",
+ "GJcy": "\u0403",
+ "GT": "\u003E",
+ "Gamma": "\u0393",
+ "Gammad": "\u03DC",
+ "Gbreve": "\u011E",
+ "Gcedil": "\u0122",
+ "Gcirc": "\u011C",
+ "Gcy": "\u0413",
+ "Gdot": "\u0120",
+ "Gfr": "\U0001D50A",
+ "Gg": "\u22D9",
+ "Gopf": "\U0001D53E",
+ "GreaterEqual": "\u2265",
+ "GreaterEqualLess": "\u22DB",
+ "GreaterFullEqual": "\u2267",
+ "GreaterGreater": "\u2AA2",
+ "GreaterLess": "\u2277",
+ "GreaterSlantEqual": "\u2A7E",
+ "GreaterTilde": "\u2273",
+ "Gscr": "\U0001D4A2",
+ "Gt": "\u226B",
+ "HARDcy": "\u042A",
+ "Hacek": "\u02C7",
+ "Hat": "\u005E",
+ "Hcirc": "\u0124",
+ "Hfr": "\u210C",
+ "HilbertSpace": "\u210B",
+ "Hopf": "\u210D",
+ "HorizontalLine": "\u2500",
+ "Hscr": "\u210B",
+ "Hstrok": "\u0126",
+ "HumpDownHump": "\u224E",
+ "HumpEqual": "\u224F",
+ "IEcy": "\u0415",
+ "IJlig": "\u0132",
+ "IOcy": "\u0401",
+ "Iacute": "\u00CD",
+ "Icirc": "\u00CE",
+ "Icy": "\u0418",
+ "Idot": "\u0130",
+ "Ifr": "\u2111",
+ "Igrave": "\u00CC",
+ "Im": "\u2111",
+ "Imacr": "\u012A",
+ "ImaginaryI": "\u2148",
+ "Implies": "\u21D2",
+ "Int": "\u222C",
+ "Integral": "\u222B",
+ "Intersection": "\u22C2",
+ "InvisibleComma": "\u2063",
+ "InvisibleTimes": "\u2062",
+ "Iogon": "\u012E",
+ "Iopf": "\U0001D540",
+ "Iota": "\u0399",
+ "Iscr": "\u2110",
+ "Itilde": "\u0128",
+ "Iukcy": "\u0406",
+ "Iuml": "\u00CF",
+ "Jcirc": "\u0134",
+ "Jcy": "\u0419",
+ "Jfr": "\U0001D50D",
+ "Jopf": "\U0001D541",
+ "Jscr": "\U0001D4A5",
+ "Jsercy": "\u0408",
+ "Jukcy": "\u0404",
+ "KHcy": "\u0425",
+ "KJcy": "\u040C",
+ "Kappa": "\u039A",
+ "Kcedil": "\u0136",
+ "Kcy": "\u041A",
+ "Kfr": "\U0001D50E",
+ "Kopf": "\U0001D542",
+ "Kscr": "\U0001D4A6",
+ "LJcy": "\u0409",
+ "LT": "\u003C",
+ "Lacute": "\u0139",
+ "Lambda": "\u039B",
+ "Lang": "\u27EA",
+ "Laplacetrf": "\u2112",
+ "Larr": "\u219E",
+ "Lcaron": "\u013D",
+ "Lcedil": "\u013B",
+ "Lcy": "\u041B",
+ "LeftAngleBracket": "\u27E8",
+ "LeftArrow": "\u2190",
+ "LeftArrowBar": "\u21E4",
+ "LeftArrowRightArrow": "\u21C6",
+ "LeftCeiling": "\u2308",
+ "LeftDoubleBracket": "\u27E6",
+ "LeftDownTeeVector": "\u2961",
+ "LeftDownVector": "\u21C3",
+ "LeftDownVectorBar": "\u2959",
+ "LeftFloor": "\u230A",
+ "LeftRightArrow": "\u2194",
+ "LeftRightVector": "\u294E",
+ "LeftTee": "\u22A3",
+ "LeftTeeArrow": "\u21A4",
+ "LeftTeeVector": "\u295A",
+ "LeftTriangle": "\u22B2",
+ "LeftTriangleBar": "\u29CF",
+ "LeftTriangleEqual": "\u22B4",
+ "LeftUpDownVector": "\u2951",
+ "LeftUpTeeVector": "\u2960",
+ "LeftUpVector": "\u21BF",
+ "LeftUpVectorBar": "\u2958",
+ "LeftVector": "\u21BC",
+ "LeftVectorBar": "\u2952",
+ "Leftarrow": "\u21D0",
+ "Leftrightarrow": "\u21D4",
+ "LessEqualGreater": "\u22DA",
+ "LessFullEqual": "\u2266",
+ "LessGreater": "\u2276",
+ "LessLess": "\u2AA1",
+ "LessSlantEqual": "\u2A7D",
+ "LessTilde": "\u2272",
+ "Lfr": "\U0001D50F",
+ "Ll": "\u22D8",
+ "Lleftarrow": "\u21DA",
+ "Lmidot": "\u013F",
+ "LongLeftArrow": "\u27F5",
+ "LongLeftRightArrow": "\u27F7",
+ "LongRightArrow": "\u27F6",
+ "Longleftarrow": "\u27F8",
+ "Longleftrightarrow": "\u27FA",
+ "Longrightarrow": "\u27F9",
+ "Lopf": "\U0001D543",
+ "LowerLeftArrow": "\u2199",
+ "LowerRightArrow": "\u2198",
+ "Lscr": "\u2112",
+ "Lsh": "\u21B0",
+ "Lstrok": "\u0141",
+ "Lt": "\u226A",
+ "Map": "\u2905",
+ "Mcy": "\u041C",
+ "MediumSpace": "\u205F",
+ "Mellintrf": "\u2133",
+ "Mfr": "\U0001D510",
+ "MinusPlus": "\u2213",
+ "Mopf": "\U0001D544",
+ "Mscr": "\u2133",
+ "Mu": "\u039C",
+ "NJcy": "\u040A",
+ "Nacute": "\u0143",
+ "Ncaron": "\u0147",
+ "Ncedil": "\u0145",
+ "Ncy": "\u041D",
+ "NegativeMediumSpace": "\u200B",
+ "NegativeThickSpace": "\u200B",
+ "NegativeThinSpace": "\u200B",
+ "NegativeVeryThinSpace": "\u200B",
+ "NestedGreaterGreater": "\u226B",
+ "NestedLessLess": "\u226A",
+ "NewLine": "\u000A",
+ "Nfr": "\U0001D511",
+ "NoBreak": "\u2060",
+ "NonBreakingSpace": "\u00A0",
+ "Nopf": "\u2115",
+ "Not": "\u2AEC",
+ "NotCongruent": "\u2262",
+ "NotCupCap": "\u226D",
+ "NotDoubleVerticalBar": "\u2226",
+ "NotElement": "\u2209",
+ "NotEqual": "\u2260",
+ "NotEqualTilde": "\u2242\u0338",
+ "NotExists": "\u2204",
+ "NotGreater": "\u226F",
+ "NotGreaterEqual": "\u2271",
+ "NotGreaterFullEqual": "\u2267\u0338",
+ "NotGreaterGreater": "\u226B\u0338",
+ "NotGreaterLess": "\u2279",
+ "NotGreaterSlantEqual": "\u2A7E\u0338",
+ "NotGreaterTilde": "\u2275",
+ "NotHumpDownHump": "\u224E\u0338",
+ "NotHumpEqual": "\u224F\u0338",
+ "NotLeftTriangle": "\u22EA",
+ "NotLeftTriangleBar": "\u29CF\u0338",
+ "NotLeftTriangleEqual": "\u22EC",
+ "NotLess": "\u226E",
+ "NotLessEqual": "\u2270",
+ "NotLessGreater": "\u2278",
+ "NotLessLess": "\u226A\u0338",
+ "NotLessSlantEqual": "\u2A7D\u0338",
+ "NotLessTilde": "\u2274",
+ "NotNestedGreaterGreater": "\u2AA2\u0338",
+ "NotNestedLessLess": "\u2AA1\u0338",
+ "NotPrecedes": "\u2280",
+ "NotPrecedesEqual": "\u2AAF\u0338",
+ "NotPrecedesSlantEqual": "\u22E0",
+ "NotReverseElement": "\u220C",
+ "NotRightTriangle": "\u22EB",
+ "NotRightTriangleBar": "\u29D0\u0338",
+ "NotRightTriangleEqual": "\u22ED",
+ "NotSquareSubset": "\u228F\u0338",
+ "NotSquareSubsetEqual": "\u22E2",
+ "NotSquareSuperset": "\u2290\u0338",
+ "NotSquareSupersetEqual": "\u22E3",
+ "NotSubset": "\u2282\u20D2",
+ "NotSubsetEqual": "\u2288",
+ "NotSucceeds": "\u2281",
+ "NotSucceedsEqual": "\u2AB0\u0338",
+ "NotSucceedsSlantEqual": "\u22E1",
+ "NotSucceedsTilde": "\u227F\u0338",
+ "NotSuperset": "\u2283\u20D2",
+ "NotSupersetEqual": "\u2289",
+ "NotTilde": "\u2241",
+ "NotTildeEqual": "\u2244",
+ "NotTildeFullEqual": "\u2247",
+ "NotTildeTilde": "\u2249",
+ "NotVerticalBar": "\u2224",
+ "Nscr": "\U0001D4A9",
+ "Ntilde": "\u00D1",
+ "Nu": "\u039D",
+ "OElig": "\u0152",
+ "Oacute": "\u00D3",
+ "Ocirc": "\u00D4",
+ "Ocy": "\u041E",
+ "Odblac": "\u0150",
+ "Ofr": "\U0001D512",
+ "Ograve": "\u00D2",
+ "Omacr": "\u014C",
+ "Omega": "\u03A9",
+ "Omicron": "\u039F",
+ "Oopf": "\U0001D546",
+ "OpenCurlyDoubleQuote": "\u201C",
+ "OpenCurlyQuote": "\u2018",
+ "Or": "\u2A54",
+ "Oscr": "\U0001D4AA",
+ "Oslash": "\u00D8",
+ "Otilde": "\u00D5",
+ "Otimes": "\u2A37",
+ "Ouml": "\u00D6",
+ "OverBar": "\u203E",
+ "OverBrace": "\u23DE",
+ "OverBracket": "\u23B4",
+ "OverParenthesis": "\u23DC",
+ "PartialD": "\u2202",
+ "Pcy": "\u041F",
+ "Pfr": "\U0001D513",
+ "Phi": "\u03A6",
+ "Pi": "\u03A0",
+ "PlusMinus": "\u00B1",
+ "Poincareplane": "\u210C",
+ "Popf": "\u2119",
+ "Pr": "\u2ABB",
+ "Precedes": "\u227A",
+ "PrecedesEqual": "\u2AAF",
+ "PrecedesSlantEqual": "\u227C",
+ "PrecedesTilde": "\u227E",
+ "Prime": "\u2033",
+ "Product": "\u220F",
+ "Proportion": "\u2237",
+ "Proportional": "\u221D",
+ "Pscr": "\U0001D4AB",
+ "Psi": "\u03A8",
+ "QUOT": "\u0022",
+ "Qfr": "\U0001D514",
+ "Qopf": "\u211A",
+ "Qscr": "\U0001D4AC",
+ "RBarr": "\u2910",
+ "REG": "\u00AE",
+ "Racute": "\u0154",
+ "Rang": "\u27EB",
+ "Rarr": "\u21A0",
+ "Rarrtl": "\u2916",
+ "Rcaron": "\u0158",
+ "Rcedil": "\u0156",
+ "Rcy": "\u0420",
+ "Re": "\u211C",
+ "ReverseElement": "\u220B",
+ "ReverseEquilibrium": "\u21CB",
+ "ReverseUpEquilibrium": "\u296F",
+ "Rfr": "\u211C",
+ "Rho": "\u03A1",
+ "RightAngleBracket": "\u27E9",
+ "RightArrow": "\u2192",
+ "RightArrowBar": "\u21E5",
+ "RightArrowLeftArrow": "\u21C4",
+ "RightCeiling": "\u2309",
+ "RightDoubleBracket": "\u27E7",
+ "RightDownTeeVector": "\u295D",
+ "RightDownVector": "\u21C2",
+ "RightDownVectorBar": "\u2955",
+ "RightFloor": "\u230B",
+ "RightTee": "\u22A2",
+ "RightTeeArrow": "\u21A6",
+ "RightTeeVector": "\u295B",
+ "RightTriangle": "\u22B3",
+ "RightTriangleBar": "\u29D0",
+ "RightTriangleEqual": "\u22B5",
+ "RightUpDownVector": "\u294F",
+ "RightUpTeeVector": "\u295C",
+ "RightUpVector": "\u21BE",
+ "RightUpVectorBar": "\u2954",
+ "RightVector": "\u21C0",
+ "RightVectorBar": "\u2953",
+ "Rightarrow": "\u21D2",
+ "Ropf": "\u211D",
+ "RoundImplies": "\u2970",
+ "Rrightarrow": "\u21DB",
+ "Rscr": "\u211B",
+ "Rsh": "\u21B1",
+ "RuleDelayed": "\u29F4",
+ "SHCHcy": "\u0429",
+ "SHcy": "\u0428",
+ "SOFTcy": "\u042C",
+ "Sacute": "\u015A",
+ "Sc": "\u2ABC",
+ "Scaron": "\u0160",
+ "Scedil": "\u015E",
+ "Scirc": "\u015C",
+ "Scy": "\u0421",
+ "Sfr": "\U0001D516",
+ "ShortDownArrow": "\u2193",
+ "ShortLeftArrow": "\u2190",
+ "ShortRightArrow": "\u2192",
+ "ShortUpArrow": "\u2191",
+ "Sigma": "\u03A3",
+ "SmallCircle": "\u2218",
+ "Sopf": "\U0001D54A",
+ "Sqrt": "\u221A",
+ "Square": "\u25A1",
+ "SquareIntersection": "\u2293",
+ "SquareSubset": "\u228F",
+ "SquareSubsetEqual": "\u2291",
+ "SquareSuperset": "\u2290",
+ "SquareSupersetEqual": "\u2292",
+ "SquareUnion": "\u2294",
+ "Sscr": "\U0001D4AE",
+ "Star": "\u22C6",
+ "Sub": "\u22D0",
+ "Subset": "\u22D0",
+ "SubsetEqual": "\u2286",
+ "Succeeds": "\u227B",
+ "SucceedsEqual": "\u2AB0",
+ "SucceedsSlantEqual": "\u227D",
+ "SucceedsTilde": "\u227F",
+ "SuchThat": "\u220B",
+ "Sum": "\u2211",
+ "Sup": "\u22D1",
+ "Superset": "\u2283",
+ "SupersetEqual": "\u2287",
+ "Supset": "\u22D1",
+ "THORN": "\u00DE",
+ "TRADE": "\u2122",
+ "TSHcy": "\u040B",
+ "TScy": "\u0426",
+ "Tab": "\u0009",
+ "Tau": "\u03A4",
+ "Tcaron": "\u0164",
+ "Tcedil": "\u0162",
+ "Tcy": "\u0422",
+ "Tfr": "\U0001D517",
+ "Therefore": "\u2234",
+ "Theta": "\u0398",
+ "ThickSpace": "\u205F\u200A",
+ "ThinSpace": "\u2009",
+ "Tilde": "\u223C",
+ "TildeEqual": "\u2243",
+ "TildeFullEqual": "\u2245",
+ "TildeTilde": "\u2248",
+ "Topf": "\U0001D54B",
+ "TripleDot": "\u20DB",
+ "Tscr": "\U0001D4AF",
+ "Tstrok": "\u0166",
+ "Uacute": "\u00DA",
+ "Uarr": "\u219F",
+ "Uarrocir": "\u2949",
+ "Ubrcy": "\u040E",
+ "Ubreve": "\u016C",
+ "Ucirc": "\u00DB",
+ "Ucy": "\u0423",
+ "Udblac": "\u0170",
+ "Ufr": "\U0001D518",
+ "Ugrave": "\u00D9",
+ "Umacr": "\u016A",
+ "UnderBar": "\u005F",
+ "UnderBrace": "\u23DF",
+ "UnderBracket": "\u23B5",
+ "UnderParenthesis": "\u23DD",
+ "Union": "\u22C3",
+ "UnionPlus": "\u228E",
+ "Uogon": "\u0172",
+ "Uopf": "\U0001D54C",
+ "UpArrow": "\u2191",
+ "UpArrowBar": "\u2912",
+ "UpArrowDownArrow": "\u21C5",
+ "UpDownArrow": "\u2195",
+ "UpEquilibrium": "\u296E",
+ "UpTee": "\u22A5",
+ "UpTeeArrow": "\u21A5",
+ "Uparrow": "\u21D1",
+ "Updownarrow": "\u21D5",
+ "UpperLeftArrow": "\u2196",
+ "UpperRightArrow": "\u2197",
+ "Upsi": "\u03D2",
+ "Upsilon": "\u03A5",
+ "Uring": "\u016E",
+ "Uscr": "\U0001D4B0",
+ "Utilde": "\u0168",
+ "Uuml": "\u00DC",
+ "VDash": "\u22AB",
+ "Vbar": "\u2AEB",
+ "Vcy": "\u0412",
+ "Vdash": "\u22A9",
+ "Vdashl": "\u2AE6",
+ "Vee": "\u22C1",
+ "Verbar": "\u2016",
+ "Vert": "\u2016",
+ "VerticalBar": "\u2223",
+ "VerticalLine": "\u007C",
+ "VerticalSeparator": "\u2758",
+ "VerticalTilde": "\u2240",
+ "VeryThinSpace": "\u200A",
+ "Vfr": "\U0001D519",
+ "Vopf": "\U0001D54D",
+ "Vscr": "\U0001D4B1",
+ "Vvdash": "\u22AA",
+ "Wcirc": "\u0174",
+ "Wedge": "\u22C0",
+ "Wfr": "\U0001D51A",
+ "Wopf": "\U0001D54E",
+ "Wscr": "\U0001D4B2",
+ "Xfr": "\U0001D51B",
+ "Xi": "\u039E",
+ "Xopf": "\U0001D54F",
+ "Xscr": "\U0001D4B3",
+ "YAcy": "\u042F",
+ "YIcy": "\u0407",
+ "YUcy": "\u042E",
+ "Yacute": "\u00DD",
+ "Ycirc": "\u0176",
+ "Ycy": "\u042B",
+ "Yfr": "\U0001D51C",
+ "Yopf": "\U0001D550",
+ "Yscr": "\U0001D4B4",
+ "Yuml": "\u0178",
+ "ZHcy": "\u0416",
+ "Zacute": "\u0179",
+ "Zcaron": "\u017D",
+ "Zcy": "\u0417",
+ "Zdot": "\u017B",
+ "ZeroWidthSpace": "\u200B",
+ "Zeta": "\u0396",
+ "Zfr": "\u2128",
+ "Zopf": "\u2124",
+ "Zscr": "\U0001D4B5",
+ "aacute": "\u00E1",
+ "abreve": "\u0103",
+ "ac": "\u223E",
+ "acE": "\u223E\u0333",
+ "acd": "\u223F",
+ "acirc": "\u00E2",
+ "acute": "\u00B4",
+ "acy": "\u0430",
+ "aelig": "\u00E6",
+ "af": "\u2061",
+ "afr": "\U0001D51E",
+ "agrave": "\u00E0",
+ "alefsym": "\u2135",
+ "aleph": "\u2135",
+ "alpha": "\u03B1",
+ "amacr": "\u0101",
+ "amalg": "\u2A3F",
+ "amp": "\u0026",
+ "and": "\u2227",
+ "andand": "\u2A55",
+ "andd": "\u2A5C",
+ "andslope": "\u2A58",
+ "andv": "\u2A5A",
+ "ang": "\u2220",
+ "ange": "\u29A4",
+ "angle": "\u2220",
+ "angmsd": "\u2221",
+ "angmsdaa": "\u29A8",
+ "angmsdab": "\u29A9",
+ "angmsdac": "\u29AA",
+ "angmsdad": "\u29AB",
+ "angmsdae": "\u29AC",
+ "angmsdaf": "\u29AD",
+ "angmsdag": "\u29AE",
+ "angmsdah": "\u29AF",
+ "angrt": "\u221F",
+ "angrtvb": "\u22BE",
+ "angrtvbd": "\u299D",
+ "angsph": "\u2222",
+ "angst": "\u00C5",
+ "angzarr": "\u237C",
+ "aogon": "\u0105",
+ "aopf": "\U0001D552",
+ "ap": "\u2248",
+ "apE": "\u2A70",
+ "apacir": "\u2A6F",
+ "ape": "\u224A",
+ "apid": "\u224B",
+ "apos": "\u0027",
+ "approx": "\u2248",
+ "approxeq": "\u224A",
+ "aring": "\u00E5",
+ "ascr": "\U0001D4B6",
+ "ast": "\u002A",
+ "asymp": "\u2248",
+ "asympeq": "\u224D",
+ "atilde": "\u00E3",
+ "auml": "\u00E4",
+ "awconint": "\u2233",
+ "awint": "\u2A11",
+ "bNot": "\u2AED",
+ "backcong": "\u224C",
+ "backepsilon": "\u03F6",
+ "backprime": "\u2035",
+ "backsim": "\u223D",
+ "backsimeq": "\u22CD",
+ "barvee": "\u22BD",
+ "barwed": "\u2305",
+ "barwedge": "\u2305",
+ "bbrk": "\u23B5",
+ "bbrktbrk": "\u23B6",
+ "bcong": "\u224C",
+ "bcy": "\u0431",
+ "bdquo": "\u201E",
+ "becaus": "\u2235",
+ "because": "\u2235",
+ "bemptyv": "\u29B0",
+ "bepsi": "\u03F6",
+ "bernou": "\u212C",
+ "beta": "\u03B2",
+ "beth": "\u2136",
+ "between": "\u226C",
+ "bfr": "\U0001D51F",
+ "bigcap": "\u22C2",
+ "bigcirc": "\u25EF",
+ "bigcup": "\u22C3",
+ "bigodot": "\u2A00",
+ "bigoplus": "\u2A01",
+ "bigotimes": "\u2A02",
+ "bigsqcup": "\u2A06",
+ "bigstar": "\u2605",
+ "bigtriangledown": "\u25BD",
+ "bigtriangleup": "\u25B3",
+ "biguplus": "\u2A04",
+ "bigvee": "\u22C1",
+ "bigwedge": "\u22C0",
+ "bkarow": "\u290D",
+ "blacklozenge": "\u29EB",
+ "blacksquare": "\u25AA",
+ "blacktriangle": "\u25B4",
+ "blacktriangledown": "\u25BE",
+ "blacktriangleleft": "\u25C2",
+ "blacktriangleright": "\u25B8",
+ "blank": "\u2423",
+ "blk12": "\u2592",
+ "blk14": "\u2591",
+ "blk34": "\u2593",
+ "block": "\u2588",
+ "bne": "\u003D\u20E5",
+ "bnequiv": "\u2261\u20E5",
+ "bnot": "\u2310",
+ "bopf": "\U0001D553",
+ "bot": "\u22A5",
+ "bottom": "\u22A5",
+ "bowtie": "\u22C8",
+ "boxDL": "\u2557",
+ "boxDR": "\u2554",
+ "boxDl": "\u2556",
+ "boxDr": "\u2553",
+ "boxH": "\u2550",
+ "boxHD": "\u2566",
+ "boxHU": "\u2569",
+ "boxHd": "\u2564",
+ "boxHu": "\u2567",
+ "boxUL": "\u255D",
+ "boxUR": "\u255A",
+ "boxUl": "\u255C",
+ "boxUr": "\u2559",
+ "boxV": "\u2551",
+ "boxVH": "\u256C",
+ "boxVL": "\u2563",
+ "boxVR": "\u2560",
+ "boxVh": "\u256B",
+ "boxVl": "\u2562",
+ "boxVr": "\u255F",
+ "boxbox": "\u29C9",
+ "boxdL": "\u2555",
+ "boxdR": "\u2552",
+ "boxdl": "\u2510",
+ "boxdr": "\u250C",
+ "boxh": "\u2500",
+ "boxhD": "\u2565",
+ "boxhU": "\u2568",
+ "boxhd": "\u252C",
+ "boxhu": "\u2534",
+ "boxminus": "\u229F",
+ "boxplus": "\u229E",
+ "boxtimes": "\u22A0",
+ "boxuL": "\u255B",
+ "boxuR": "\u2558",
+ "boxul": "\u2518",
+ "boxur": "\u2514",
+ "boxv": "\u2502",
+ "boxvH": "\u256A",
+ "boxvL": "\u2561",
+ "boxvR": "\u255E",
+ "boxvh": "\u253C",
+ "boxvl": "\u2524",
+ "boxvr": "\u251C",
+ "bprime": "\u2035",
+ "breve": "\u02D8",
+ "brvbar": "\u00A6",
+ "bscr": "\U0001D4B7",
+ "bsemi": "\u204F",
+ "bsim": "\u223D",
+ "bsime": "\u22CD",
+ "bsol": "\u005C",
+ "bsolb": "\u29C5",
+ "bsolhsub": "\u27C8",
+ "bull": "\u2022",
+ "bullet": "\u2022",
+ "bump": "\u224E",
+ "bumpE": "\u2AAE",
+ "bumpe": "\u224F",
+ "bumpeq": "\u224F",
+ "cacute": "\u0107",
+ "cap": "\u2229",
+ "capand": "\u2A44",
+ "capbrcup": "\u2A49",
+ "capcap": "\u2A4B",
+ "capcup": "\u2A47",
+ "capdot": "\u2A40",
+ "caps": "\u2229\uFE00",
+ "caret": "\u2041",
+ "caron": "\u02C7",
+ "ccaps": "\u2A4D",
+ "ccaron": "\u010D",
+ "ccedil": "\u00E7",
+ "ccirc": "\u0109",
+ "ccups": "\u2A4C",
+ "ccupssm": "\u2A50",
+ "cdot": "\u010B",
+ "cedil": "\u00B8",
+ "cemptyv": "\u29B2",
+ "cent": "\u00A2",
+ "centerdot": "\u00B7",
+ "cfr": "\U0001D520",
+ "chcy": "\u0447",
+ "check": "\u2713",
+ "checkmark": "\u2713",
+ "chi": "\u03C7",
+ "cir": "\u25CB",
+ "cirE": "\u29C3",
+ "circ": "\u02C6",
+ "circeq": "\u2257",
+ "circlearrowleft": "\u21BA",
+ "circlearrowright": "\u21BB",
+ "circledR": "\u00AE",
+ "circledS": "\u24C8",
+ "circledast": "\u229B",
+ "circledcirc": "\u229A",
+ "circleddash": "\u229D",
+ "cire": "\u2257",
+ "cirfnint": "\u2A10",
+ "cirmid": "\u2AEF",
+ "cirscir": "\u29C2",
+ "clubs": "\u2663",
+ "clubsuit": "\u2663",
+ "colon": "\u003A",
+ "colone": "\u2254",
+ "coloneq": "\u2254",
+ "comma": "\u002C",
+ "commat": "\u0040",
+ "comp": "\u2201",
+ "compfn": "\u2218",
+ "complement": "\u2201",
+ "complexes": "\u2102",
+ "cong": "\u2245",
+ "congdot": "\u2A6D",
+ "conint": "\u222E",
+ "copf": "\U0001D554",
+ "coprod": "\u2210",
+ "copy": "\u00A9",
+ "copysr": "\u2117",
+ "crarr": "\u21B5",
+ "cross": "\u2717",
+ "cscr": "\U0001D4B8",
+ "csub": "\u2ACF",
+ "csube": "\u2AD1",
+ "csup": "\u2AD0",
+ "csupe": "\u2AD2",
+ "ctdot": "\u22EF",
+ "cudarrl": "\u2938",
+ "cudarrr": "\u2935",
+ "cuepr": "\u22DE",
+ "cuesc": "\u22DF",
+ "cularr": "\u21B6",
+ "cularrp": "\u293D",
+ "cup": "\u222A",
+ "cupbrcap": "\u2A48",
+ "cupcap": "\u2A46",
+ "cupcup": "\u2A4A",
+ "cupdot": "\u228D",
+ "cupor": "\u2A45",
+ "cups": "\u222A\uFE00",
+ "curarr": "\u21B7",
+ "curarrm": "\u293C",
+ "curlyeqprec": "\u22DE",
+ "curlyeqsucc": "\u22DF",
+ "curlyvee": "\u22CE",
+ "curlywedge": "\u22CF",
+ "curren": "\u00A4",
+ "curvearrowleft": "\u21B6",
+ "curvearrowright": "\u21B7",
+ "cuvee": "\u22CE",
+ "cuwed": "\u22CF",
+ "cwconint": "\u2232",
+ "cwint": "\u2231",
+ "cylcty": "\u232D",
+ "dArr": "\u21D3",
+ "dHar": "\u2965",
+ "dagger": "\u2020",
+ "daleth": "\u2138",
+ "darr": "\u2193",
+ "dash": "\u2010",
+ "dashv": "\u22A3",
+ "dbkarow": "\u290F",
+ "dblac": "\u02DD",
+ "dcaron": "\u010F",
+ "dcy": "\u0434",
+ "dd": "\u2146",
+ "ddagger": "\u2021",
+ "ddarr": "\u21CA",
+ "ddotseq": "\u2A77",
+ "deg": "\u00B0",
+ "delta": "\u03B4",
+ "demptyv": "\u29B1",
+ "dfisht": "\u297F",
+ "dfr": "\U0001D521",
+ "dharl": "\u21C3",
+ "dharr": "\u21C2",
+ "diam": "\u22C4",
+ "diamond": "\u22C4",
+ "diamondsuit": "\u2666",
+ "diams": "\u2666",
+ "die": "\u00A8",
+ "digamma": "\u03DD",
+ "disin": "\u22F2",
+ "div": "\u00F7",
+ "divide": "\u00F7",
+ "divideontimes": "\u22C7",
+ "divonx": "\u22C7",
+ "djcy": "\u0452",
+ "dlcorn": "\u231E",
+ "dlcrop": "\u230D",
+ "dollar": "\u0024",
+ "dopf": "\U0001D555",
+ "dot": "\u02D9",
+ "doteq": "\u2250",
+ "doteqdot": "\u2251",
+ "dotminus": "\u2238",
+ "dotplus": "\u2214",
+ "dotsquare": "\u22A1",
+ "doublebarwedge": "\u2306",
+ "downarrow": "\u2193",
+ "downdownarrows": "\u21CA",
+ "downharpoonleft": "\u21C3",
+ "downharpoonright": "\u21C2",
+ "drbkarow": "\u2910",
+ "drcorn": "\u231F",
+ "drcrop": "\u230C",
+ "dscr": "\U0001D4B9",
+ "dscy": "\u0455",
+ "dsol": "\u29F6",
+ "dstrok": "\u0111",
+ "dtdot": "\u22F1",
+ "dtri": "\u25BF",
+ "dtrif": "\u25BE",
+ "duarr": "\u21F5",
+ "duhar": "\u296F",
+ "dwangle": "\u29A6",
+ "dzcy": "\u045F",
+ "dzigrarr": "\u27FF",
+ "eDDot": "\u2A77",
+ "eDot": "\u2251",
+ "eacute": "\u00E9",
+ "easter": "\u2A6E",
+ "ecaron": "\u011B",
+ "ecir": "\u2256",
+ "ecirc": "\u00EA",
+ "ecolon": "\u2255",
+ "ecy": "\u044D",
+ "edot": "\u0117",
+ "ee": "\u2147",
+ "efDot": "\u2252",
+ "efr": "\U0001D522",
+ "eg": "\u2A9A",
+ "egrave": "\u00E8",
+ "egs": "\u2A96",
+ "egsdot": "\u2A98",
+ "el": "\u2A99",
+ "elinters": "\u23E7",
+ "ell": "\u2113",
+ "els": "\u2A95",
+ "elsdot": "\u2A97",
+ "emacr": "\u0113",
+ "empty": "\u2205",
+ "emptyset": "\u2205",
+ "emptyv": "\u2205",
+ "emsp": "\u2003",
+ "emsp13": "\u2004",
+ "emsp14": "\u2005",
+ "eng": "\u014B",
+ "ensp": "\u2002",
+ "eogon": "\u0119",
+ "eopf": "\U0001D556",
+ "epar": "\u22D5",
+ "eparsl": "\u29E3",
+ "eplus": "\u2A71",
+ "epsi": "\u03B5",
+ "epsilon": "\u03B5",
+ "epsiv": "\u03F5",
+ "eqcirc": "\u2256",
+ "eqcolon": "\u2255",
+ "eqsim": "\u2242",
+ "eqslantgtr": "\u2A96",
+ "eqslantless": "\u2A95",
+ "equals": "\u003D",
+ "equest": "\u225F",
+ "equiv": "\u2261",
+ "equivDD": "\u2A78",
+ "eqvparsl": "\u29E5",
+ "erDot": "\u2253",
+ "erarr": "\u2971",
+ "escr": "\u212F",
+ "esdot": "\u2250",
+ "esim": "\u2242",
+ "eta": "\u03B7",
+ "eth": "\u00F0",
+ "euml": "\u00EB",
+ "euro": "\u20AC",
+ "excl": "\u0021",
+ "exist": "\u2203",
+ "expectation": "\u2130",
+ "exponentiale": "\u2147",
+ "fallingdotseq": "\u2252",
+ "fcy": "\u0444",
+ "female": "\u2640",
+ "ffilig": "\uFB03",
+ "fflig": "\uFB00",
+ "ffllig": "\uFB04",
+ "ffr": "\U0001D523",
+ "filig": "\uFB01",
+ "fjlig": "\u0066\u006A",
+ "flat": "\u266D",
+ "fllig": "\uFB02",
+ "fltns": "\u25B1",
+ "fnof": "\u0192",
+ "fopf": "\U0001D557",
+ "forall": "\u2200",
+ "fork": "\u22D4",
+ "forkv": "\u2AD9",
+ "fpartint": "\u2A0D",
+ "frac12": "\u00BD",
+ "frac13": "\u2153",
+ "frac14": "\u00BC",
+ "frac15": "\u2155",
+ "frac16": "\u2159",
+ "frac18": "\u215B",
+ "frac23": "\u2154",
+ "frac25": "\u2156",
+ "frac34": "\u00BE",
+ "frac35": "\u2157",
+ "frac38": "\u215C",
+ "frac45": "\u2158",
+ "frac56": "\u215A",
+ "frac58": "\u215D",
+ "frac78": "\u215E",
+ "frasl": "\u2044",
+ "frown": "\u2322",
+ "fscr": "\U0001D4BB",
+ "gE": "\u2267",
+ "gEl": "\u2A8C",
+ "gacute": "\u01F5",
+ "gamma": "\u03B3",
+ "gammad": "\u03DD",
+ "gap": "\u2A86",
+ "gbreve": "\u011F",
+ "gcirc": "\u011D",
+ "gcy": "\u0433",
+ "gdot": "\u0121",
+ "ge": "\u2265",
+ "gel": "\u22DB",
+ "geq": "\u2265",
+ "geqq": "\u2267",
+ "geqslant": "\u2A7E",
+ "ges": "\u2A7E",
+ "gescc": "\u2AA9",
+ "gesdot": "\u2A80",
+ "gesdoto": "\u2A82",
+ "gesdotol": "\u2A84",
+ "gesl": "\u22DB\uFE00",
+ "gesles": "\u2A94",
+ "gfr": "\U0001D524",
+ "gg": "\u226B",
+ "ggg": "\u22D9",
+ "gimel": "\u2137",
+ "gjcy": "\u0453",
+ "gl": "\u2277",
+ "glE": "\u2A92",
+ "gla": "\u2AA5",
+ "glj": "\u2AA4",
+ "gnE": "\u2269",
+ "gnap": "\u2A8A",
+ "gnapprox": "\u2A8A",
+ "gne": "\u2A88",
+ "gneq": "\u2A88",
+ "gneqq": "\u2269",
+ "gnsim": "\u22E7",
+ "gopf": "\U0001D558",
+ "grave": "\u0060",
+ "gscr": "\u210A",
+ "gsim": "\u2273",
+ "gsime": "\u2A8E",
+ "gsiml": "\u2A90",
+ "gt": "\u003E",
+ "gtcc": "\u2AA7",
+ "gtcir": "\u2A7A",
+ "gtdot": "\u22D7",
+ "gtlPar": "\u2995",
+ "gtquest": "\u2A7C",
+ "gtrapprox": "\u2A86",
+ "gtrarr": "\u2978",
+ "gtrdot": "\u22D7",
+ "gtreqless": "\u22DB",
+ "gtreqqless": "\u2A8C",
+ "gtrless": "\u2277",
+ "gtrsim": "\u2273",
+ "gvertneqq": "\u2269\uFE00",
+ "gvnE": "\u2269\uFE00",
+ "hArr": "\u21D4",
+ "hairsp": "\u200A",
+ "half": "\u00BD",
+ "hamilt": "\u210B",
+ "hardcy": "\u044A",
+ "harr": "\u2194",
+ "harrcir": "\u2948",
+ "harrw": "\u21AD",
+ "hbar": "\u210F",
+ "hcirc": "\u0125",
+ "hearts": "\u2665",
+ "heartsuit": "\u2665",
+ "hellip": "\u2026",
+ "hercon": "\u22B9",
+ "hfr": "\U0001D525",
+ "hksearow": "\u2925",
+ "hkswarow": "\u2926",
+ "hoarr": "\u21FF",
+ "homtht": "\u223B",
+ "hookleftarrow": "\u21A9",
+ "hookrightarrow": "\u21AA",
+ "hopf": "\U0001D559",
+ "horbar": "\u2015",
+ "hscr": "\U0001D4BD",
+ "hslash": "\u210F",
+ "hstrok": "\u0127",
+ "hybull": "\u2043",
+ "hyphen": "\u2010",
+ "iacute": "\u00ED",
+ "ic": "\u2063",
+ "icirc": "\u00EE",
+ "icy": "\u0438",
+ "iecy": "\u0435",
+ "iexcl": "\u00A1",
+ "iff": "\u21D4",
+ "ifr": "\U0001D526",
+ "igrave": "\u00EC",
+ "ii": "\u2148",
+ "iiiint": "\u2A0C",
+ "iiint": "\u222D",
+ "iinfin": "\u29DC",
+ "iiota": "\u2129",
+ "ijlig": "\u0133",
+ "imacr": "\u012B",
+ "image": "\u2111",
+ "imagline": "\u2110",
+ "imagpart": "\u2111",
+ "imath": "\u0131",
+ "imof": "\u22B7",
+ "imped": "\u01B5",
+ "in": "\u2208",
+ "incare": "\u2105",
+ "infin": "\u221E",
+ "infintie": "\u29DD",
+ "inodot": "\u0131",
+ "int": "\u222B",
+ "intcal": "\u22BA",
+ "integers": "\u2124",
+ "intercal": "\u22BA",
+ "intlarhk": "\u2A17",
+ "intprod": "\u2A3C",
+ "iocy": "\u0451",
+ "iogon": "\u012F",
+ "iopf": "\U0001D55A",
+ "iota": "\u03B9",
+ "iprod": "\u2A3C",
+ "iquest": "\u00BF",
+ "iscr": "\U0001D4BE",
+ "isin": "\u2208",
+ "isinE": "\u22F9",
+ "isindot": "\u22F5",
+ "isins": "\u22F4",
+ "isinsv": "\u22F3",
+ "isinv": "\u2208",
+ "it": "\u2062",
+ "itilde": "\u0129",
+ "iukcy": "\u0456",
+ "iuml": "\u00EF",
+ "jcirc": "\u0135",
+ "jcy": "\u0439",
+ "jfr": "\U0001D527",
+ "jmath": "\u0237",
+ "jopf": "\U0001D55B",
+ "jscr": "\U0001D4BF",
+ "jsercy": "\u0458",
+ "jukcy": "\u0454",
+ "kappa": "\u03BA",
+ "kappav": "\u03F0",
+ "kcedil": "\u0137",
+ "kcy": "\u043A",
+ "kfr": "\U0001D528",
+ "kgreen": "\u0138",
+ "khcy": "\u0445",
+ "kjcy": "\u045C",
+ "kopf": "\U0001D55C",
+ "kscr": "\U0001D4C0",
+ "lAarr": "\u21DA",
+ "lArr": "\u21D0",
+ "lAtail": "\u291B",
+ "lBarr": "\u290E",
+ "lE": "\u2266",
+ "lEg": "\u2A8B",
+ "lHar": "\u2962",
+ "lacute": "\u013A",
+ "laemptyv": "\u29B4",
+ "lagran": "\u2112",
+ "lambda": "\u03BB",
+ "lang": "\u27E8",
+ "langd": "\u2991",
+ "langle": "\u27E8",
+ "lap": "\u2A85",
+ "laquo": "\u00AB",
+ "larr": "\u2190",
+ "larrb": "\u21E4",
+ "larrbfs": "\u291F",
+ "larrfs": "\u291D",
+ "larrhk": "\u21A9",
+ "larrlp": "\u21AB",
+ "larrpl": "\u2939",
+ "larrsim": "\u2973",
+ "larrtl": "\u21A2",
+ "lat": "\u2AAB",
+ "latail": "\u2919",
+ "late": "\u2AAD",
+ "lates": "\u2AAD\uFE00",
+ "lbarr": "\u290C",
+ "lbbrk": "\u2772",
+ "lbrace": "\u007B",
+ "lbrack": "\u005B",
+ "lbrke": "\u298B",
+ "lbrksld": "\u298F",
+ "lbrkslu": "\u298D",
+ "lcaron": "\u013E",
+ "lcedil": "\u013C",
+ "lceil": "\u2308",
+ "lcub": "\u007B",
+ "lcy": "\u043B",
+ "ldca": "\u2936",
+ "ldquo": "\u201C",
+ "ldquor": "\u201E",
+ "ldrdhar": "\u2967",
+ "ldrushar": "\u294B",
+ "ldsh": "\u21B2",
+ "le": "\u2264",
+ "leftarrow": "\u2190",
+ "leftarrowtail": "\u21A2",
+ "leftharpoondown": "\u21BD",
+ "leftharpoonup": "\u21BC",
+ "leftleftarrows": "\u21C7",
+ "leftrightarrow": "\u2194",
+ "leftrightarrows": "\u21C6",
+ "leftrightharpoons": "\u21CB",
+ "leftrightsquigarrow": "\u21AD",
+ "leftthreetimes": "\u22CB",
+ "leg": "\u22DA",
+ "leq": "\u2264",
+ "leqq": "\u2266",
+ "leqslant": "\u2A7D",
+ "les": "\u2A7D",
+ "lescc": "\u2AA8",
+ "lesdot": "\u2A7F",
+ "lesdoto": "\u2A81",
+ "lesdotor": "\u2A83",
+ "lesg": "\u22DA\uFE00",
+ "lesges": "\u2A93",
+ "lessapprox": "\u2A85",
+ "lessdot": "\u22D6",
+ "lesseqgtr": "\u22DA",
+ "lesseqqgtr": "\u2A8B",
+ "lessgtr": "\u2276",
+ "lesssim": "\u2272",
+ "lfisht": "\u297C",
+ "lfloor": "\u230A",
+ "lfr": "\U0001D529",
+ "lg": "\u2276",
+ "lgE": "\u2A91",
+ "lhard": "\u21BD",
+ "lharu": "\u21BC",
+ "lharul": "\u296A",
+ "lhblk": "\u2584",
+ "ljcy": "\u0459",
+ "ll": "\u226A",
+ "llarr": "\u21C7",
+ "llcorner": "\u231E",
+ "llhard": "\u296B",
+ "lltri": "\u25FA",
+ "lmidot": "\u0140",
+ "lmoust": "\u23B0",
+ "lmoustache": "\u23B0",
+ "lnE": "\u2268",
+ "lnap": "\u2A89",
+ "lnapprox": "\u2A89",
+ "lne": "\u2A87",
+ "lneq": "\u2A87",
+ "lneqq": "\u2268",
+ "lnsim": "\u22E6",
+ "loang": "\u27EC",
+ "loarr": "\u21FD",
+ "lobrk": "\u27E6",
+ "longleftarrow": "\u27F5",
+ "longleftrightarrow": "\u27F7",
+ "longmapsto": "\u27FC",
+ "longrightarrow": "\u27F6",
+ "looparrowleft": "\u21AB",
+ "looparrowright": "\u21AC",
+ "lopar": "\u2985",
+ "lopf": "\U0001D55D",
+ "loplus": "\u2A2D",
+ "lotimes": "\u2A34",
+ "lowast": "\u2217",
+ "lowbar": "\u005F",
+ "loz": "\u25CA",
+ "lozenge": "\u25CA",
+ "lozf": "\u29EB",
+ "lpar": "\u0028",
+ "lparlt": "\u2993",
+ "lrarr": "\u21C6",
+ "lrcorner": "\u231F",
+ "lrhar": "\u21CB",
+ "lrhard": "\u296D",
+ "lrm": "\u200E",
+ "lrtri": "\u22BF",
+ "lsaquo": "\u2039",
+ "lscr": "\U0001D4C1",
+ "lsh": "\u21B0",
+ "lsim": "\u2272",
+ "lsime": "\u2A8D",
+ "lsimg": "\u2A8F",
+ "lsqb": "\u005B",
+ "lsquo": "\u2018",
+ "lsquor": "\u201A",
+ "lstrok": "\u0142",
+ "lt": "\u003C",
+ "ltcc": "\u2AA6",
+ "ltcir": "\u2A79",
+ "ltdot": "\u22D6",
+ "lthree": "\u22CB",
+ "ltimes": "\u22C9",
+ "ltlarr": "\u2976",
+ "ltquest": "\u2A7B",
+ "ltrPar": "\u2996",
+ "ltri": "\u25C3",
+ "ltrie": "\u22B4",
+ "ltrif": "\u25C2",
+ "lurdshar": "\u294A",
+ "luruhar": "\u2966",
+ "lvertneqq": "\u2268\uFE00",
+ "lvnE": "\u2268\uFE00",
+ "mDDot": "\u223A",
+ "macr": "\u00AF",
+ "male": "\u2642",
+ "malt": "\u2720",
+ "maltese": "\u2720",
+ "map": "\u21A6",
+ "mapsto": "\u21A6",
+ "mapstodown": "\u21A7",
+ "mapstoleft": "\u21A4",
+ "mapstoup": "\u21A5",
+ "marker": "\u25AE",
+ "mcomma": "\u2A29",
+ "mcy": "\u043C",
+ "mdash": "\u2014",
+ "measuredangle": "\u2221",
+ "mfr": "\U0001D52A",
+ "mho": "\u2127",
+ "micro": "\u00B5",
+ "mid": "\u2223",
+ "midast": "\u002A",
+ "midcir": "\u2AF0",
+ "middot": "\u00B7",
+ "minus": "\u2212",
+ "minusb": "\u229F",
+ "minusd": "\u2238",
+ "minusdu": "\u2A2A",
+ "mlcp": "\u2ADB",
+ "mldr": "\u2026",
+ "mnplus": "\u2213",
+ "models": "\u22A7",
+ "mopf": "\U0001D55E",
+ "mp": "\u2213",
+ "mscr": "\U0001D4C2",
+ "mstpos": "\u223E",
+ "mu": "\u03BC",
+ "multimap": "\u22B8",
+ "mumap": "\u22B8",
+ "nGg": "\u22D9\u0338",
+ "nGt": "\u226B\u20D2",
+ "nGtv": "\u226B\u0338",
+ "nLeftarrow": "\u21CD",
+ "nLeftrightarrow": "\u21CE",
+ "nLl": "\u22D8\u0338",
+ "nLt": "\u226A\u20D2",
+ "nLtv": "\u226A\u0338",
+ "nRightarrow": "\u21CF",
+ "nVDash": "\u22AF",
+ "nVdash": "\u22AE",
+ "nabla": "\u2207",
+ "nacute": "\u0144",
+ "nang": "\u2220\u20D2",
+ "nap": "\u2249",
+ "napE": "\u2A70\u0338",
+ "napid": "\u224B\u0338",
+ "napos": "\u0149",
+ "napprox": "\u2249",
+ "natur": "\u266E",
+ "natural": "\u266E",
+ "naturals": "\u2115",
+ "nbsp": "\u00A0",
+ "nbump": "\u224E\u0338",
+ "nbumpe": "\u224F\u0338",
+ "ncap": "\u2A43",
+ "ncaron": "\u0148",
+ "ncedil": "\u0146",
+ "ncong": "\u2247",
+ "ncongdot": "\u2A6D\u0338",
+ "ncup": "\u2A42",
+ "ncy": "\u043D",
+ "ndash": "\u2013",
+ "ne": "\u2260",
+ "neArr": "\u21D7",
+ "nearhk": "\u2924",
+ "nearr": "\u2197",
+ "nearrow": "\u2197",
+ "nedot": "\u2250\u0338",
+ "nequiv": "\u2262",
+ "nesear": "\u2928",
+ "nesim": "\u2242\u0338",
+ "nexist": "\u2204",
+ "nexists": "\u2204",
+ "nfr": "\U0001D52B",
+ "ngE": "\u2267\u0338",
+ "nge": "\u2271",
+ "ngeq": "\u2271",
+ "ngeqq": "\u2267\u0338",
+ "ngeqslant": "\u2A7E\u0338",
+ "nges": "\u2A7E\u0338",
+ "ngsim": "\u2275",
+ "ngt": "\u226F",
+ "ngtr": "\u226F",
+ "nhArr": "\u21CE",
+ "nharr": "\u21AE",
+ "nhpar": "\u2AF2",
+ "ni": "\u220B",
+ "nis": "\u22FC",
+ "nisd": "\u22FA",
+ "niv": "\u220B",
+ "njcy": "\u045A",
+ "nlArr": "\u21CD",
+ "nlE": "\u2266\u0338",
+ "nlarr": "\u219A",
+ "nldr": "\u2025",
+ "nle": "\u2270",
+ "nleftarrow": "\u219A",
+ "nleftrightarrow": "\u21AE",
+ "nleq": "\u2270",
+ "nleqq": "\u2266\u0338",
+ "nleqslant": "\u2A7D\u0338",
+ "nles": "\u2A7D\u0338",
+ "nless": "\u226E",
+ "nlsim": "\u2274",
+ "nlt": "\u226E",
+ "nltri": "\u22EA",
+ "nltrie": "\u22EC",
+ "nmid": "\u2224",
+ "nopf": "\U0001D55F",
+ "not": "\u00AC",
+ "notin": "\u2209",
+ "notinE": "\u22F9\u0338",
+ "notindot": "\u22F5\u0338",
+ "notinva": "\u2209",
+ "notinvb": "\u22F7",
+ "notinvc": "\u22F6",
+ "notni": "\u220C",
+ "notniva": "\u220C",
+ "notnivb": "\u22FE",
+ "notnivc": "\u22FD",
+ "npar": "\u2226",
+ "nparallel": "\u2226",
+ "nparsl": "\u2AFD\u20E5",
+ "npart": "\u2202\u0338",
+ "npolint": "\u2A14",
+ "npr": "\u2280",
+ "nprcue": "\u22E0",
+ "npre": "\u2AAF\u0338",
+ "nprec": "\u2280",
+ "npreceq": "\u2AAF\u0338",
+ "nrArr": "\u21CF",
+ "nrarr": "\u219B",
+ "nrarrc": "\u2933\u0338",
+ "nrarrw": "\u219D\u0338",
+ "nrightarrow": "\u219B",
+ "nrtri": "\u22EB",
+ "nrtrie": "\u22ED",
+ "nsc": "\u2281",
+ "nsccue": "\u22E1",
+ "nsce": "\u2AB0\u0338",
+ "nscr": "\U0001D4C3",
+ "nshortmid": "\u2224",
+ "nshortparallel": "\u2226",
+ "nsim": "\u2241",
+ "nsime": "\u2244",
+ "nsimeq": "\u2244",
+ "nsmid": "\u2224",
+ "nspar": "\u2226",
+ "nsqsube": "\u22E2",
+ "nsqsupe": "\u22E3",
+ "nsub": "\u2284",
+ "nsubE": "\u2AC5\u0338",
+ "nsube": "\u2288",
+ "nsubset": "\u2282\u20D2",
+ "nsubseteq": "\u2288",
+ "nsubseteqq": "\u2AC5\u0338",
+ "nsucc": "\u2281",
+ "nsucceq": "\u2AB0\u0338",
+ "nsup": "\u2285",
+ "nsupE": "\u2AC6\u0338",
+ "nsupe": "\u2289",
+ "nsupset": "\u2283\u20D2",
+ "nsupseteq": "\u2289",
+ "nsupseteqq": "\u2AC6\u0338",
+ "ntgl": "\u2279",
+ "ntilde": "\u00F1",
+ "ntlg": "\u2278",
+ "ntriangleleft": "\u22EA",
+ "ntrianglelefteq": "\u22EC",
+ "ntriangleright": "\u22EB",
+ "ntrianglerighteq": "\u22ED",
+ "nu": "\u03BD",
+ "num": "\u0023",
+ "numero": "\u2116",
+ "numsp": "\u2007",
+ "nvDash": "\u22AD",
+ "nvHarr": "\u2904",
+ "nvap": "\u224D\u20D2",
+ "nvdash": "\u22AC",
+ "nvge": "\u2265\u20D2",
+ "nvgt": "\u003E\u20D2",
+ "nvinfin": "\u29DE",
+ "nvlArr": "\u2902",
+ "nvle": "\u2264\u20D2",
+ "nvlt": "\u003C\u20D2",
+ "nvltrie": "\u22B4\u20D2",
+ "nvrArr": "\u2903",
+ "nvrtrie": "\u22B5\u20D2",
+ "nvsim": "\u223C\u20D2",
+ "nwArr": "\u21D6",
+ "nwarhk": "\u2923",
+ "nwarr": "\u2196",
+ "nwarrow": "\u2196",
+ "nwnear": "\u2927",
+ "oS": "\u24C8",
+ "oacute": "\u00F3",
+ "oast": "\u229B",
+ "ocir": "\u229A",
+ "ocirc": "\u00F4",
+ "ocy": "\u043E",
+ "odash": "\u229D",
+ "odblac": "\u0151",
+ "odiv": "\u2A38",
+ "odot": "\u2299",
+ "odsold": "\u29BC",
+ "oelig": "\u0153",
+ "ofcir": "\u29BF",
+ "ofr": "\U0001D52C",
+ "ogon": "\u02DB",
+ "ograve": "\u00F2",
+ "ogt": "\u29C1",
+ "ohbar": "\u29B5",
+ "ohm": "\u03A9",
+ "oint": "\u222E",
+ "olarr": "\u21BA",
+ "olcir": "\u29BE",
+ "olcross": "\u29BB",
+ "oline": "\u203E",
+ "olt": "\u29C0",
+ "omacr": "\u014D",
+ "omega": "\u03C9",
+ "omicron": "\u03BF",
+ "omid": "\u29B6",
+ "ominus": "\u2296",
+ "oopf": "\U0001D560",
+ "opar": "\u29B7",
+ "operp": "\u29B9",
+ "oplus": "\u2295",
+ "or": "\u2228",
+ "orarr": "\u21BB",
+ "ord": "\u2A5D",
+ "order": "\u2134",
+ "orderof": "\u2134",
+ "ordf": "\u00AA",
+ "ordm": "\u00BA",
+ "origof": "\u22B6",
+ "oror": "\u2A56",
+ "orslope": "\u2A57",
+ "orv": "\u2A5B",
+ "oscr": "\u2134",
+ "oslash": "\u00F8",
+ "osol": "\u2298",
+ "otilde": "\u00F5",
+ "otimes": "\u2297",
+ "otimesas": "\u2A36",
+ "ouml": "\u00F6",
+ "ovbar": "\u233D",
+ "par": "\u2225",
+ "para": "\u00B6",
+ "parallel": "\u2225",
+ "parsim": "\u2AF3",
+ "parsl": "\u2AFD",
+ "part": "\u2202",
+ "pcy": "\u043F",
+ "percnt": "\u0025",
+ "period": "\u002E",
+ "permil": "\u2030",
+ "perp": "\u22A5",
+ "pertenk": "\u2031",
+ "pfr": "\U0001D52D",
+ "phi": "\u03C6",
+ "phiv": "\u03D5",
+ "phmmat": "\u2133",
+ "phone": "\u260E",
+ "pi": "\u03C0",
+ "pitchfork": "\u22D4",
+ "piv": "\u03D6",
+ "planck": "\u210F",
+ "planckh": "\u210E",
+ "plankv": "\u210F",
+ "plus": "\u002B",
+ "plusacir": "\u2A23",
+ "plusb": "\u229E",
+ "pluscir": "\u2A22",
+ "plusdo": "\u2214",
+ "plusdu": "\u2A25",
+ "pluse": "\u2A72",
+ "plusmn": "\u00B1",
+ "plussim": "\u2A26",
+ "plustwo": "\u2A27",
+ "pm": "\u00B1",
+ "pointint": "\u2A15",
+ "popf": "\U0001D561",
+ "pound": "\u00A3",
+ "pr": "\u227A",
+ "prE": "\u2AB3",
+ "prap": "\u2AB7",
+ "prcue": "\u227C",
+ "pre": "\u2AAF",
+ "prec": "\u227A",
+ "precapprox": "\u2AB7",
+ "preccurlyeq": "\u227C",
+ "preceq": "\u2AAF",
+ "precnapprox": "\u2AB9",
+ "precneqq": "\u2AB5",
+ "precnsim": "\u22E8",
+ "precsim": "\u227E",
+ "prime": "\u2032",
+ "primes": "\u2119",
+ "prnE": "\u2AB5",
+ "prnap": "\u2AB9",
+ "prnsim": "\u22E8",
+ "prod": "\u220F",
+ "profalar": "\u232E",
+ "profline": "\u2312",
+ "profsurf": "\u2313",
+ "prop": "\u221D",
+ "propto": "\u221D",
+ "prsim": "\u227E",
+ "prurel": "\u22B0",
+ "pscr": "\U0001D4C5",
+ "psi": "\u03C8",
+ "puncsp": "\u2008",
+ "qfr": "\U0001D52E",
+ "qint": "\u2A0C",
+ "qopf": "\U0001D562",
+ "qprime": "\u2057",
+ "qscr": "\U0001D4C6",
+ "quaternions": "\u210D",
+ "quatint": "\u2A16",
+ "quest": "\u003F",
+ "questeq": "\u225F",
+ "quot": "\u0022",
+ "rAarr": "\u21DB",
+ "rArr": "\u21D2",
+ "rAtail": "\u291C",
+ "rBarr": "\u290F",
+ "rHar": "\u2964",
+ "race": "\u223D\u0331",
+ "racute": "\u0155",
+ "radic": "\u221A",
+ "raemptyv": "\u29B3",
+ "rang": "\u27E9",
+ "rangd": "\u2992",
+ "range": "\u29A5",
+ "rangle": "\u27E9",
+ "raquo": "\u00BB",
+ "rarr": "\u2192",
+ "rarrap": "\u2975",
+ "rarrb": "\u21E5",
+ "rarrbfs": "\u2920",
+ "rarrc": "\u2933",
+ "rarrfs": "\u291E",
+ "rarrhk": "\u21AA",
+ "rarrlp": "\u21AC",
+ "rarrpl": "\u2945",
+ "rarrsim": "\u2974",
+ "rarrtl": "\u21A3",
+ "rarrw": "\u219D",
+ "ratail": "\u291A",
+ "ratio": "\u2236",
+ "rationals": "\u211A",
+ "rbarr": "\u290D",
+ "rbbrk": "\u2773",
+ "rbrace": "\u007D",
+ "rbrack": "\u005D",
+ "rbrke": "\u298C",
+ "rbrksld": "\u298E",
+ "rbrkslu": "\u2990",
+ "rcaron": "\u0159",
+ "rcedil": "\u0157",
+ "rceil": "\u2309",
+ "rcub": "\u007D",
+ "rcy": "\u0440",
+ "rdca": "\u2937",
+ "rdldhar": "\u2969",
+ "rdquo": "\u201D",
+ "rdquor": "\u201D",
+ "rdsh": "\u21B3",
+ "real": "\u211C",
+ "realine": "\u211B",
+ "realpart": "\u211C",
+ "reals": "\u211D",
+ "rect": "\u25AD",
+ "reg": "\u00AE",
+ "rfisht": "\u297D",
+ "rfloor": "\u230B",
+ "rfr": "\U0001D52F",
+ "rhard": "\u21C1",
+ "rharu": "\u21C0",
+ "rharul": "\u296C",
+ "rho": "\u03C1",
+ "rhov": "\u03F1",
+ "rightarrow": "\u2192",
+ "rightarrowtail": "\u21A3",
+ "rightharpoondown": "\u21C1",
+ "rightharpoonup": "\u21C0",
+ "rightleftarrows": "\u21C4",
+ "rightleftharpoons": "\u21CC",
+ "rightrightarrows": "\u21C9",
+ "rightsquigarrow": "\u219D",
+ "rightthreetimes": "\u22CC",
+ "ring": "\u02DA",
+ "risingdotseq": "\u2253",
+ "rlarr": "\u21C4",
+ "rlhar": "\u21CC",
+ "rlm": "\u200F",
+ "rmoust": "\u23B1",
+ "rmoustache": "\u23B1",
+ "rnmid": "\u2AEE",
+ "roang": "\u27ED",
+ "roarr": "\u21FE",
+ "robrk": "\u27E7",
+ "ropar": "\u2986",
+ "ropf": "\U0001D563",
+ "roplus": "\u2A2E",
+ "rotimes": "\u2A35",
+ "rpar": "\u0029",
+ "rpargt": "\u2994",
+ "rppolint": "\u2A12",
+ "rrarr": "\u21C9",
+ "rsaquo": "\u203A",
+ "rscr": "\U0001D4C7",
+ "rsh": "\u21B1",
+ "rsqb": "\u005D",
+ "rsquo": "\u2019",
+ "rsquor": "\u2019",
+ "rthree": "\u22CC",
+ "rtimes": "\u22CA",
+ "rtri": "\u25B9",
+ "rtrie": "\u22B5",
+ "rtrif": "\u25B8",
+ "rtriltri": "\u29CE",
+ "ruluhar": "\u2968",
+ "rx": "\u211E",
+ "sacute": "\u015B",
+ "sbquo": "\u201A",
+ "sc": "\u227B",
+ "scE": "\u2AB4",
+ "scap": "\u2AB8",
+ "scaron": "\u0161",
+ "sccue": "\u227D",
+ "sce": "\u2AB0",
+ "scedil": "\u015F",
+ "scirc": "\u015D",
+ "scnE": "\u2AB6",
+ "scnap": "\u2ABA",
+ "scnsim": "\u22E9",
+ "scpolint": "\u2A13",
+ "scsim": "\u227F",
+ "scy": "\u0441",
+ "sdot": "\u22C5",
+ "sdotb": "\u22A1",
+ "sdote": "\u2A66",
+ "seArr": "\u21D8",
+ "searhk": "\u2925",
+ "searr": "\u2198",
+ "searrow": "\u2198",
+ "sect": "\u00A7",
+ "semi": "\u003B",
+ "seswar": "\u2929",
+ "setminus": "\u2216",
+ "setmn": "\u2216",
+ "sext": "\u2736",
+ "sfr": "\U0001D530",
+ "sfrown": "\u2322",
+ "sharp": "\u266F",
+ "shchcy": "\u0449",
+ "shcy": "\u0448",
+ "shortmid": "\u2223",
+ "shortparallel": "\u2225",
+ "shy": "\u00AD",
+ "sigma": "\u03C3",
+ "sigmaf": "\u03C2",
+ "sigmav": "\u03C2",
+ "sim": "\u223C",
+ "simdot": "\u2A6A",
+ "sime": "\u2243",
+ "simeq": "\u2243",
+ "simg": "\u2A9E",
+ "simgE": "\u2AA0",
+ "siml": "\u2A9D",
+ "simlE": "\u2A9F",
+ "simne": "\u2246",
+ "simplus": "\u2A24",
+ "simrarr": "\u2972",
+ "slarr": "\u2190",
+ "smallsetminus": "\u2216",
+ "smashp": "\u2A33",
+ "smeparsl": "\u29E4",
+ "smid": "\u2223",
+ "smile": "\u2323",
+ "smt": "\u2AAA",
+ "smte": "\u2AAC",
+ "smtes": "\u2AAC\uFE00",
+ "softcy": "\u044C",
+ "sol": "\u002F",
+ "solb": "\u29C4",
+ "solbar": "\u233F",
+ "sopf": "\U0001D564",
+ "spades": "\u2660",
+ "spadesuit": "\u2660",
+ "spar": "\u2225",
+ "sqcap": "\u2293",
+ "sqcaps": "\u2293\uFE00",
+ "sqcup": "\u2294",
+ "sqcups": "\u2294\uFE00",
+ "sqsub": "\u228F",
+ "sqsube": "\u2291",
+ "sqsubset": "\u228F",
+ "sqsubseteq": "\u2291",
+ "sqsup": "\u2290",
+ "sqsupe": "\u2292",
+ "sqsupset": "\u2290",
+ "sqsupseteq": "\u2292",
+ "squ": "\u25A1",
+ "square": "\u25A1",
+ "squarf": "\u25AA",
+ "squf": "\u25AA",
+ "srarr": "\u2192",
+ "sscr": "\U0001D4C8",
+ "ssetmn": "\u2216",
+ "ssmile": "\u2323",
+ "sstarf": "\u22C6",
+ "star": "\u2606",
+ "starf": "\u2605",
+ "straightepsilon": "\u03F5",
+ "straightphi": "\u03D5",
+ "strns": "\u00AF",
+ "sub": "\u2282",
+ "subE": "\u2AC5",
+ "subdot": "\u2ABD",
+ "sube": "\u2286",
+ "subedot": "\u2AC3",
+ "submult": "\u2AC1",
+ "subnE": "\u2ACB",
+ "subne": "\u228A",
+ "subplus": "\u2ABF",
+ "subrarr": "\u2979",
+ "subset": "\u2282",
+ "subseteq": "\u2286",
+ "subseteqq": "\u2AC5",
+ "subsetneq": "\u228A",
+ "subsetneqq": "\u2ACB",
+ "subsim": "\u2AC7",
+ "subsub": "\u2AD5",
+ "subsup": "\u2AD3",
+ "succ": "\u227B",
+ "succapprox": "\u2AB8",
+ "succcurlyeq": "\u227D",
+ "succeq": "\u2AB0",
+ "succnapprox": "\u2ABA",
+ "succneqq": "\u2AB6",
+ "succnsim": "\u22E9",
+ "succsim": "\u227F",
+ "sum": "\u2211",
+ "sung": "\u266A",
+ "sup": "\u2283",
+ "sup1": "\u00B9",
+ "sup2": "\u00B2",
+ "sup3": "\u00B3",
+ "supE": "\u2AC6",
+ "supdot": "\u2ABE",
+ "supdsub": "\u2AD8",
+ "supe": "\u2287",
+ "supedot": "\u2AC4",
+ "suphsol": "\u27C9",
+ "suphsub": "\u2AD7",
+ "suplarr": "\u297B",
+ "supmult": "\u2AC2",
+ "supnE": "\u2ACC",
+ "supne": "\u228B",
+ "supplus": "\u2AC0",
+ "supset": "\u2283",
+ "supseteq": "\u2287",
+ "supseteqq": "\u2AC6",
+ "supsetneq": "\u228B",
+ "supsetneqq": "\u2ACC",
+ "supsim": "\u2AC8",
+ "supsub": "\u2AD4",
+ "supsup": "\u2AD6",
+ "swArr": "\u21D9",
+ "swarhk": "\u2926",
+ "swarr": "\u2199",
+ "swarrow": "\u2199",
+ "swnwar": "\u292A",
+ "szlig": "\u00DF",
+ "target": "\u2316",
+ "tau": "\u03C4",
+ "tbrk": "\u23B4",
+ "tcaron": "\u0165",
+ "tcedil": "\u0163",
+ "tcy": "\u0442",
+ "tdot": "\u20DB",
+ "telrec": "\u2315",
+ "tfr": "\U0001D531",
+ "there4": "\u2234",
+ "therefore": "\u2234",
+ "theta": "\u03B8",
+ "thetasym": "\u03D1",
+ "thetav": "\u03D1",
+ "thickapprox": "\u2248",
+ "thicksim": "\u223C",
+ "thinsp": "\u2009",
+ "thkap": "\u2248",
+ "thksim": "\u223C",
+ "thorn": "\u00FE",
+ "tilde": "\u02DC",
+ "times": "\u00D7",
+ "timesb": "\u22A0",
+ "timesbar": "\u2A31",
+ "timesd": "\u2A30",
+ "tint": "\u222D",
+ "toea": "\u2928",
+ "top": "\u22A4",
+ "topbot": "\u2336",
+ "topcir": "\u2AF1",
+ "topf": "\U0001D565",
+ "topfork": "\u2ADA",
+ "tosa": "\u2929",
+ "tprime": "\u2034",
+ "trade": "\u2122",
+ "triangle": "\u25B5",
+ "triangledown": "\u25BF",
+ "triangleleft": "\u25C3",
+ "trianglelefteq": "\u22B4",
+ "triangleq": "\u225C",
+ "triangleright": "\u25B9",
+ "trianglerighteq": "\u22B5",
+ "tridot": "\u25EC",
+ "trie": "\u225C",
+ "triminus": "\u2A3A",
+ "triplus": "\u2A39",
+ "trisb": "\u29CD",
+ "tritime": "\u2A3B",
+ "trpezium": "\u23E2",
+ "tscr": "\U0001D4C9",
+ "tscy": "\u0446",
+ "tshcy": "\u045B",
+ "tstrok": "\u0167",
+ "twixt": "\u226C",
+ "twoheadleftarrow": "\u219E",
+ "twoheadrightarrow": "\u21A0",
+ "uArr": "\u21D1",
+ "uHar": "\u2963",
+ "uacute": "\u00FA",
+ "uarr": "\u2191",
+ "ubrcy": "\u045E",
+ "ubreve": "\u016D",
+ "ucirc": "\u00FB",
+ "ucy": "\u0443",
+ "udarr": "\u21C5",
+ "udblac": "\u0171",
+ "udhar": "\u296E",
+ "ufisht": "\u297E",
+ "ufr": "\U0001D532",
+ "ugrave": "\u00F9",
+ "uharl": "\u21BF",
+ "uharr": "\u21BE",
+ "uhblk": "\u2580",
+ "ulcorn": "\u231C",
+ "ulcorner": "\u231C",
+ "ulcrop": "\u230F",
+ "ultri": "\u25F8",
+ "umacr": "\u016B",
+ "uml": "\u00A8",
+ "uogon": "\u0173",
+ "uopf": "\U0001D566",
+ "uparrow": "\u2191",
+ "updownarrow": "\u2195",
+ "upharpoonleft": "\u21BF",
+ "upharpoonright": "\u21BE",
+ "uplus": "\u228E",
+ "upsi": "\u03C5",
+ "upsih": "\u03D2",
+ "upsilon": "\u03C5",
+ "upuparrows": "\u21C8",
+ "urcorn": "\u231D",
+ "urcorner": "\u231D",
+ "urcrop": "\u230E",
+ "uring": "\u016F",
+ "urtri": "\u25F9",
+ "uscr": "\U0001D4CA",
+ "utdot": "\u22F0",
+ "utilde": "\u0169",
+ "utri": "\u25B5",
+ "utrif": "\u25B4",
+ "uuarr": "\u21C8",
+ "uuml": "\u00FC",
+ "uwangle": "\u29A7",
+ "vArr": "\u21D5",
+ "vBar": "\u2AE8",
+ "vBarv": "\u2AE9",
+ "vDash": "\u22A8",
+ "vangrt": "\u299C",
+ "varepsilon": "\u03F5",
+ "varkappa": "\u03F0",
+ "varnothing": "\u2205",
+ "varphi": "\u03D5",
+ "varpi": "\u03D6",
+ "varpropto": "\u221D",
+ "varr": "\u2195",
+ "varrho": "\u03F1",
+ "varsigma": "\u03C2",
+ "varsubsetneq": "\u228A\uFE00",
+ "varsubsetneqq": "\u2ACB\uFE00",
+ "varsupsetneq": "\u228B\uFE00",
+ "varsupsetneqq": "\u2ACC\uFE00",
+ "vartheta": "\u03D1",
+ "vartriangleleft": "\u22B2",
+ "vartriangleright": "\u22B3",
+ "vcy": "\u0432",
+ "vdash": "\u22A2",
+ "vee": "\u2228",
+ "veebar": "\u22BB",
+ "veeeq": "\u225A",
+ "vellip": "\u22EE",
+ "verbar": "\u007C",
+ "vert": "\u007C",
+ "vfr": "\U0001D533",
+ "vltri": "\u22B2",
+ "vnsub": "\u2282\u20D2",
+ "vnsup": "\u2283\u20D2",
+ "vopf": "\U0001D567",
+ "vprop": "\u221D",
+ "vrtri": "\u22B3",
+ "vscr": "\U0001D4CB",
+ "vsubnE": "\u2ACB\uFE00",
+ "vsubne": "\u228A\uFE00",
+ "vsupnE": "\u2ACC\uFE00",
+ "vsupne": "\u228B\uFE00",
+ "vzigzag": "\u299A",
+ "wcirc": "\u0175",
+ "wedbar": "\u2A5F",
+ "wedge": "\u2227",
+ "wedgeq": "\u2259",
+ "weierp": "\u2118",
+ "wfr": "\U0001D534",
+ "wopf": "\U0001D568",
+ "wp": "\u2118",
+ "wr": "\u2240",
+ "wreath": "\u2240",
+ "wscr": "\U0001D4CC",
+ "xcap": "\u22C2",
+ "xcirc": "\u25EF",
+ "xcup": "\u22C3",
+ "xdtri": "\u25BD",
+ "xfr": "\U0001D535",
+ "xhArr": "\u27FA",
+ "xharr": "\u27F7",
+ "xi": "\u03BE",
+ "xlArr": "\u27F8",
+ "xlarr": "\u27F5",
+ "xmap": "\u27FC",
+ "xnis": "\u22FB",
+ "xodot": "\u2A00",
+ "xopf": "\U0001D569",
+ "xoplus": "\u2A01",
+ "xotime": "\u2A02",
+ "xrArr": "\u27F9",
+ "xrarr": "\u27F6",
+ "xscr": "\U0001D4CD",
+ "xsqcup": "\u2A06",
+ "xuplus": "\u2A04",
+ "xutri": "\u25B3",
+ "xvee": "\u22C1",
+ "xwedge": "\u22C0",
+ "yacute": "\u00FD",
+ "yacy": "\u044F",
+ "ycirc": "\u0177",
+ "ycy": "\u044B",
+ "yen": "\u00A5",
+ "yfr": "\U0001D536",
+ "yicy": "\u0457",
+ "yopf": "\U0001D56A",
+ "yscr": "\U0001D4CE",
+ "yucy": "\u044E",
+ "yuml": "\u00FF",
+ "zacute": "\u017A",
+ "zcaron": "\u017E",
+ "zcy": "\u0437",
+ "zdot": "\u017C",
+ "zeetrf": "\u2128",
+ "zeta": "\u03B6",
+ "zfr": "\U0001D537",
+ "zhcy": "\u0436",
+ "zigrarr": "\u21DD",
+ "zopf": "\U0001D56B",
+ "zscr": "\U0001D4CF",
+ "zwj": "\u200D",
+ "zwnj": "\u200C",
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/indented_code.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/indented_code.go
new file mode 100644
index 00000000..dc5dce1a
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/indented_code.go
@@ -0,0 +1,98 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type IndentedCodeLine struct {
+ Indentation int
+ Range Range
+}
+
+type IndentedCode struct {
+ blockBase
+ markdown string
+
+ RawCode []IndentedCodeLine
+}
+
+func (b *IndentedCode) Code() (result string) {
+ for _, code := range b.RawCode {
+ result += strings.Repeat(" ", code.Indentation) + b.markdown[code.Range.Position:code.Range.End]
+ }
+ return
+}
+
+func (b *IndentedCode) Continuation(indentation int, r Range) *continuation {
+ if indentation >= 4 {
+ return &continuation{
+ Indentation: indentation - 4,
+ Remaining: r,
+ }
+ }
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return &continuation{
+ Remaining: r,
+ }
+ }
+ return nil
+}
+
+func (b *IndentedCode) AddLine(indentation int, r Range) bool {
+ b.RawCode = append(b.RawCode, IndentedCodeLine{
+ Indentation: indentation,
+ Range: r,
+ })
+ return true
+}
+
+func (b *IndentedCode) Close() {
+ for {
+ last := b.RawCode[len(b.RawCode)-1]
+ s := b.markdown[last.Range.Position:last.Range.End]
+ if strings.TrimRight(s, "\r\n") == "" {
+ b.RawCode = b.RawCode[:len(b.RawCode)-1]
+ } else {
+ break
+ }
+ }
+}
+
+func (b *IndentedCode) AllowsBlockStarts() bool {
+ return false
+}
+
+func indentedCodeStart(markdown string, indentation int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ if len(unmatchedBlocks) > 0 {
+ if _, ok := unmatchedBlocks[len(unmatchedBlocks)-1].(*Paragraph); ok {
+ return nil
+ }
+ } else if len(matchedBlocks) > 0 {
+ if _, ok := matchedBlocks[len(matchedBlocks)-1].(*Paragraph); ok {
+ return nil
+ }
+ }
+
+ if indentation < 4 {
+ return nil
+ }
+
+ s := markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return nil
+ }
+
+ return []Block{
+ &IndentedCode{
+ markdown: markdown,
+ RawCode: []IndentedCodeLine{{
+ Indentation: indentation - 4,
+ Range: r,
+ }},
+ },
+ }
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go
new file mode 100644
index 00000000..a3abccef
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go
@@ -0,0 +1,664 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "container/list"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+type Inline interface {
+ IsInline() bool
+}
+
+type inlineBase struct{}
+
+func (inlineBase) IsInline() bool { return true }
+
+type Text struct {
+ inlineBase
+
+ Text string
+ Range Range
+}
+
+type CodeSpan struct {
+ inlineBase
+
+ Code string
+}
+
+type HardLineBreak struct {
+ inlineBase
+}
+
+type SoftLineBreak struct {
+ inlineBase
+}
+
+type InlineLinkOrImage struct {
+ inlineBase
+
+ Children []Inline
+
+ RawDestination Range
+
+ markdown string
+ rawTitle string
+}
+
+func (i *InlineLinkOrImage) Destination() string {
+ return Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End])
+}
+
+func (i *InlineLinkOrImage) Title() string {
+ return Unescape(i.rawTitle)
+}
+
+type InlineLink struct {
+ InlineLinkOrImage
+}
+
+type InlineImage struct {
+ InlineLinkOrImage
+}
+
+type ReferenceLinkOrImage struct {
+ inlineBase
+ *ReferenceDefinition
+
+ Children []Inline
+}
+
+type ReferenceLink struct {
+ ReferenceLinkOrImage
+}
+
+type ReferenceImage struct {
+ ReferenceLinkOrImage
+}
+
+type Autolink struct {
+ inlineBase
+
+ Children []Inline
+
+ RawDestination Range
+
+ markdown string
+}
+
+func (i *Autolink) Destination() string {
+ destination := Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End])
+
+ if strings.HasPrefix(destination, "www") {
+ destination = "http://" + destination
+ }
+
+ return destination
+}
+
+type delimiterType int
+
+const (
+ linkOpeningDelimiter delimiterType = iota
+ imageOpeningDelimiter
+)
+
+type delimiter struct {
+ Type delimiterType
+ IsInactive bool
+ TextNode int
+ Range Range
+}
+
+type inlineParser struct {
+ markdown string
+ ranges []Range
+ referenceDefinitions []*ReferenceDefinition
+
+ raw string
+ position int
+ inlines []Inline
+ delimiterStack *list.List
+}
+
+func newInlineParser(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) *inlineParser {
+ return &inlineParser{
+ markdown: markdown,
+ ranges: ranges,
+ referenceDefinitions: referenceDefinitions,
+ delimiterStack: list.New(),
+ }
+}
+
+func (p *inlineParser) parseBackticks() {
+ count := 1
+ for i := p.position + 1; i < len(p.raw) && p.raw[i] == '`'; i++ {
+ count++
+ }
+ opening := p.raw[p.position : p.position+count]
+ search := p.position + count
+ for search < len(p.raw) {
+ end := strings.Index(p.raw[search:], opening)
+ if end == -1 {
+ break
+ }
+ if search+end+count < len(p.raw) && p.raw[search+end+count] == '`' {
+ search += end + count
+ for search < len(p.raw) && p.raw[search] == '`' {
+ search++
+ }
+ continue
+ }
+ code := strings.Join(strings.Fields(p.raw[p.position+count:search+end]), " ")
+ p.position = search + end + count
+ p.inlines = append(p.inlines, &CodeSpan{
+ Code: code,
+ })
+ return
+ }
+ p.position += len(opening)
+ absPos := relativeToAbsolutePosition(p.ranges, p.position-len(opening))
+ p.inlines = append(p.inlines, &Text{
+ Text: opening,
+ Range: Range{absPos, absPos + len(opening)},
+ })
+}
+
+func (p *inlineParser) parseLineEnding() {
+ if p.position >= 1 && p.raw[p.position-1] == '\t' {
+ p.inlines = append(p.inlines, &HardLineBreak{})
+ } else if p.position >= 2 && p.raw[p.position-1] == ' ' && (p.raw[p.position-2] == '\t' || p.raw[p.position-1] == ' ') {
+ p.inlines = append(p.inlines, &HardLineBreak{})
+ } else {
+ p.inlines = append(p.inlines, &SoftLineBreak{})
+ }
+ p.position++
+ if p.position < len(p.raw) && p.raw[p.position] == '\n' {
+ p.position++
+ }
+}
+
+func (p *inlineParser) parseEscapeCharacter() {
+ if p.position+1 < len(p.raw) && isEscapableByte(p.raw[p.position+1]) {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position+1)
+ p.inlines = append(p.inlines, &Text{
+ Text: string(p.raw[p.position+1]),
+ Range: Range{absPos, absPos + len(string(p.raw[p.position+1]))},
+ })
+ p.position += 2
+ } else {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.inlines = append(p.inlines, &Text{
+ Text: `\`,
+ Range: Range{absPos, absPos + 1},
+ })
+ p.position++
+ }
+}
+
+func (p *inlineParser) parseText() {
+ if next := strings.IndexAny(p.raw[p.position:], "\r\n\\`&![]wW:"); next == -1 {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.inlines = append(p.inlines, &Text{
+ Text: strings.TrimRightFunc(p.raw[p.position:], isWhitespace),
+ Range: Range{absPos, absPos + len(p.raw[p.position:])},
+ })
+ p.position = len(p.raw)
+ } else {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ if p.raw[p.position+next] == '\r' || p.raw[p.position+next] == '\n' {
+ s := strings.TrimRightFunc(p.raw[p.position:p.position+next], isWhitespace)
+ p.inlines = append(p.inlines, &Text{
+ Text: s,
+ Range: Range{absPos, absPos + len(s)},
+ })
+ } else {
+ if next == 0 {
+ // Always read at least one character since 'w', 'W', and ':' may not actually match another
+ // type of node
+ next = 1
+ }
+
+ p.inlines = append(p.inlines, &Text{
+ Text: p.raw[p.position : p.position+next],
+ Range: Range{absPos, absPos + next},
+ })
+ }
+ p.position += next
+ }
+}
+
+func (p *inlineParser) parseLinkOrImageDelimiter() {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ if p.raw[p.position] == '[' {
+ p.inlines = append(p.inlines, &Text{
+ Text: "[",
+ Range: Range{absPos, absPos + 1},
+ })
+ p.delimiterStack.PushBack(&delimiter{
+ Type: linkOpeningDelimiter,
+ TextNode: len(p.inlines) - 1,
+ Range: Range{p.position, p.position + 1},
+ })
+ p.position++
+ } else if p.raw[p.position] == '!' && p.position+1 < len(p.raw) && p.raw[p.position+1] == '[' {
+ p.inlines = append(p.inlines, &Text{
+ Text: "![",
+ Range: Range{absPos, absPos + 2},
+ })
+ p.delimiterStack.PushBack(&delimiter{
+ Type: imageOpeningDelimiter,
+ TextNode: len(p.inlines) - 1,
+ Range: Range{p.position, p.position + 2},
+ })
+ p.position += 2
+ } else {
+ p.inlines = append(p.inlines, &Text{
+ Text: "!",
+ Range: Range{absPos, absPos + 1},
+ })
+ p.position++
+ }
+}
+
+func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int, isImage bool) (destination, title Range, end int, ok bool) {
+ if position >= len(p.raw) || p.raw[position] != '(' {
+ return
+ }
+ position++
+
+ destinationStart := nextNonWhitespace(p.raw, position)
+ if destinationStart >= len(p.raw) {
+ return
+ } else if p.raw[destinationStart] == ')' {
+ return Range{destinationStart, destinationStart}, Range{destinationStart, destinationStart}, destinationStart + 1, true
+ }
+
+ destination, end, ok = parseLinkDestination(p.raw, destinationStart)
+ if !ok {
+ return
+ }
+ position = end
+
+ if isImage && position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
+ dimensionsStart := nextNonWhitespace(p.raw, position)
+ if dimensionsStart >= len(p.raw) {
+ return
+ }
+
+ if p.raw[dimensionsStart] == '=' {
+ // Read optional image dimensions even if we don't use them
+ _, end, ok = parseImageDimensions(p.raw, dimensionsStart)
+ if !ok {
+ return
+ }
+
+ position = end
+ }
+ }
+
+ if position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
+ titleStart := nextNonWhitespace(p.raw, position)
+ if titleStart >= len(p.raw) {
+ return
+ } else if p.raw[titleStart] == ')' {
+ return destination, Range{titleStart, titleStart}, titleStart + 1, true
+ }
+
+ if p.raw[titleStart] == '"' || p.raw[titleStart] == '\'' || p.raw[titleStart] == '(' {
+ title, end, ok = parseLinkTitle(p.raw, titleStart)
+ if !ok {
+ return
+ }
+ position = end
+ }
+ }
+
+ closingPosition := nextNonWhitespace(p.raw, position)
+ if closingPosition >= len(p.raw) || p.raw[closingPosition] != ')' {
+ return Range{}, Range{}, 0, false
+ }
+
+ return destination, title, closingPosition + 1, true
+}
+
+func (p *inlineParser) referenceDefinition(label string) *ReferenceDefinition {
+ clean := strings.Join(strings.Fields(label), " ")
+ for _, d := range p.referenceDefinitions {
+ if strings.EqualFold(clean, strings.Join(strings.Fields(d.Label()), " ")) {
+ return d
+ }
+ }
+ return nil
+}
+
+func (p *inlineParser) lookForLinkOrImage() {
+ for element := p.delimiterStack.Back(); element != nil; element = element.Prev() {
+ d := element.Value.(*delimiter)
+ if d.Type != imageOpeningDelimiter && d.Type != linkOpeningDelimiter {
+ continue
+ }
+ if d.IsInactive {
+ p.delimiterStack.Remove(element)
+ break
+ }
+
+ isImage := d.Type == imageOpeningDelimiter
+
+ var inline Inline
+
+ if destination, title, next, ok := p.peekAtInlineLinkDestinationAndTitle(p.position+1, isImage); ok {
+ destinationMarkdownPosition := relativeToAbsolutePosition(p.ranges, destination.Position)
+ linkOrImage := InlineLinkOrImage{
+ Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...),
+ RawDestination: Range{destinationMarkdownPosition, destinationMarkdownPosition + destination.End - destination.Position},
+ markdown: p.markdown,
+ rawTitle: p.raw[title.Position:title.End],
+ }
+ if d.Type == imageOpeningDelimiter {
+ inline = &InlineImage{linkOrImage}
+ } else {
+ inline = &InlineLink{linkOrImage}
+ }
+ p.position = next
+ } else {
+ referenceLabel := ""
+ label, next, hasLinkLabel := parseLinkLabel(p.raw, p.position+1)
+ if hasLinkLabel && label.End > label.Position {
+ referenceLabel = p.raw[label.Position:label.End]
+ } else {
+ referenceLabel = p.raw[d.Range.End:p.position]
+ if !hasLinkLabel {
+ next = p.position + 1
+ }
+ }
+ if referenceLabel != "" {
+ if reference := p.referenceDefinition(referenceLabel); reference != nil {
+ linkOrImage := ReferenceLinkOrImage{
+ ReferenceDefinition: reference,
+ Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...),
+ }
+ if d.Type == imageOpeningDelimiter {
+ inline = &ReferenceImage{linkOrImage}
+ } else {
+ inline = &ReferenceLink{linkOrImage}
+ }
+ p.position = next
+ }
+ }
+ }
+
+ if inline != nil {
+ if d.Type == imageOpeningDelimiter {
+ p.inlines = append(p.inlines[:d.TextNode], inline)
+ } else {
+ p.inlines = append(p.inlines[:d.TextNode], inline)
+ for element := element.Prev(); element != nil; element = element.Prev() {
+ if d := element.Value.(*delimiter); d.Type == linkOpeningDelimiter {
+ d.IsInactive = true
+ }
+ }
+ }
+ p.delimiterStack.Remove(element)
+ return
+ } else {
+ p.delimiterStack.Remove(element)
+ break
+ }
+ }
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.inlines = append(p.inlines, &Text{
+ Text: "]",
+ Range: Range{absPos, absPos + 1},
+ })
+ p.position++
+}
+
+func CharacterReference(ref string) string {
+ if ref == "" {
+ return ""
+ }
+ if ref[0] == '#' {
+ if len(ref) < 2 {
+ return ""
+ }
+ n := 0
+ if ref[1] == 'X' || ref[1] == 'x' {
+ if len(ref) < 3 {
+ return ""
+ }
+ for i := 2; i < len(ref); i++ {
+ if i > 9 {
+ return ""
+ }
+ d := ref[i]
+ switch {
+ case d >= '0' && d <= '9':
+ n = n*16 + int(d-'0')
+ case d >= 'a' && d <= 'f':
+ n = n*16 + 10 + int(d-'a')
+ case d >= 'A' && d <= 'F':
+ n = n*16 + 10 + int(d-'A')
+ default:
+ return ""
+ }
+ }
+ } else {
+ for i := 1; i < len(ref); i++ {
+ if i > 8 || ref[i] < '0' || ref[i] > '9' {
+ return ""
+ }
+ n = n*10 + int(ref[i]-'0')
+ }
+ }
+ c := rune(n)
+ if c == '\u0000' || !utf8.ValidRune(c) {
+ return string(unicode.ReplacementChar)
+ }
+ return string(c)
+ }
+ if entity, ok := htmlEntities[ref]; ok {
+ return entity
+ }
+ return ""
+}
+
+func (p *inlineParser) parseCharacterReference() {
+ absPos := relativeToAbsolutePosition(p.ranges, p.position)
+ p.position++
+ if semicolon := strings.IndexByte(p.raw[p.position:], ';'); semicolon == -1 {
+ p.inlines = append(p.inlines, &Text{
+ Text: "&",
+ Range: Range{absPos, absPos + 1},
+ })
+ } else if s := CharacterReference(p.raw[p.position : p.position+semicolon]); s != "" {
+ p.position += semicolon + 1
+ p.inlines = append(p.inlines, &Text{
+ Text: s,
+ Range: Range{absPos, absPos + len(s)},
+ })
+ } else {
+ p.inlines = append(p.inlines, &Text{
+ Text: "&",
+ Range: Range{absPos, absPos + 1},
+ })
+ }
+}
+
+func (p *inlineParser) parseAutolink(c rune) bool {
+ for element := p.delimiterStack.Back(); element != nil; element = element.Prev() {
+ d := element.Value.(*delimiter)
+ if !d.IsInactive {
+ return false
+ }
+ }
+
+ var link Range
+ if c == ':' {
+ var ok bool
+ link, ok = parseURLAutolink(p.raw, p.position)
+
+ if !ok {
+ return false
+ }
+
+ // Since the current position is at the colon, we have to rewind the parsing slightly so that
+ // we don't duplicate the URL scheme
+ rewind := strings.Index(p.raw[link.Position:link.End], ":")
+ if rewind != -1 {
+ lastInline := p.inlines[len(p.inlines)-1]
+ lastText, ok := lastInline.(*Text)
+
+ if !ok {
+ // This should never occur since parseURLAutolink will only return a non-empty value
+ // when the previous text ends in a valid URL protocol which would mean that the previous
+ // node is a Text node
+ return false
+ }
+
+ p.inlines = p.inlines[0 : len(p.inlines)-1]
+ p.inlines = append(p.inlines, &Text{
+ Text: lastText.Text[:len(lastText.Text)-rewind],
+ Range: Range{lastText.Range.Position, lastText.Range.End - rewind},
+ })
+ p.position -= rewind
+ }
+ } else if c == 'w' || c == 'W' {
+ var ok bool
+ link, ok = parseWWWAutolink(p.raw, p.position)
+
+ if !ok {
+ return false
+ }
+ }
+
+ linkMarkdownPosition := relativeToAbsolutePosition(p.ranges, link.Position)
+ linkRange := Range{linkMarkdownPosition, linkMarkdownPosition + link.End - link.Position}
+
+ p.inlines = append(p.inlines, &Autolink{
+ Children: []Inline{
+ &Text{
+ Text: p.raw[link.Position:link.End],
+ Range: linkRange,
+ },
+ },
+ RawDestination: linkRange,
+ markdown: p.markdown,
+ })
+ p.position += (link.End - link.Position)
+
+ return true
+}
+
+func (p *inlineParser) Parse() []Inline {
+ for _, r := range p.ranges {
+ p.raw += p.markdown[r.Position:r.End]
+ }
+
+ for p.position < len(p.raw) {
+ c, _ := utf8.DecodeRuneInString(p.raw[p.position:])
+
+ switch c {
+ case '\r', '\n':
+ p.parseLineEnding()
+ case '\\':
+ p.parseEscapeCharacter()
+ case '`':
+ p.parseBackticks()
+ case '&':
+ p.parseCharacterReference()
+ case '!', '[':
+ p.parseLinkOrImageDelimiter()
+ case ']':
+ p.lookForLinkOrImage()
+ case 'w', 'W', ':':
+ matched := p.parseAutolink(c)
+
+ if !matched {
+ p.parseText()
+ }
+ default:
+ p.parseText()
+ }
+ }
+
+ return p.inlines
+}
+
+func ParseInlines(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) (inlines []Inline) {
+ return newInlineParser(markdown, ranges, referenceDefinitions).Parse()
+}
+
+func MergeInlineText(inlines []Inline) []Inline {
+ var ret []Inline
+ for i, v := range inlines {
+ // always add first node
+ if i == 0 {
+ ret = append(ret, v)
+ continue
+ }
+ // not a text node? nothing to merge
+ text, ok := v.(*Text)
+ if !ok {
+ ret = append(ret, v)
+ continue
+ }
+ // previous node is not a text node? nothing to merge
+ prevText, ok := ret[len(ret)-1].(*Text)
+ if !ok {
+ ret = append(ret, v)
+ continue
+ }
+ // previous node is not right before this one
+ if prevText.Range.End != text.Range.Position {
+ ret = append(ret, v)
+ continue
+ }
+ // we have two consecutive text nodes
+ ret[len(ret)-1] = &Text{
+ Text: prevText.Text + text.Text,
+ Range: Range{prevText.Range.Position, text.Range.End},
+ }
+ }
+ return ret
+}
+
+func Unescape(markdown string) string {
+ ret := ""
+
+ position := 0
+ for position < len(markdown) {
+ c, cSize := utf8.DecodeRuneInString(markdown[position:])
+
+ switch c {
+ case '\\':
+ if position+1 < len(markdown) && isEscapableByte(markdown[position+1]) {
+ ret += string(markdown[position+1])
+ position += 2
+ } else {
+ ret += `\`
+ position++
+ }
+ case '&':
+ position++
+ if semicolon := strings.IndexByte(markdown[position:], ';'); semicolon == -1 {
+ ret += "&"
+ } else if s := CharacterReference(markdown[position : position+semicolon]); s != "" {
+ position += semicolon + 1
+ ret += s
+ } else {
+ ret += "&"
+ }
+ default:
+ ret += string(c)
+ position += cSize
+ }
+ }
+
+ return ret
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/inspect.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inspect.go
new file mode 100644
index 00000000..80b5bc24
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inspect.go
@@ -0,0 +1,78 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+// Inspect traverses the markdown tree in depth-first order. If f returns true, Inspect invokes f
+// recursively for each child of the block or inline, followed by a call of f(nil).
+func Inspect(markdown string, f func(interface{}) bool) {
+ document, referenceDefinitions := Parse(markdown)
+ InspectBlock(document, func(block Block) bool {
+ if !f(block) {
+ return false
+ }
+ switch v := block.(type) {
+ case *Paragraph:
+ for _, inline := range MergeInlineText(v.ParseInlines(referenceDefinitions)) {
+ InspectInline(inline, func(inline Inline) bool {
+ return f(inline)
+ })
+ }
+ }
+ return true
+ })
+}
+
+// InspectBlock traverses the blocks in depth-first order, starting with block. If f returns true,
+// InspectBlock invokes f recursively for each child of the block, followed by a call of f(nil).
+func InspectBlock(block Block, f func(Block) bool) {
+ if !f(block) {
+ return
+ }
+ switch v := block.(type) {
+ case *Document:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ case *List:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ case *ListItem:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ case *BlockQuote:
+ for _, child := range v.Children {
+ InspectBlock(child, f)
+ }
+ }
+ f(nil)
+}
+
+// InspectInline traverses the blocks in depth-first order, starting with block. If f returns true,
+// InspectInline invokes f recursively for each child of the block, followed by a call of f(nil).
+func InspectInline(inline Inline, f func(Inline) bool) {
+ if !f(inline) {
+ return
+ }
+ switch v := inline.(type) {
+ case *InlineImage:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ case *InlineLink:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ case *ReferenceImage:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ case *ReferenceLink:
+ for _, child := range v.Children {
+ InspectInline(child, f)
+ }
+ }
+ f(nil)
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/lines.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/lines.go
new file mode 100644
index 00000000..a38b5164
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/lines.go
@@ -0,0 +1,27 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+type Line struct {
+ Range
+}
+
+func ParseLines(markdown string) (lines []Line) {
+ lineStartPosition := 0
+ isAfterCarriageReturn := false
+ for position, r := range markdown {
+ if r == '\n' {
+ lines = append(lines, Line{Range{lineStartPosition, position + 1}})
+ lineStartPosition = position + 1
+ } else if isAfterCarriageReturn {
+ lines = append(lines, Line{Range{lineStartPosition, position}})
+ lineStartPosition = position
+ }
+ isAfterCarriageReturn = r == '\r'
+ }
+ if lineStartPosition < len(markdown) {
+ lines = append(lines, Line{Range{lineStartPosition, len(markdown)}})
+ }
+ return
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/links.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/links.go
new file mode 100644
index 00000000..9f3128c4
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/links.go
@@ -0,0 +1,184 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "unicode/utf8"
+)
+
+func parseLinkDestination(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) {
+ return
+ }
+
+ if markdown[position] == '<' {
+ isEscaped := false
+
+ for offset, c := range []byte(markdown[position+1:]) {
+ if isEscaped {
+ isEscaped = false
+ if isEscapableByte(c) {
+ continue
+ }
+ }
+
+ if c == '\\' {
+ isEscaped = true
+ } else if c == '<' {
+ break
+ } else if c == '>' {
+ return Range{position + 1, position + 1 + offset}, position + 1 + offset + 1, true
+ } else if isWhitespaceByte(c) {
+ break
+ }
+ }
+ }
+
+ openCount := 0
+ isEscaped := false
+ for offset, c := range []byte(markdown[position:]) {
+ if isEscaped {
+ isEscaped = false
+ if isEscapableByte(c) {
+ continue
+ }
+ }
+
+ switch c {
+ case '\\':
+ isEscaped = true
+ case '(':
+ openCount++
+ case ')':
+ if openCount < 1 {
+ return Range{position, position + offset}, position + offset, true
+ }
+ openCount--
+ default:
+ if isWhitespaceByte(c) {
+ return Range{position, position + offset}, position + offset, true
+ }
+ }
+ }
+ return Range{position, len(markdown)}, len(markdown), true
+}
+
+func parseLinkTitle(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) {
+ return
+ }
+
+ originalPosition := position
+
+ var closer byte
+ switch markdown[position] {
+ case '"', '\'':
+ closer = markdown[position]
+ case '(':
+ closer = ')'
+ default:
+ return
+ }
+ position++
+
+ for position < len(markdown) {
+ switch markdown[position] {
+ case '\\':
+ position++
+ if position < len(markdown) && isEscapableByte(markdown[position]) {
+ position++
+ }
+ case closer:
+ return Range{originalPosition + 1, position}, position + 1, true
+ default:
+ position++
+ }
+ }
+
+ return
+}
+
+func parseLinkLabel(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) || markdown[position] != '[' {
+ return
+ }
+
+ originalPosition := position
+ position++
+
+ for position < len(markdown) {
+ switch markdown[position] {
+ case '\\':
+ position++
+ if position < len(markdown) && isEscapableByte(markdown[position]) {
+ position++
+ }
+ case '[':
+ return
+ case ']':
+ if position-originalPosition >= 1000 && utf8.RuneCountInString(markdown[originalPosition:position]) >= 1000 {
+ return
+ }
+ return Range{originalPosition + 1, position}, position + 1, true
+ default:
+ position++
+ }
+ }
+
+ return
+}
+
+// As a non-standard feature, we allow image links to specify dimensions of the image by adding "=WIDTHxHEIGHT"
+// after the image destination but before the image title like ![alt](http://example.com/image.png =100x200 "title").
+// Both width and height are optional, but at least one of them must be specified.
+func parseImageDimensions(markdown string, position int) (raw Range, next int, ok bool) {
+ if position >= len(markdown) {
+ return
+ }
+
+ originalPosition := position
+
+ // Read =
+ position += 1
+ if position >= len(markdown) {
+ return
+ }
+
+ // Read width
+ hasWidth := false
+ for isNumericByte(markdown[position]) {
+ hasWidth = true
+ position += 1
+ }
+
+ // Look for early end of dimensions
+ if isWhitespaceByte(markdown[position]) || markdown[position] == ')' {
+ return Range{originalPosition, position - 1}, position, true
+ }
+
+ // Read the x
+ if markdown[position] != 'x' && markdown[position] != 'X' {
+ return
+ }
+ position += 1
+
+ // Read height
+ hasHeight := false
+ for isNumericByte(markdown[position]) {
+ hasHeight = true
+ position += 1
+ }
+
+ // Make sure the there's no trailing characters
+ if !isWhitespaceByte(markdown[position]) && markdown[position] != ')' {
+ return
+ }
+
+ if !hasWidth && !hasHeight {
+ // At least one of width or height is required
+ return
+ }
+
+ return Range{originalPosition, position - 1}, position, true
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/list.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/list.go
new file mode 100644
index 00000000..aea71156
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/list.go
@@ -0,0 +1,220 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type ListItem struct {
+ blockBase
+ markdown string
+ hasTrailingBlankLine bool
+ hasBlankLineBetweenChildren bool
+
+ Indentation int
+ Children []Block
+}
+
+func (b *ListItem) Continuation(indentation int, r Range) *continuation {
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ if b.Children == nil {
+ return nil
+ }
+ return &continuation{
+ Remaining: r,
+ }
+ }
+ if indentation < b.Indentation {
+ return nil
+ }
+ return &continuation{
+ Indentation: indentation - b.Indentation,
+ Remaining: r,
+ }
+}
+
+func (b *ListItem) AddChild(openBlocks []Block) []Block {
+ b.Children = append(b.Children, openBlocks[0])
+ if b.hasTrailingBlankLine {
+ b.hasBlankLineBetweenChildren = true
+ }
+ b.hasTrailingBlankLine = false
+ return openBlocks
+}
+
+func (b *ListItem) AddLine(indentation int, r Range) bool {
+ isBlank := strings.TrimSpace(b.markdown[r.Position:r.End]) == ""
+ if isBlank {
+ b.hasTrailingBlankLine = true
+ }
+ return false
+}
+
+func (b *ListItem) HasTrailingBlankLine() bool {
+ return b.hasTrailingBlankLine || (len(b.Children) > 0 && b.Children[len(b.Children)-1].HasTrailingBlankLine())
+}
+
+func (b *ListItem) isLoose() bool {
+ if b.hasBlankLineBetweenChildren {
+ return true
+ }
+ for i, child := range b.Children {
+ if i < len(b.Children)-1 && child.HasTrailingBlankLine() {
+ return true
+ }
+ }
+ return false
+}
+
+type List struct {
+ blockBase
+ markdown string
+ hasTrailingBlankLine bool
+ hasBlankLineBetweenChildren bool
+
+ IsLoose bool
+ IsOrdered bool
+ OrderedStart int
+ BulletOrDelimiter byte
+ Children []*ListItem
+}
+
+func (b *List) Continuation(indentation int, r Range) *continuation {
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return &continuation{
+ Remaining: r,
+ }
+ }
+ return &continuation{
+ Indentation: indentation,
+ Remaining: r,
+ }
+}
+
+func (b *List) AddChild(openBlocks []Block) []Block {
+ if item, ok := openBlocks[0].(*ListItem); ok {
+ b.Children = append(b.Children, item)
+ if b.hasTrailingBlankLine {
+ b.hasBlankLineBetweenChildren = true
+ }
+ b.hasTrailingBlankLine = false
+ return openBlocks
+ } else if list, ok := openBlocks[0].(*List); ok {
+ if len(list.Children) == 1 && list.IsOrdered == b.IsOrdered && list.BulletOrDelimiter == b.BulletOrDelimiter {
+ return b.AddChild(openBlocks[1:])
+ }
+ }
+ return nil
+}
+
+func (b *List) AddLine(indentation int, r Range) bool {
+ isBlank := strings.TrimSpace(b.markdown[r.Position:r.End]) == ""
+ if isBlank {
+ b.hasTrailingBlankLine = true
+ }
+ return false
+}
+
+func (b *List) HasTrailingBlankLine() bool {
+ return b.hasTrailingBlankLine || (len(b.Children) > 0 && b.Children[len(b.Children)-1].HasTrailingBlankLine())
+}
+
+func (b *List) isLoose() bool {
+ if b.hasBlankLineBetweenChildren {
+ return true
+ }
+ for i, child := range b.Children {
+ if child.isLoose() || (i < len(b.Children)-1 && child.HasTrailingBlankLine()) {
+ return true
+ }
+ }
+ return false
+}
+
+func (b *List) Close() {
+ b.IsLoose = b.isLoose()
+}
+
+func parseListMarker(markdown string, r Range) (success, isOrdered bool, orderedStart int, bulletOrDelimiter byte, markerWidth int, remaining Range) {
+ digits := 0
+ n := 0
+ for i := r.Position; i < r.End && markdown[i] >= '0' && markdown[i] <= '9'; i++ {
+ digits++
+ n = n*10 + int(markdown[i]-'0')
+ }
+ if digits > 0 {
+ if digits > 9 || r.Position+digits >= r.End {
+ return
+ }
+ next := markdown[r.Position+digits]
+ if next != '.' && next != ')' {
+ return
+ }
+ return true, true, n, next, digits + 1, Range{r.Position + digits + 1, r.End}
+ }
+ if r.Position >= r.End {
+ return
+ }
+ next := markdown[r.Position]
+ if next != '-' && next != '+' && next != '*' {
+ return
+ }
+ return true, false, 0, next, 1, Range{r.Position + 1, r.End}
+}
+
+func listStart(markdown string, indent int, r Range, matchedBlocks, unmatchedBlocks []Block) []Block {
+ afterList := false
+ if len(matchedBlocks) > 0 {
+ _, afterList = matchedBlocks[len(matchedBlocks)-1].(*List)
+ }
+ if !afterList && indent > 3 {
+ return nil
+ }
+
+ success, isOrdered, orderedStart, bulletOrDelimiter, markerWidth, remaining := parseListMarker(markdown, r)
+ if !success {
+ return nil
+ }
+
+ isBlank := strings.TrimSpace(markdown[remaining.Position:remaining.End]) == ""
+ if len(matchedBlocks) > 0 && len(unmatchedBlocks) == 0 {
+ if _, ok := matchedBlocks[len(matchedBlocks)-1].(*Paragraph); ok {
+ if isBlank || (isOrdered && orderedStart != 1) {
+ return nil
+ }
+ }
+ }
+
+ indentAfterMarker, indentBytesAfterMarker := countIndentation(markdown, remaining)
+ if !isBlank && indentAfterMarker < 1 {
+ return nil
+ }
+
+ remaining = Range{remaining.Position + indentBytesAfterMarker, remaining.End}
+ consumedIndentAfterMarker := indentAfterMarker
+ if isBlank || indentAfterMarker >= 5 {
+ consumedIndentAfterMarker = 1
+ }
+
+ listItem := &ListItem{
+ markdown: markdown,
+ Indentation: indent + markerWidth + consumedIndentAfterMarker,
+ }
+ list := &List{
+ markdown: markdown,
+ IsOrdered: isOrdered,
+ OrderedStart: orderedStart,
+ BulletOrDelimiter: bulletOrDelimiter,
+ Children: []*ListItem{listItem},
+ }
+ ret := []Block{list, listItem}
+ if descendants := blockStartOrParagraph(markdown, indentAfterMarker-consumedIndentAfterMarker, remaining, nil, nil); descendants != nil {
+ listItem.Children = append(listItem.Children, descendants[0])
+ ret = append(ret, descendants...)
+ }
+ return ret
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/markdown.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/markdown.go
new file mode 100644
index 00000000..57b10f3f
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/markdown.go
@@ -0,0 +1,148 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+// This package implements a parser for the subset of the CommonMark spec necessary for us to do
+// server-side processing. It is not a full implementation and lacks many features. But it is
+// complete enough to efficiently and accurately allow us to do what we need to like rewrite image
+// URLs for proxying.
+package markdown
+
+import (
+ "strings"
+)
+
+func isEscapable(c rune) bool {
+ return c > ' ' && (c < '0' || (c > '9' && (c < 'A' || (c > 'Z' && (c < 'a' || (c > 'z' && c <= '~'))))))
+}
+
+func isEscapableByte(c byte) bool {
+ return isEscapable(rune(c))
+}
+
+func isWhitespace(c rune) bool {
+ switch c {
+ case ' ', '\t', '\n', '\u000b', '\u000c', '\r':
+ return true
+ default:
+ return false
+ }
+}
+
+func isWhitespaceByte(c byte) bool {
+ return isWhitespace(rune(c))
+}
+
+func isNumeric(c rune) bool {
+ return c >= '0' && c <= '9'
+}
+
+func isNumericByte(c byte) bool {
+ return isNumeric(rune(c))
+}
+
+func isHex(c rune) bool {
+ return isNumeric(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
+}
+
+func isHexByte(c byte) bool {
+ return isHex(rune(c))
+}
+
+func isAlphanumeric(c rune) bool {
+ return isNumeric(c) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+}
+
+func isAlphanumericByte(c byte) bool {
+ return isAlphanumeric(rune(c))
+}
+
+func nextNonWhitespace(markdown string, position int) int {
+ for offset, c := range []byte(markdown[position:]) {
+ if !isWhitespaceByte(c) {
+ return position + offset
+ }
+ }
+ return len(markdown)
+}
+
+func nextLine(markdown string, position int) (linePosition int, skippedNonWhitespace bool) {
+ for i := position; i < len(markdown); i++ {
+ c := markdown[i]
+ if c == '\r' {
+ if i+1 < len(markdown) && markdown[i+1] == '\n' {
+ return i + 2, skippedNonWhitespace
+ }
+ return i + 1, skippedNonWhitespace
+ } else if c == '\n' {
+ return i + 1, skippedNonWhitespace
+ } else if !isWhitespaceByte(c) {
+ skippedNonWhitespace = true
+ }
+ }
+ return len(markdown), skippedNonWhitespace
+}
+
+func countIndentation(markdown string, r Range) (spaces, bytes int) {
+ for i := r.Position; i < r.End; i++ {
+ if markdown[i] == ' ' {
+ spaces++
+ bytes++
+ } else if markdown[i] == '\t' {
+ spaces += 4
+ bytes++
+ } else {
+ break
+ }
+ }
+ return
+}
+
+func trimLeftSpace(markdown string, r Range) Range {
+ s := markdown[r.Position:r.End]
+ trimmed := strings.TrimLeftFunc(s, isWhitespace)
+ return Range{r.Position, r.End - (len(s) - len(trimmed))}
+}
+
+func trimRightSpace(markdown string, r Range) Range {
+ s := markdown[r.Position:r.End]
+ trimmed := strings.TrimRightFunc(s, isWhitespace)
+ return Range{r.Position, r.End - (len(s) - len(trimmed))}
+}
+
+func relativeToAbsolutePosition(ranges []Range, position int) int {
+ rem := position
+ for _, r := range ranges {
+ l := r.End - r.Position
+ if rem < l {
+ return r.Position + rem
+ }
+ rem -= l
+ }
+ if len(ranges) == 0 {
+ return 0
+ }
+ return ranges[len(ranges)-1].End
+}
+
+func trimBytesFromRanges(ranges []Range, bytes int) (result []Range) {
+ rem := bytes
+ for _, r := range ranges {
+ if rem == 0 {
+ result = append(result, r)
+ continue
+ }
+ l := r.End - r.Position
+ if rem < l {
+ result = append(result, Range{r.Position + rem, r.End})
+ rem = 0
+ continue
+ }
+ rem -= l
+ }
+ return
+}
+
+func Parse(markdown string) (*Document, []*ReferenceDefinition) {
+ lines := ParseLines(markdown)
+ return ParseBlocks(markdown, lines)
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/paragraph.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/paragraph.go
new file mode 100644
index 00000000..6a40fdf8
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/paragraph.go
@@ -0,0 +1,71 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+import (
+ "strings"
+)
+
+type Paragraph struct {
+ blockBase
+ markdown string
+
+ Text []Range
+ ReferenceDefinitions []*ReferenceDefinition
+}
+
+func (b *Paragraph) ParseInlines(referenceDefinitions []*ReferenceDefinition) []Inline {
+ return ParseInlines(b.markdown, b.Text, referenceDefinitions)
+}
+
+func (b *Paragraph) Continuation(indentation int, r Range) *continuation {
+ s := b.markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return nil
+ }
+ return &continuation{
+ Indentation: indentation,
+ Remaining: r,
+ }
+}
+
+func (b *Paragraph) Close() {
+ for {
+ for i := 0; i < len(b.Text); i++ {
+ b.Text[i] = trimLeftSpace(b.markdown, b.Text[i])
+ if b.Text[i].Position < b.Text[i].End {
+ break
+ }
+ }
+
+ if len(b.Text) == 0 || b.Text[0].Position < b.Text[0].End && b.markdown[b.Text[0].Position] != '[' {
+ break
+ }
+
+ definition, remaining := parseReferenceDefinition(b.markdown, b.Text)
+ if definition == nil {
+ break
+ }
+ b.ReferenceDefinitions = append(b.ReferenceDefinitions, definition)
+ b.Text = remaining
+ }
+
+ for i := len(b.Text) - 1; i >= 0; i-- {
+ b.Text[i] = trimRightSpace(b.markdown, b.Text[i])
+ if b.Text[i].Position < b.Text[i].End {
+ break
+ }
+ }
+}
+
+func newParagraph(markdown string, r Range) *Paragraph {
+ s := markdown[r.Position:r.End]
+ if strings.TrimSpace(s) == "" {
+ return nil
+ }
+ return &Paragraph{
+ markdown: markdown,
+ Text: []Range{r},
+ }
+}
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/reference_definition.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/reference_definition.go
new file mode 100644
index 00000000..e2d0be35
--- /dev/null
+++ b/vendor/github.com/mattermost/mattermost-server/utils/markdown/reference_definition.go
@@ -0,0 +1,75 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package markdown
+
+type ReferenceDefinition struct {
+ RawDestination Range
+
+ markdown string
+ rawLabel string
+ rawTitle string
+}
+
+func (d *ReferenceDefinition) Destination() string {
+ return Unescape(d.markdown[d.RawDestination.Position:d.RawDestination.End])
+}
+
+func (d *ReferenceDefinition) Label() string {
+ return d.rawLabel
+}
+
+func (d *ReferenceDefinition) Title() string {
+ return Unescape(d.rawTitle)
+}
+
+func parseReferenceDefinition(markdown string, ranges []Range) (*ReferenceDefinition, []Range) {
+ raw := ""
+ for _, r := range ranges {
+ raw += markdown[r.Position:r.End]
+ }
+
+ label, next, ok := parseLinkLabel(raw, 0)
+ if !ok {
+ return nil, nil
+ }
+ position := next
+
+ if position >= len(raw) || raw[position] != ':' {
+ return nil, nil
+ }
+ position++
+
+ destination, next, ok := parseLinkDestination(raw, nextNonWhitespace(raw, position))
+ if !ok {
+ return nil, nil
+ }
+ position = next
+
+ absoluteDestination := relativeToAbsolutePosition(ranges, destination.Position)
+ ret := &ReferenceDefinition{
+ RawDestination: Range{absoluteDestination, absoluteDestination + destination.End - destination.Position},
+ markdown: markdown,
+ rawLabel: raw[label.Position:label.End],
+ }
+
+ if position < len(raw) && isWhitespaceByte(raw[position]) {
+ title, next, ok := parseLinkTitle(raw, nextNonWhitespace(raw, position))
+ if !ok {
+ if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace {
+ return ret, trimBytesFromRanges(ranges, nextLine)
+ }
+ return nil, nil
+ }
+ if nextLine, skippedNonWhitespace := nextLine(raw, next); !skippedNonWhitespace {
+ ret.rawTitle = raw[title.Position:title.End]
+ return ret, trimBytesFromRanges(ranges, nextLine)
+ }
+ }
+
+ if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace {
+ return ret, trimBytesFromRanges(ranges, nextLine)
+ }
+
+ return nil, nil
+}
diff --git a/vendor/github.com/mattermost/platform/NOTICE.txt b/vendor/github.com/mattermost/platform/NOTICE.txt
deleted file mode 100644
index f68ef872..00000000
--- a/vendor/github.com/mattermost/platform/NOTICE.txt
+++ /dev/null
@@ -1,2935 +0,0 @@
-Mattermost Server
-© 2015-present Mattermost, Inc. All Rights Reserved. See LICENSE.txt for license information.
-
-NOTICES:
---------
-
-This document includes a list of open source components used in Mattermost Server, including those that have been modified.
-
----
-
-This product contains a modified portion of 'jquery-dragster', a drag and drop listener
-by Jan Martin.
-
-* HOMEPAGE:
- * https://github.com/catmanjan/jquery-dragster
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Jan Martin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'golang-lru', a golang LRU cache by hashicorp,
-based on Groupcache by Google Inc.
-
-* HOMEPAGE:
- * https://github.com/hashicorp/golang-lru
-
-* LICENSE:
-
-This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a
-copy of the MPL was not distributed with this file, You can obtain one at
-http://mozilla.org/MPL/2.0/.
-
----
-
-This product contains a modified portion of 'golang-freetype', a port of the Freetype
-font rasterizer (www.freetype.org) to the Go programming.
-
-Portions of this software are copyright © 2010 The FreeType Project (www.freetype.org).
-All rights reserved.
-
-* HOMEPAGE:
- * http://www.freetype.org
-
-* LICENSE:
-
- The FreeType Project LICENSE
- ----------------------------
-
- 2006-Jan-27
-
- Copyright 1996-2002, 2006 by
- David Turner, Robert Wilhelm, and Werner Lemberg
-
-
-
-Introduction
-============
-
- The FreeType Project is distributed in several archive packages;
- some of them may contain, in addition to the FreeType font engine,
- various tools and contributions which rely on, or relate to, the
- FreeType Project.
-
- This license applies to all files found in such packages, and
- which do not fall under their own explicit license. The license
- affects thus the FreeType font engine, the test programs,
- documentation and makefiles, at the very least.
-
- This license was inspired by the BSD, Artistic, and IJG
- (Independent JPEG Group) licenses, which all encourage inclusion
- and use of free software in commercial and freeware products
- alike. As a consequence, its main points are that:
-
- o We don't promise that this software works. However, we will be
- interested in any kind of bug reports. (`as is' distribution)
-
- o You can use this software for whatever you want, in parts or
- full form, without having to pay us. (`royalty-free' usage)
-
- o You may not pretend that you wrote this software. If you use
- it, or only parts of it, in a program, you must acknowledge
- somewhere in your documentation that you have used the
- FreeType code. (`credits')
-
- We specifically permit and encourage the inclusion of this
- software, with or without modifications, in commercial products.
- We disclaim all warranties covering The FreeType Project and
- assume no liability related to The FreeType Project.
-
-
- Finally, many people asked us for a preferred form for a
- credit/disclaimer to use in compliance with this license. We thus
- encourage you to use the following text:
-
- """
- Portions of this software are copyright � <year> The FreeType
- Project (www.freetype.org). All rights reserved.
- """
-
- Please replace <year> with the value from the FreeType version you
- actually use.
-
-
-Legal Terms
-===========
-
-0. Definitions
---------------
-
- Throughout this license, the terms `package', `FreeType Project',
- and `FreeType archive' refer to the set of files originally
- distributed by the authors (David Turner, Robert Wilhelm, and
- Werner Lemberg) as the `FreeType Project', be they named as alpha,
- beta or final release.
-
- `You' refers to the licensee, or person using the project, where
- `using' is a generic term including compiling the project's source
- code as well as linking it to form a `program' or `executable'.
- This program is referred to as `a program using the FreeType
- engine'.
-
- This license applies to all files distributed in the original
- FreeType Project, including all source code, binaries and
- documentation, unless otherwise stated in the file in its
- original, unmodified form as distributed in the original archive.
- If you are unsure whether or not a particular file is covered by
- this license, you must contact us to verify this.
-
- The FreeType Project is copyright (C) 1996-2000 by David Turner,
- Robert Wilhelm, and Werner Lemberg. All rights reserved except as
- specified below.
-
-1. No Warranty
---------------
-
- THE FREETYPE PROJECT IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
- BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
- USE, OF THE FREETYPE PROJECT.
-
-2. Redistribution
------------------
-
- This license grants a worldwide, royalty-free, perpetual and
- irrevocable right and license to use, execute, perform, compile,
- display, copy, create derivative works of, distribute and
- sublicense the FreeType Project (in both source and object code
- forms) and derivative works thereof for any purpose; and to
- authorize others to exercise some or all of the rights granted
- herein, subject to the following conditions:
-
- o Redistribution of source code must retain this license file
- (`FTL.TXT') unaltered; any additions, deletions or changes to
- the original files must be clearly indicated in accompanying
- documentation. The copyright notices of the unaltered,
- original files must be preserved in all copies of source
- files.
-
- o Redistribution in binary form must provide a disclaimer that
- states that the software is based in part of the work of the
- FreeType Team, in the distribution documentation. We also
- encourage you to put an URL to the FreeType web page in your
- documentation, though this isn't mandatory.
-
- These conditions apply to any software derived from or based on
- the FreeType Project, not just the unmodified files. If you use
- our work, you must acknowledge us. However, no fee need be paid
- to us.
-
-3. Advertising
---------------
-
- Neither the FreeType authors and contributors nor you shall use
- the name of the other for commercial, advertising, or promotional
- purposes without specific prior written permission.
-
- We suggest, but do not require, that you use one or more of the
- following phrases to refer to this software in your documentation
- or advertising materials: `FreeType Project', `FreeType Engine',
- `FreeType library', or `FreeType Distribution'.
-
- As you have not signed this license, you are not required to
- accept it. However, as the FreeType Project is copyrighted
- material, only this license, or another one contracted with the
- authors, grants you the right to use, distribute, and modify it.
- Therefore, by using, distributing, or modifying the FreeType
- Project, you indicate that you understand and accept all the terms
- of this license.
-
-4. Contacts
------------
-
- There are two mailing lists related to FreeType:
-
- o freetype@nongnu.org
-
- Discusses general use and applications of FreeType, as well as
- future and wanted additions to the library and distribution.
- If you are looking for support, start in this list if you
- haven't found anything to help you in the documentation.
-
- o freetype-devel@nongnu.org
-
- Discusses bugs, as well as engine internals, design issues,
- specific licenses, porting, etc.
-
- Our home page can be found at
-
- http://www.freetype.org
-
---- end of FTL.TXT ---
-
----
-
-This product contains a modified portion of 'gemoji', a collection of emoji images and names by Apple Inc. and other contributors.
-
-* HOMEPAGE:
- * https://github.com/github/gemoji/blob/master/LICENSE
-
-* LICENSE:
-
-octocat, squirrel, shipit
-Copyright (c) 2013 GitHub Inc. All rights reserved.
-
-bowtie, neckbeard, fu
-Copyright (c) 2013 37signals, LLC. All rights reserved.
-
-feelsgood, finnadie, goberserk, godmode, hurtrealbad, rage 1-4, suspect
-Copyright (c) 2013 id Software. All rights reserved.
-
-trollface
-Copyright (c) 2013 whynne@deviantart. All rights reserved.
-
-All other images
-Copyright (c) 2013 Apple Inc. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this
-software and associated documentation files (the "Software"), to deal in the Software
-without restriction, including without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be included in all copies
-or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'goexif', which provides decoding of basic exif and tiff encoded data.
-
-by Robert Carlsen & Contributors
-
-* HOMEPAGE:
- * https://github.com/rwcarlsen/goexif
-
-* LICENSE:
-
-Copyright (c) 2012, Robert Carlsen & Contributors
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'graphics-go', an implementation of basic image manipulation operations in the Go programming language.
-
-by The Graphics-Go Authors
-
-* HOMEPAGE:
- * https://code.google.com/p/graphics-go/
-
-* LICENSE:
-
-Copyright (c) 2011 The Graphics-Go Authors. All rights reserved.
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'babel-es6-polyfill', a pure ES6 polyfill for Babel that only includes standardised features.
-
-by Jason Berry
-
-* HOMEPAGE:
- * https://github.com/JasonBerry/babel-es6-polyfill
-
-* LICENSE:
-
-Copyright (c) 2015 Jason Berry
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'go-ldap', basic LDAP v3 functionality for the GO programming language.
-
-by The Go Authors
-
-* HOMEPAGE:
- * https://github.com/go-ldap/ldap
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'Google Fonts', various Open Source fonts for use on the web.
-
-by Google Inc.
-
-* HOMEPAGE:
- * https://www.google.com/fonts
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Copyright 2011 Google Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-# Fonts licensed under CC-BY 3.0:
-
-CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
-License
-
-THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
-
-BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
-
-1. Definitions
-
-"Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
-"Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
-"Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
-"Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
-"Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
-"Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
-"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
-"Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
-"Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
-
-2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
-
-3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
-
-to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
-to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
-to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
-to Distribute and Publicly Perform Adaptations.
-For the avoidance of doubt:
-
-Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
-Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
-Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
-The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
-
-4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
-
-You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested.
-If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
-Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
-
-5. Representations, Warranties and Disclaimer
-
-UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
-
-6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-7. Termination
-
-This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
-Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
-
-8. Miscellaneous
-
-Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
-Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
-If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
-No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
-This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
-The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
-
-Creative Commons Notice
-
-Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
-
-Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
-
-Creative Commons may be contacted at https://creativecommons.org/.
-
----
-
-This product contains a modified portion of 'go-i18n', a Go package and a command that translates Go programs into multiple languages
-by Nick Snyder.
-
-* HOMEPAGE:
- * https://github.com/nicksnyder/go-i18n
-
-* LICENSE:
-
-Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'dgoogauth', a go implementation of the Google Authenticator library by Damian Gryski.
-
-* HOMEPAGE:
- * https://github.com/dgryski/dgoogauth
-
-* LICENSE:
-
-Copyright 2012 Damian Gryski
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of 'fbjs', a collection of JavaScript utilities by Facebook, Inc.
-
-* HOMEPAGE:
- * https://github.com/facebook/fbjs
-
-* LICENSE:
-
-BSD License
-
-For fbjs software
-
-Copyright (c) 2013-present, Facebook, Inc.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- * Neither the name Facebook nor the names of its contributors may be used to
- endorse or promote products derived from this software without specific
- prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'marked', a full-featured markdown parser and compiler, written in JavaScript. Built for speed by Christopher Jeffrey.
-
-* HOMEPAGE:
- * https://github.com/chjj/marked
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2011-2014, Christopher Jeffrey.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-No attribution is required by products that make use of this software.
-
----
-
-This product contains a modified portion of 'gziphandler', a tiny Go package which wraps HTTP handlers to transparently gzip the response body, for clients which support it by The New York Times Company.
-
-* HOMEPAGE:
- * https://github.com/NYTimes/gziphandler
-
-* LICENSE:
-
-Apache License 2.0
-
-Copyright (c) 2015 The New York Times Company
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this library except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of 'handlers', a collection of handlers (aka "HTTP middleware") for use with Go's net/http package (or any framework supporting http.Handler) by The Gorilla Handlers Authors.
-
-* HOMEPAGE:
- * https://github.com/gorilla/handlers
-
-* LICENSE:
-
-Copyright (c) 2013 The Gorilla Handlers Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'log4go', an unmaintained fork, left only so it doesn't break imports by Kyle Lemons.
-
-* HOMEPAGE:
- * https://github.com/alecthomas/log4go
-
-* LICENSE:
-
-Copyright (c) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'manners', a package imaging providing basic image manipulation functions (resize, rotate, flip, crop, etc.) by Grigory Dryapak.
-
-* HOMEPAGE:
- * https://github.com/disintegration/imaging
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2012-2014 Grigory Dryapak
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'gorp', to save time, minimize the drudgery of getting data in and out of the database, and help code focus on algorithms, not infrastructure by James Cooper.
-
-* HOMEPAGE:
- * https://github.com/go-gorp/gorp
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2012 James Cooper <james@bitmechanic.com>
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-'Software'), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'mysql', a lightweight and fast MySQL driver for Go's (golang) database/sql package by Mozilla Public.
-
-* HOMEPAGE:
- * https://github.com/go-sql-driver/mysql
-
-* LICENSE:
-
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
- means each individual or legal entity that creates, contributes to
- the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
- means the combination of the Contributions of others (if any) used
- by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
- means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
- means Source Code Form to which the initial Contributor has attached
- the notice in Exhibit A, the Executable Form of such Source Code
- Form, and Modifications of such Source Code Form, in each case
- including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
- means
-
- (a) that the initial Contributor has attached the notice described
- in Exhibit B to the Covered Software; or
-
- (b) that the Covered Software was made available under the terms of
- version 1.1 or earlier of the License, but not also under the
- terms of a Secondary License.
-
-1.6. "Executable Form"
- means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
- a separate file or files, that is not Covered Software.
-
-1.8. "License"
- means this document.
-
-1.9. "Licensable"
- means having the right to grant, to the maximum extent possible,
- whether at the time of the initial grant or subsequently, any and
- all of the rights conveyed by this License.
-
-1.10. "Modifications"
- means any of the following:
-
- (a) any file in Source Code Form that results from an addition to,
- deletion from, or modification of the contents of Covered
- Software; or
-
- (b) any new file in Source Code Form that contains any Covered
- Software.
-
-1.11. "Patent Claims" of a Contributor
- means any patent claim(s), including without limitation, method,
- process, and apparatus claims, in any patent Licensable by such
- Contributor that would be infringed, but for the grant of the
- License, by the making, using, selling, offering for sale, having
- made, import, or transfer of either its Contributions or its
- Contributor Version.
-
-1.12. "Secondary License"
- means either the GNU General Public License, Version 2.0, the GNU
- Lesser General Public License, Version 2.1, the GNU Affero General
- Public License, Version 3.0, or any later versions of those
- licenses.
-
-1.13. "Source Code Form"
- means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
- means an individual or a legal entity exercising rights under this
- License. For legal entities, "You" includes any entity that
- controls, is controlled by, or is under common control with You. For
- purposes of this definition, "control" means (a) the power, direct
- or indirect, to cause the direction or management of such entity,
- whether by contract or otherwise, or (b) ownership of more than
- fifty percent (50%) of the outstanding shares or beneficial
- ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
- Licensable by such Contributor to use, reproduce, make available,
- modify, display, perform, distribute, and otherwise exploit its
- Contributions, either on an unmodified basis, with Modifications, or
- as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
- for sale, have made, import, and otherwise transfer either its
- Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
- or
-
-(b) for infringements caused by: (i) Your and any other third party's
- modifications of Covered Software, or (ii) the combination of its
- Contributions with other software (except as part of its Contributor
- Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
- its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
- Form, as described in Section 3.1, and You must inform recipients of
- the Executable Form how they can obtain a copy of such Source Code
- Form by reasonable means in a timely manner, at a charge no more
- than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
- License, or sublicense it under different terms, provided that the
- license for the Executable Form does not attempt to limit or alter
- the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-* *
-* 6. Disclaimer of Warranty *
-* ------------------------- *
-* *
-* Covered Software is provided under this License on an "as is" *
-* basis, without warranty of any kind, either expressed, implied, or *
-* statutory, including, without limitation, warranties that the *
-* Covered Software is free of defects, merchantable, fit for a *
-* particular purpose or non-infringing. The entire risk as to the *
-* quality and performance of the Covered Software is with You. *
-* Should any Covered Software prove defective in any respect, You *
-* (not any Contributor) assume the cost of any necessary servicing, *
-* repair, or correction. This disclaimer of warranty constitutes an *
-* essential part of this License. No use of any Covered Software is *
-* authorized under this License except under this disclaimer. *
-* *
-************************************************************************
-
-************************************************************************
-* *
-* 7. Limitation of Liability *
-* -------------------------- *
-* *
-* Under no circumstances and under no legal theory, whether tort *
-* (including negligence), contract, or otherwise, shall any *
-* Contributor, or anyone who distributes Covered Software as *
-* permitted above, be liable to You for any direct, indirect, *
-* special, incidental, or consequential damages of any character *
-* including, without limitation, damages for lost profits, loss of *
-* goodwill, work stoppage, computer failure or malfunction, or any *
-* and all other commercial damages or losses, even if such party *
-* shall have been informed of the possibility of such damages. This *
-* limitation of liability shall not apply to liability for death or *
-* personal injury resulting from such party's negligence to the *
-* extent applicable law prohibits such limitation. Some *
-* jurisdictions do not allow the exclusion or limitation of *
-* incidental or consequential damages, so this exclusion and *
-* limitation may not apply to You. *
-* *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
- This Source Code Form is "Incompatible With Secondary Licenses", as
- defined by the Mozilla Public License, v. 2.0.
-
----
-
-This product contains a modified portion of 'context', a golang registry for global request variables by Rodrigo Moraes.
-
-* HOMEPAGE:
- * https://github.com/gorilla/context
-
-* LICENSE:
-
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'mux', a powerful URL router and dispatcher for golang by Rodrigo Moraes.
-
-* HOMEPAGE:
- * https://github.com/gorilla/mux
-
-* LICENSE:
-
-Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'websocket', a WebSocket implementation for Go by The Gorilla WebSocket Authors.
-
-* HOMEPAGE:
- * https://github.com/gorilla/websocket
-
-* LICENSE:
-
-Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## pq
-
-This product contains a modified portion of 'pq', a Pure Go Postgres driver for database/sql by Blake Mizerany and Contributors.
-
-* HOMEPAGE:
- * https://github.com/lib/pq
-
-* LICENSE:
-
-Copyright (c) 2011-2013, 'pq' Contributors
-Portions Copyright (C) 2011 Blake Mizerany
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'user_agent', a Go library that parses HTTP User Agents by Miquel Sabaté Solà.
-
-* HOMEPAGE:
- * https://github.com/mssola/user_agent
-
-* LICENSE:
-
-Copyright (c) 2012-2016 Miquel Sabaté Solà
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of 'uuid', a package generates and inspects UUIDs based on RFC 412 and DCE 1.1: Authentication and Security Services by Google Inc.
-
-* HOMEPAGE:
- * https://github.com/pborman/uuid
-
-* LICENSE:
-
-Copyright (c) 2009,2014 Google Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'yaml', a YAML support for the Go language by Canonical Inc.
-
-* HOMEPAGE:
- * https://gopkg.in/yaml.v2
-
-* LICENSE:
-
-
-Copyright (c) 2011-2014 - Canonical Inc.
-
-This software is licensed under the LGPLv3, included below.
-
-As a special exception to the GNU Lesser General Public License version 3
-("LGPL3"), the copyright holders of this Library give you permission to
-convey to a third party a Combined Work that links statically or dynamically
-to this Library without providing any Minimal Corresponding Source or
-Minimal Application Code as set out in 4d or providing the installation
-information set out in section 4e, provided that you comply with the other
-provisions of LGPL3 and provided that you meet, for the Application the
-terms and conditions of the license(s) which apply to the Application.
-
-Except as stated in this special exception, the provisions of LGPL3 will
-continue to comply in full to this Library. If you modify this Library, you
-may apply this exception to your version of this Library, but you are not
-obliged to do so. If you do not wish to do so, delete this exception
-statement from your version. This exception does not (and cannot) modify any
-license terms which apply to the Application, with which you must still
-comply.
-
-
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
-
----
-
-This product contains a modified portion of 'throttled', a package store offering a memory-based and a Redis-based throttled by Martin Angers.
-
-* HOMEPAGE:
- * http://gopkg.in/throttled/throttled.v1/store
-
-* LICENSE:
-
-Copyright (c) 2014, Martin Angers
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'throttled', a package throttled implementing different throttling strategies for controlling access to HTTP handlers by Martin Angers and Contributors.
-
-* HOMEPAGE:
- * http://gopkg.in/throttled/throttled.v1
-
-* LICENSE:
-
-Copyright (c) 2014, Martin Angers and Contributors.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'fsnotify', to utilize golang.org/x/sys rather than syscall from the standard library by The Go Authors and fsnotify Authors.
-
-* HOMEPAGE:
- * http://gopkg.in/fsnotify.v1
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-Copyright (c) 2012 fsnotify Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## asn1-ber
-
-This product contains a modified portion of 'asn1-ber', an ASN1 BER Encoding / Decoding Library for the GO programming language by The Go Authors.
-
-* HOMEPAGE:
- * http://gopkg.in/asn1-ber.v1
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'asn1-ber', an ASN1 BER Encoding / Decoding Library for the GO programming language by The Go Authors.
-
-* HOMEPAGE:
- * http://gopkg.in/asn1-ber.v1
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## redigo
-
-This product contains a modified portion of 'redigo', a Go client for the Redis database.
-
-* HOMEPAGE:
- * https://github.com/garyburd/redigo
-
-* LICENSE:
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
----
-
-## crypto
-
-This product contains a modified portion of 'crypto', a set of Go cryptographic libraries.
-
-* HOMEPAGE:
- * https://github.com/golang/crypto
-
-* LICENSE:
-
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
----
-
-## image
-
-This product contains a modified portion of 'image', a set of Go image libraries.
-
-* HOMEPAGE:
- * https://github.com/golang/image
-
-* LICENSE:
-
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
----
-
-## rsc
-
-This product contains a modified portion of 'rsc', a random source code library from Google
-
-* HOMEPAGE:
- * https://code.google.com/archive/p/rsc/
-
-* LICENSE:
-
-Copyright (c) <YEAR>, <OWNER>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## mattermost-driver-javascript
-
-This product contains a modified portion of 'mattermost-driver-javascript', a Javascript library for interacting with the Mattermost API.
-
-* HOMEPAGE:
- * https://github.com/mattermost/mattermost-driver-javascript
-
-* LICENSE:
-
-Copyright 2016-present Mattermost
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-
----
-
-This product contains a modified portion of 'osext', providing a function that returns an absolute path to the current program executable, built by kardianos.
-
-* HOMEPAGE:
- * https://github.com/kardianos/osext
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of 'go-saml', a SAML client library for Go written by RobotsAndPencils.
-
-* HOMEPAGE:
- * https://github.com/RobotsAndPencils/go-saml
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Robots and Pencils
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'minio-go', a Golang Client SDK providing simple APIs to access any Amazon S3 compatible object storage server, built by Minio.
-
-* HOMEPAGE:
- * https://github.com/minio/minio-go
-
-* LICENSE:
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
----
-
-This product contains a modified portion of 'graceful', a Go package enabling graceful shutdown of an http.Handler server, built by Tyler Bunnell.
-
-* HOMEPAGE:
- * https://github.com/tylerb/graceful
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2014 Tyler Bunnell
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-This product contains a modified portion of 'letsencrypt' to manage TLS certificates automatically via LetsEncrypt.org, built by Russ Cox.
-
-* HOMEPAGE:
- * https://github.com/rsc/letsencrypt
-
-* LICENSE:
-
-Copyright (c) 2009 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of ‘manners’ a polite webserver for Go, built by Braintree, a division of PayPal, Inc.
-
-* HOMEPAGE:
- * https://github.com/braintree/manners
-
-* LICENSE:
-
-Copyright (c) 2014 Braintree, a division of PayPal, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
----
-
-This product contains a modified portion of ‘perks’ which contains the Go package quantile that computes approximate quantiles over an unbounded data stream within low memory and CPU bounds, built by Blake Mizerany.
-
-* HOMEPAGE:
- * https://github.com/beorn7/perks
-
-* LICENSE:
-
-Copyright (C) 2013 Blake Mizerany
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-This product contains a modified portion of ‘protobuf’ for Go support for Protocol Buffers, built by The Go Authors.
-
-* HOMEPAGE:
- * https://github.com/golang/protobuf
-
-* LICENSE:
-
-Go support for Protocol Buffers - Google's data interchange format
-
-Copyright 2010 The Go Authors. All rights reserved.
-https://github.com/golang/protobuf
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains a modified portion of ‘golang_protobuf_extensions’ which provides various Protocol Buffer extensions, built by matttproud.
-
-* HOMEPAGE:
- * https://github.com/matttproud/golang_protobuf_extensions
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of ‘cobra’, a Commander for modern Go CLI interactions, built by spf13.
-
-* HOMEPAGE:
- * https://github.com/spf13/cobra
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains a modified portion of ‘pflag’, a Commander for modern Go CLI interactions, built by Alex Ogier.
-
-* HOMEPAGE:
- * https://github.com/spf13/pflag
-
-* LICENSE:
-
-Copyright (c) 2012 Alex Ogier. All rights reserved.
-Copyright (c) 2012 The Go Authors. All rights reserved.
-
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-This product contains modified portions of Prometheus ‘client_golang’, which is the Go library for Prometheus. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/client_golang
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains modified portions of Prometheus ‘client_model’, which provides data model artifacts for Prometheus. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/client_model
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains modified portions of Prometheus ‘common’, which is the part of the Prometheus project that contains Go libraries that are shared across Prometheus components and libraries. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/common
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
----
-
-This product contains modified portions of Prometheus ‘procfs’, which is the part of the Prometheus project that provides functions to retrieve system, kernel and process metrics from the pseudo-filesystem proc. Prometheus is a systems and service monitoring system, started by Matt T. Proud and Julius Volz in 2012.
-
-* HOMEPAGE:
- * https://github.com/prometheus/procfs
-
-* LICENSE:
-
-# Code licensed under the Apache 2.0 License:
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-----
-
-This product contains a modified portion of 'go-opengraph', for parsing OpenGraph data from HTML into regular structures by Vitaly Dyatlov.
-
-* HOMEPAGE:
- * https://github.com/dyatlov/go-opengraph
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Vitaly Dyatlov
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-----
-
-This product contains a modified portion of `viper` for handling configuration in Go programs. Built by Steve Francia.
-
-* HOMEPAGE:
- * https://github.com/spf13/viper
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2014 Steve Francia
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-----
-
-This product contains a modified version of `gomail` for sending emails. Built by Alexandre Cesaro.
-
-* HOMEPAGE:
- * https://github.com/go-gomail/gomail
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2014 Alexandre Cesaro
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-----
-
-This product contains a modified version of `fsnotify` for file system notifications. Built by the Go Authors and the fsnotify Authors.
-
-* HOMEPAGE:
- * https://github.com/fsnotify/fsnotify
-
-* LICENSE:
-
-Copyright (c) 2012 The Go Authors. All rights reserved.
-Copyright (c) 2012 fsnotify Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-----
-
-## analytics-go
-
-This product contains a modified portion of `analytics-go`, a segment analytics client for Go by Segment.
-
-* HOMEPAGE:
- * https://github.com/segmentio/analytics-go
-
-* LICENSE:
-
-MIT license
-
-----
-
-## html2text
-
-This product contains a modified portion of 'html2text', an HTML to raw text converter by Carlos Tadeu Panato Junior, based on `html2text` by Jay Taylor.
-
-* HOMEPAGE
- * https://github.com/mattermost/html2text
-
-* LICENSE
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Jay Taylor
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
-## elastic
-
-This product contains a modified portion of `elastic`, an Elasticsearch client for the Go programming language by Oliver Eilhard.
-
-* HOMEPAGE
- * https://github.com/olivere/elastic
-
-* LICENSE
-
-The MIT License (MIT)
-Copyright © 2012-2015 Oliver Eilhard
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the “Software”), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE.
-
----
-
-## redis
-
-This product contains a modified portion of `redis`, a type-safe Redis client for Golang, by go-redis.
-
-* HOMEPAGE
- * https://github.com/go-redis/redis
-
-* LICENSE
-
-Copyright (c) 2013 The github.com/go-redis/redis Authors.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
----
-
-## testify
-
-This product contains a modified portion of testify, a Golang toolkit providing tools for testifying that code behaves as intended.
-
-* HOMEPAGE
- * https://github.com/stretchr/testify
-
-* LICENSE
-
-Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
-
-Please consider promoting this project if you find it useful.
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of the Software,
-and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
-OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
-OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
----
-
-## html2text
-
-This product contains a modified portion of `html2text`, a Golang library for HTML to plaintext conversion.
-
-* HOMEPAGE
- * https://github.com/mattermost/html2text
-
-* LICENSE
-
-The MIT License (MIT)
-
-Copyright (c) 2015 Jay Taylor
-Modified work: Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/mattermost/platform/model/client.go b/vendor/github.com/mattermost/platform/model/client.go
deleted file mode 100644
index ef890b59..00000000
--- a/vendor/github.com/mattermost/platform/model/client.go
+++ /dev/null
@@ -1,2379 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "mime/multipart"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
-
- l4g "github.com/alecthomas/log4go"
-)
-
-var UsedApiV3 *int32 = new(int32)
-
-const (
- HEADER_REQUEST_ID = "X-Request-ID"
- HEADER_VERSION_ID = "X-Version-ID"
- HEADER_CLUSTER_ID = "X-Cluster-ID"
- HEADER_ETAG_SERVER = "ETag"
- HEADER_ETAG_CLIENT = "If-None-Match"
- HEADER_FORWARDED = "X-Forwarded-For"
- HEADER_REAL_IP = "X-Real-IP"
- HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
- HEADER_TOKEN = "token"
- HEADER_BEARER = "BEARER"
- HEADER_AUTH = "Authorization"
- HEADER_REQUESTED_WITH = "X-Requested-With"
- HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
- STATUS = "status"
- STATUS_OK = "OK"
- STATUS_FAIL = "FAIL"
- STATUS_REMOVE = "REMOVE"
-
- CLIENT_DIR = "client"
-
- API_URL_SUFFIX_V1 = "/api/v1"
- API_URL_SUFFIX_V3 = "/api/v3"
- API_URL_SUFFIX_V4 = "/api/v4"
- API_URL_SUFFIX = API_URL_SUFFIX_V4
-)
-
-type Result struct {
- RequestId string
- Etag string
- Data interface{}
-}
-
-type ResponseMetadata struct {
- StatusCode int
- Error *AppError
- RequestId string
- Etag string
-}
-
-type Client struct {
- Url string // The location of the server like "http://localhost:8065"
- ApiUrl string // The api location of the server like "http://localhost:8065/api/v3"
- HttpClient *http.Client // The http client
- AuthToken string
- AuthType string
- TeamId string
- RequestId string
- Etag string
- ServerVersion string
-}
-
-// NewClient constructs a new client with convienence methods for talking to
-// the server.
-func NewClient(url string) *Client {
- return &Client{url, url + API_URL_SUFFIX_V3, &http.Client{}, "", "", "", "", "", ""}
-}
-
-func closeBody(r *http.Response) {
- if r.Body != nil {
- ioutil.ReadAll(r.Body)
- r.Body.Close()
- }
-}
-
-func (c *Client) SetOAuthToken(token string) {
- c.AuthToken = token
- c.AuthType = HEADER_TOKEN
-}
-
-func (c *Client) ClearOAuthToken() {
- c.AuthToken = ""
- c.AuthType = HEADER_BEARER
-}
-
-func (c *Client) SetTeamId(teamId string) {
- c.TeamId = teamId
-}
-
-func (c *Client) GetTeamId() string {
- if len(c.TeamId) == 0 {
- println(`You are trying to use a route that requires a team_id,
- but you have not called SetTeamId() in client.go`)
- }
-
- return c.TeamId
-}
-
-func (c *Client) ClearTeamId() {
- c.TeamId = ""
-}
-
-func (c *Client) GetTeamRoute() string {
- return fmt.Sprintf("/teams/%v", c.GetTeamId())
-}
-
-func (c *Client) GetChannelRoute(channelId string) string {
- return fmt.Sprintf("/teams/%v/channels/%v", c.GetTeamId(), channelId)
-}
-
-func (c *Client) GetUserRequiredRoute(userId string) string {
- return fmt.Sprintf("/users/%v", userId)
-}
-
-func (c *Client) GetChannelNameRoute(channelName string) string {
- return fmt.Sprintf("/teams/%v/channels/name/%v", c.GetTeamId(), channelName)
-}
-
-func (c *Client) GetEmojiRoute() string {
- return "/emoji"
-}
-
-func (c *Client) GetGeneralRoute() string {
- return "/general"
-}
-
-func (c *Client) GetFileRoute(fileId string) string {
- return fmt.Sprintf("/files/%v", fileId)
-}
-
-func (c *Client) DoPost(url, data, contentType string) (*http.Response, *AppError) {
- rq, _ := http.NewRequest("POST", c.Url+url, strings.NewReader(data))
- rq.Header.Set("Content-Type", contentType)
- rq.Close = true
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- defer closeBody(rp)
- return nil, AppErrorFromJson(rp.Body)
- } else {
- return rp, nil
- }
-}
-
-func (c *Client) DoApiPost(url string, data string) (*http.Response, *AppError) {
- rq, _ := http.NewRequest("POST", c.ApiUrl+url, strings.NewReader(data))
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- defer closeBody(rp)
- return nil, AppErrorFromJson(rp.Body)
- } else {
- return rp, nil
- }
-}
-
-func (c *Client) DoApiGet(url string, data string, etag string) (*http.Response, *AppError) {
- rq, _ := http.NewRequest("GET", c.ApiUrl+url, strings.NewReader(data))
- rq.Close = true
-
- if len(etag) > 0 {
- rq.Header.Set(HEADER_ETAG_CLIENT, etag)
- }
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode == 304 {
- return rp, nil
- } else if rp.StatusCode >= 300 {
- defer closeBody(rp)
- return rp, AppErrorFromJson(rp.Body)
- } else {
- return rp, nil
- }
-}
-
-func getCookie(name string, resp *http.Response) *http.Cookie {
- for _, cookie := range resp.Cookies() {
- if cookie.Name == name {
- return cookie
- }
- }
-
- return nil
-}
-
-// Must is a convenience function used for testing.
-func (c *Client) Must(result *Result, err *AppError) *Result {
- if err != nil {
- l4g.Close()
- time.Sleep(time.Second)
- panic(err)
- }
-
- return result
-}
-
-// MustGeneric is a convenience function used for testing.
-func (c *Client) MustGeneric(result interface{}, err *AppError) interface{} {
- if err != nil {
- l4g.Close()
- time.Sleep(time.Second)
- panic(err)
- }
-
- return result
-}
-
-// CheckStatusOK is a convenience function for checking the return of Web Service
-// call that return the a map of status=OK.
-func (c *Client) CheckStatusOK(r *http.Response) bool {
- m := MapFromJson(r.Body)
- defer closeBody(r)
-
- if m != nil && m[STATUS] == STATUS_OK {
- return true
- }
-
- return false
-}
-
-func (c *Client) fillInExtraProperties(r *http.Response) {
- c.RequestId = r.Header.Get(HEADER_REQUEST_ID)
- c.Etag = r.Header.Get(HEADER_ETAG_SERVER)
- c.ServerVersion = r.Header.Get(HEADER_VERSION_ID)
-}
-
-func (c *Client) clearExtraProperties() {
- c.RequestId = ""
- c.Etag = ""
- c.ServerVersion = ""
-}
-
-// General Routes Section
-
-// GetClientProperties returns properties needed by the client to show/hide
-// certian features. It returns a map of strings.
-func (c *Client) GetClientProperties() (map[string]string, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet(c.GetGeneralRoute()+"/client_props", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return MapFromJson(r.Body), nil
- }
-}
-
-// LogClient is a convenience Web Service call so clients can log messages into
-// the server-side logs. For example we typically log javascript error messages
-// into the server-side. It returns true if the logging was successful.
-func (c *Client) LogClient(message string) (bool, *AppError) {
- c.clearExtraProperties()
- m := make(map[string]string)
- m["level"] = "ERROR"
- m["message"] = message
-
- if r, err := c.DoApiPost(c.GetGeneralRoute()+"/log_client", MapToJson(m)); err != nil {
- return false, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-// GetPing returns a map of strings with server time, server version, and node Id.
-// Systems that want to check on health status of the server should check the
-// url /api/v3/ping for a 200 status response.
-func (c *Client) GetPing() (map[string]string, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet(c.GetGeneralRoute()+"/ping", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return MapFromJson(r.Body), nil
- }
-}
-
-// Team Routes Section
-
-// CreateTeam creates a team based on the provided Team struct. On success it returns
-// the Team struct with the Id, CreateAt and other server-decided fields populated.
-func (c *Client) CreateTeam(team *Team) (*Result, *AppError) {
- if r, err := c.DoApiPost("/teams/create", team.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// GetAllTeams returns a map of all teams using team ids as the key.
-func (c *Client) GetAllTeams() (*Result, *AppError) {
- if r, err := c.DoApiGet("/teams/all", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMapFromJson(r.Body)}, nil
- }
-}
-
-// GetAllTeamListings returns a map of all teams that are available to join
-// using team ids as the key. Must be authenticated.
-func (c *Client) GetAllTeamListings() (*Result, *AppError) {
- if r, err := c.DoApiGet("/teams/all_team_listings", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMapFromJson(r.Body)}, nil
- }
-}
-
-// FindTeamByName returns the strings "true" or "false" depending on if a team
-// with the provided name was found.
-func (c *Client) FindTeamByName(name string) (*Result, *AppError) {
- m := make(map[string]string)
- m["name"] = name
- if r, err := c.DoApiPost("/teams/find_team_by_name", MapToJson(m)); err != nil {
- return nil, err
- } else {
- val := false
- if body, _ := ioutil.ReadAll(r.Body); string(body) == "true" {
- val = true
- }
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), val}, nil
- }
-}
-
-// Adds a user directly to the team without sending an invite.
-// The teamId and userId are required. You must be a valid member of the team and/or
-// have the correct role to add new users to the team. Returns a map of user_id=userId
-// if successful, otherwise returns an AppError.
-func (c *Client) AddUserToTeam(teamId string, userId string) (*Result, *AppError) {
- if len(teamId) == 0 {
- teamId = c.GetTeamId()
- }
-
- data := make(map[string]string)
- data["user_id"] = userId
- if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v", teamId)+"/add_user_to_team", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// AddUserToTeamFromInvite adds a user to a team based off data provided in an invite link.
-// Either hash and dataToHash are required or inviteId is required.
-func (c *Client) AddUserToTeamFromInvite(hash, dataToHash, inviteId string) (*Result, *AppError) {
- data := make(map[string]string)
- data["hash"] = hash
- data["data"] = dataToHash
- data["invite_id"] = inviteId
- if r, err := c.DoApiPost("/teams/add_user_to_team_from_invite", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// Removes a user directly from the team.
-// The teamId and userId are required. You must be a valid member of the team and/or
-// have the correct role to remove a user from the team. Returns a map of user_id=userId
-// if successful, otherwise returns an AppError.
-func (c *Client) RemoveUserFromTeam(teamId string, userId string) (*Result, *AppError) {
- if len(teamId) == 0 {
- teamId = c.GetTeamId()
- }
-
- data := make(map[string]string)
- data["user_id"] = userId
- if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v", teamId)+"/remove_user_from_team", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/invite_members", invites.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), InvitesFromJson(r.Body)}, nil
- }
-}
-
-// UpdateTeam updates a team based on the changes in the provided team struct. On success
-// it returns a sanitized version of the updated team. Must be authenticated as a team admin
-// for that team or a system admin.
-func (c *Client) UpdateTeam(team *Team) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/update", team.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// User Routes Section
-
-// CreateUser creates a user in the system based on the provided user struct.
-func (c *Client) CreateUser(user *User, hash string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/create", user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// CreateUserWithInvite creates a user based on the provided user struct. Either the hash and
-// data strings or the inviteId is required from the invite.
-func (c *Client) CreateUserWithInvite(user *User, hash string, data string, inviteId string) (*Result, *AppError) {
-
- url := "/users/create?d=" + url.QueryEscape(data) + "&h=" + url.QueryEscape(hash) + "&iid=" + url.QueryEscape(inviteId)
-
- if r, err := c.DoApiPost(url, user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateUserFromSignup(user *User, data string, hash string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/create?d="+url.QueryEscape(data)+"&h="+hash, user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// GetUser returns a user based on a provided user id string. Must be authenticated.
-func (c *Client) GetUser(id string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/"+id+"/get", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// getByUsername returns a user based on a provided username string. Must be authenticated.
-func (c *Client) GetByUsername(username string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/users/name/%v", username), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// getByEmail returns a user based on a provided username string. Must be authenticated.
-func (c *Client) GetByEmail(email string, etag string) (*User, *ResponseMetadata) {
- if r, err := c.DoApiGet(fmt.Sprintf("/users/email/%v", email), "", etag); err != nil {
- return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- defer closeBody(r)
- return UserFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-// GetMe returns the current user.
-func (c *Client) GetMe(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/me", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// GetProfiles returns a map of users using user id as the key. Must be authenticated.
-func (c *Client) GetProfiles(offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/users/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesInTeam returns a map of users for a team using user id as the key. Must
-// be authenticated.
-func (c *Client) GetProfilesInTeam(teamId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/users/%v/%v", teamId, offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesInChannel returns a map of users for a channel using user id as the key. Must
-// be authenticated.
-func (c *Client) GetProfilesInChannel(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/users/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesNotInChannel returns a map of users not in a channel but on the team using user id as the key. Must
-// be authenticated.
-func (c *Client) GetProfilesNotInChannel(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/users/not_in_channel/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// GetProfilesByIds returns a map of users based on the user ids provided. Must
-// be authenticated.
-func (c *Client) GetProfilesByIds(userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/ids", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-// SearchUsers returns a list of users that have a username matching or similar to the search term. Must
-// be authenticated.
-func (c *Client) SearchUsers(params UserSearch) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/search", params.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserListFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteUsersInChannel returns two lists for autocompletion of users in a channel. The first list "in_channel",
-// specifies users in the channel. The second list "out_of_channel" specifies users outside of the
-// channel. Term, the string to search against, is required, channel id is also required. Must be authenticated.
-func (c *Client) AutocompleteUsersInChannel(term string, channelId string) (*Result, *AppError) {
- url := fmt.Sprintf("%s/users/autocomplete?term=%s", c.GetChannelRoute(channelId), url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserAutocompleteInChannelFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteUsersInTeam returns a list for autocompletion of users in a team. The list "in_team" specifies
-// the users in the team that match the provided term, matching against username, full name and
-// nickname. Must be authenticated.
-func (c *Client) AutocompleteUsersInTeam(term string) (*Result, *AppError) {
- url := fmt.Sprintf("%s/users/autocomplete?term=%s", c.GetTeamRoute(), url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserAutocompleteInTeamFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteUsers returns a list for autocompletion of users on the system that match the provided term,
-// matching against username, full name and nickname. Must be authenticated.
-func (c *Client) AutocompleteUsers(term string) (*Result, *AppError) {
- url := fmt.Sprintf("/users/autocomplete?term=%s", url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserListFromJson(r.Body)}, nil
- }
-}
-
-// LoginById authenticates a user by user id and password.
-func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
- m := make(map[string]string)
- m["id"] = id
- m["password"] = password
- return c.login(m)
-}
-
-// Login authenticates a user by login id, which can be username, email or some sort
-// of SSO identifier based on configuration, and a password.
-func (c *Client) Login(loginId string, password string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
- m["password"] = password
- return c.login(m)
-}
-
-// LoginByLdap authenticates a user by LDAP id and password.
-func (c *Client) LoginByLdap(loginId string, password string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
- m["password"] = password
- m["ldap_only"] = "true"
- return c.login(m)
-}
-
-// LoginWithDevice authenticates a user by login id (username, email or some sort
-// of SSO identifier based on configuration), password and attaches a device id to
-// the session.
-func (c *Client) LoginWithDevice(loginId string, password string, deviceId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
- m["password"] = password
- m["device_id"] = deviceId
- return c.login(m)
-}
-
-func (c *Client) login(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/login", MapToJson(m)); err != nil {
- return nil, err
- } else {
- c.AuthToken = r.Header.Get(HEADER_TOKEN)
- c.AuthType = HEADER_BEARER
- sessionToken := getCookie(SESSION_COOKIE_TOKEN, r)
-
- if c.AuthToken != sessionToken.Value {
- NewAppError("/users/login", "model.client.login.app_error", nil, "", 0)
- }
-
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-// Logout terminates the current user's session.
-func (c *Client) Logout() (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/logout", ""); err != nil {
- return nil, err
- } else {
- c.AuthToken = ""
- c.AuthType = HEADER_BEARER
- c.TeamId = ""
-
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// CheckMfa returns a map with key "mfa_required" with the string value "true" or "false",
-// indicating whether MFA is required to log the user in, based on a provided login id
-// (username, email or some sort of SSO identifier based on configuration).
-func (c *Client) CheckMfa(loginId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["login_id"] = loginId
-
- if r, err := c.DoApiPost("/users/mfa", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GenerateMfaSecret returns a QR code image containing the secret, to be scanned
-// by a multi-factor authentication mobile application. It also returns the secret
-// for manual entry. Must be authenticated.
-func (c *Client) GenerateMfaSecret() (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/generate_mfa_secret", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// UpdateMfa activates multi-factor authenticates for the current user if activate
-// is true and a valid token is provided. If activate is false, then token is not
-// required and multi-factor authentication is disabled for the current user.
-func (c *Client) UpdateMfa(activate bool, token string) (*Result, *AppError) {
- m := make(map[string]interface{})
- m["activate"] = activate
- m["token"] = token
-
- if r, err := c.DoApiPost("/users/update_mfa", StringInterfaceToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) AdminResetMfa(userId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["user_id"] = userId
-
- if r, err := c.DoApiPost("/admin/reset_mfa", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) RevokeSession(sessionAltId string) (*Result, *AppError) {
- m := make(map[string]string)
- m["id"] = sessionAltId
-
- if r, err := c.DoApiPost("/users/revoke_session", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetSessions(id string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/"+id+"/sessions", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), SessionsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) EmailToOAuth(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/email_to_oauth", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) OAuthToEmail(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/oauth_to_email", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) LDAPToEmail(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/ldap_to_email", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) EmailToLDAP(m map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/claim/ldap_to_email", MapToJson(m)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) Command(channelId string, command string) (*Result, *AppError) {
- args := &CommandArgs{ChannelId: channelId, Command: command}
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/execute", args.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandResponseFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListCommands() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/commands/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListTeamCommands() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/commands/list_team_commands", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateCommand(cmd *Command) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/create", cmd.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateCommand(cmd *Command) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/update", cmd.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) RegenCommandToken(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/regen_token", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) DeleteCommand(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/commands/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/"+id+"/audits", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AuditsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetLogs() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/logs", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ArrayFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetClusterStatus() ([]*ClusterInfo, *AppError) {
- if r, err := c.DoApiGet("/admin/cluster_status", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return ClusterInfosFromJson(r.Body), nil
- }
-}
-
-// GetRecentlyActiveUsers returns a map of users including lastActivityAt using user id as the key
-func (c *Client) GetRecentlyActiveUsers(teamId string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/recently_active_users/"+teamId, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAllAudits() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AuditsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetConfig() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/config", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ConfigFromJson(r.Body)}, nil
- }
-}
-
-// ReloadConfig will reload the config.json file from disk. Properties
-// requiring a server restart will still need a server restart. You must
-// have the system admin role to call this method. It will return status=OK
-// if it's successfully reloaded the config file, otherwise check the returned error.
-func (c *Client) ReloadConfig() (bool, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet("/admin/reload_config", "", ""); err != nil {
- return false, err
- } else {
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) InvalidateAllCaches() (bool, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet("/admin/invalidate_all_caches", "", ""); err != nil {
- return false, err
- } else {
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) SaveConfig(config *Config) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/save_config", config.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// RecycleDatabaseConnection will attempt to recycle the database connections.
-// You must have the system admin role to call this method. It will return status=OK
-// if it's successfully recycled the connections, otherwise check the returned error.
-func (c *Client) RecycleDatabaseConnection() (bool, *AppError) {
- c.clearExtraProperties()
- if r, err := c.DoApiGet("/admin/recycle_db_conn", "", ""); err != nil {
- return false, err
- } else {
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) TestEmail(config *Config) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/test_email", config.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// TestLdap will run a connection test on the current LDAP settings.
-// It will return the standard OK response if settings work. Otherwise
-// it will return an appropriate error.
-func (c *Client) TestLdap(config *Config) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/ldap_test", config.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetComplianceReports() (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/compliance_reports", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), CompliancesFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) SaveComplianceReport(job *Compliance) (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/save_compliance_report", job.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ComplianceFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) DownloadComplianceReport(id string) (*Result, *AppError) {
- var rq *http.Request
- rq, _ = http.NewRequest("GET", c.ApiUrl+"/admin/download_compliance_report/"+id, nil)
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError("/admin/download_compliance_report", "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- defer rp.Body.Close()
- return nil, AppErrorFromJson(rp.Body)
- } else {
- defer closeBody(rp)
- return &Result{rp.Header.Get(HEADER_REQUEST_ID),
- rp.Header.Get(HEADER_ETAG_SERVER), rp.Body}, nil
- }
-}
-
-func (c *Client) GetTeamAnalytics(teamId, name string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/analytics/"+teamId+"/"+name, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AnalyticsRowsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetSystemAnalytics(name string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/admin/analytics/"+name, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AnalyticsRowsFromJson(r.Body)}, nil
- }
-}
-
-// Initiate immediate synchronization of LDAP users.
-// The synchronization will be performed asynchronously and this function will
-// always return OK unless you don't have permissions.
-// You must be the system administrator to use this function.
-func (c *Client) LdapSyncNow() (*Result, *AppError) {
- if r, err := c.DoApiPost("/admin/ldap_sync_now", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create", channel.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateDirectChannel(userId string) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = userId
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_direct", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateGroupChannel(userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/create_group", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateChannel(channel *Channel) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update", channel.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateChannelHeader(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update_header", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateChannelPurpose(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update_purpose", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateNotifyProps(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/update_notify_props", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetMyChannelMembers() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/members", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelMembersFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannel(id, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelDataFromJson(r.Body)}, nil
- }
-}
-
-// GetMoreChannelsPage will return a page of open channels the user is not in based on
-// the provided offset and limit. Must be authenticated.
-func (c *Client) GetMoreChannelsPage(offset int, limit int) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetTeamRoute()+"/channels/more/%v/%v", offset, limit), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-// SearchMoreChannels will return a list of open channels the user is not in, that matches
-// the search criteria provided. Must be authenticated.
-func (c *Client) SearchMoreChannels(channelSearch ChannelSearch) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/more/search", channelSearch.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-// AutocompleteChannels will return a list of open channels that match the provided
-// string. Must be authenticated.
-func (c *Client) AutocompleteChannels(term string) (*Result, *AppError) {
- url := fmt.Sprintf("%s/channels/autocomplete?term=%s", c.GetTeamRoute(), url.QueryEscape(term))
- if r, err := c.DoApiGet(url, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/counts", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelCountsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannels(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannelByName(channelName string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelNameRoute(channelName), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) JoinChannel(id string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) JoinChannelByName(name string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelNameRoute(name)+"/join", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) LeaveChannel(id string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/leave", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) DeleteChannel(id string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/delete", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) AddChannelMember(id, user_id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = user_id
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/add", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) RemoveChannelMember(id, user_id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = user_id
- if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/remove", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-// ViewChannel performs all the actions related to viewing a channel. This includes marking
-// the channel and the previous one as read, and marking the channel as being actively viewed.
-// ChannelId is required but may be blank to indicate no channel is being viewed.
-// PrevChannelId is optional, populate to indicate a channel switch occurred.
-func (c *Client) ViewChannel(params ChannelView) (bool, *ResponseMetadata) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/channels/view", params.ToJson()); err != nil {
- return false, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- return c.CheckStatusOK(r),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-func (c *Client) GetChannelStats(id string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/stats", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelStatsFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetChannelMember(channelId string, userId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/members/"+userId, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelMemberFromJson(r.Body)}, nil
- }
-}
-
-// GetChannelMembersByIds will return channel member objects as an array based on the
-// channel id and a list of user ids provided. Must be authenticated.
-func (c *Client) GetChannelMembersByIds(channelId string, userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/members/ids", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), ChannelMembersFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreatePost(post *Post) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(post.ChannelId)+"/posts/create", post.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdatePost(post *Post) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(post.ChannelId)+"/posts/update", post.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPosts(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/page/%v/%v", offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPostsSince(channelId string, time int64) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/since/%v", time), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPostsBefore(channelId string, postid string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/before/%v/%v", postid, offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPostsAfter(channelId string, postid string, offset int, limit int, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/posts/%v/after/%v/%v", postid, offset, limit), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPost(channelId string, postId string, etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/get", postId), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-// GetPostById returns a post and any posts in the same thread by post id
-func (c *Client) GetPostById(postId string, etag string) (*PostList, *ResponseMetadata) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/posts/%v", postId), "", etag); err != nil {
- return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- defer closeBody(r)
- return PostListFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-// GetPermalink returns a post list, based on the provided channel and post ID.
-func (c *Client) GetPermalink(channelId string, postId string, etag string) (*PostList, *ResponseMetadata) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/pltmp/%v", postId), "", etag); err != nil {
- return nil, &ResponseMetadata{StatusCode: r.StatusCode, Error: err}
- } else {
- defer closeBody(r)
- return PostListFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/delete", postId), ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) SearchPosts(terms string, isOrSearch bool) (*Result, *AppError) {
- data := map[string]interface{}{}
- data["terms"] = terms
- data["is_or_search"] = isOrSearch
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/posts/search", StringInterfaceToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-// GetFlaggedPosts will return a post list of posts that have been flagged by the user.
-// The page is set by the integer parameters offset and limit.
-func (c *Client) GetFlaggedPosts(offset int, limit int) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+fmt.Sprintf("/posts/flagged/%v/%v", offset, limit), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPinnedPosts(channelId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/pinned", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UploadProfileFile(data []byte, contentType string) (*Result, *AppError) {
- return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType)
-}
-
-func (c *Client) UploadPostAttachment(data []byte, channelId string, filename string) (*FileUploadResponse, *AppError) {
- c.clearExtraProperties()
-
- body := &bytes.Buffer{}
- writer := multipart.NewWriter(body)
-
- if part, err := writer.CreateFormFile("files", filename); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), 0)
- } else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error(), 0)
- }
-
- if part, err := writer.CreateFormField("channel_id"); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), 0)
- } else if _, err = io.Copy(part, strings.NewReader(channelId)); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error(), 0)
- }
-
- if err := writer.Close(); err != nil {
- return nil, NewAppError("UploadPostAttachment", "model.client.upload_post_attachment.writer.app_error", nil, err.Error(), 0)
- }
-
- if result, err := c.uploadFile(c.ApiUrl+c.GetTeamRoute()+"/files/upload", body.Bytes(), writer.FormDataContentType()); err != nil {
- return nil, err
- } else {
- return result.Data.(*FileUploadResponse), nil
- }
-}
-
-func (c *Client) uploadFile(url string, data []byte, contentType string) (*Result, *AppError) {
- rq, _ := http.NewRequest("POST", url, bytes.NewReader(data))
- rq.Header.Set("Content-Type", contentType)
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- return nil, AppErrorFromJson(rp.Body)
- } else {
- defer closeBody(rp)
- return &Result{rp.Header.Get(HEADER_REQUEST_ID),
- rp.Header.Get(HEADER_ETAG_SERVER), FileUploadResponseFromJson(rp.Body)}, nil
- }
-}
-
-func (c *Client) GetFile(fileId string) (io.ReadCloser, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get", "", ""); err != nil {
- return nil, err
- } else {
- c.fillInExtraProperties(r)
- return r.Body, nil
- }
-}
-
-func (c *Client) GetFileThumbnail(fileId string) (io.ReadCloser, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_thumbnail", "", ""); err != nil {
- return nil, err
- } else {
- c.fillInExtraProperties(r)
- return r.Body, nil
- }
-}
-
-func (c *Client) GetFilePreview(fileId string) (io.ReadCloser, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_preview", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return r.Body, nil
- }
-}
-
-func (c *Client) GetFileInfo(fileId string) (*FileInfo, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_info", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return FileInfoFromJson(r.Body), nil
- }
-}
-
-func (c *Client) GetPublicLink(fileId string) (string, *AppError) {
- if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_public_link", "", ""); err != nil {
- return "", err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return StringFromJson(r.Body), nil
- }
-}
-
-func (c *Client) UpdateUser(user *User) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/update", user.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateUserRoles(userId string, roles string) (*Result, *AppError) {
- data := make(map[string]string)
- data["new_roles"] = roles
-
- if r, err := c.DoApiPost(c.GetUserRequiredRoute(userId)+"/update_roles", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateTeamRoles(userId string, roles string) (*Result, *AppError) {
- data := make(map[string]string)
- data["new_roles"] = roles
- data["user_id"] = userId
-
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/update_member_roles", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) AttachDeviceId(deviceId string) (*Result, *AppError) {
- data := make(map[string]string)
- data["device_id"] = deviceId
- if r, err := c.DoApiPost("/users/attach_device", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateActive(userId string, active bool) (*Result, *AppError) {
- data := make(map[string]string)
- data["user_id"] = userId
- data["active"] = strconv.FormatBool(active)
- if r, err := c.DoApiPost("/users/update_active", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateUserNotify(data map[string]string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/update_notify", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateUserPassword(userId, currentPassword, newPassword string) (*Result, *AppError) {
- data := make(map[string]string)
- data["current_password"] = currentPassword
- data["new_password"] = newPassword
- data["user_id"] = userId
-
- if r, err := c.DoApiPost("/users/newpassword", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) SendPasswordReset(email string) (*Result, *AppError) {
- data := map[string]string{}
- data["email"] = email
- if r, err := c.DoApiPost("/users/send_password_reset", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ResetPassword(code, newPassword string) (*Result, *AppError) {
- data := map[string]string{}
- data["code"] = code
- data["new_password"] = newPassword
- if r, err := c.DoApiPost("/users/reset_password", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) AdminResetPassword(userId, newPassword string) (*Result, *AppError) {
- data := map[string]string{}
- data["user_id"] = userId
- data["new_password"] = newPassword
- if r, err := c.DoApiPost("/admin/reset_password", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetStatuses returns a map of string statuses using user id as the key
-func (c *Client) GetStatuses() (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/status", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetStatusesByIds returns a map of string statuses using user id as the key,
-// based on the provided user ids
-func (c *Client) GetStatusesByIds(userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/users/status/ids", ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/me", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamMembers will return a page of team member objects as an array paged based on the
-// team id, offset and limit provided. Must be authenticated.
-func (c *Client) GetTeamMembers(teamId string, offset int, limit int) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/members/%v/%v", teamId, offset, limit), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
- }
-}
-
-// GetMyTeamMembers will return an array with team member objects that the current user
-// is a member of. Must be authenticated.
-func (c *Client) GetMyTeamMembers() (*Result, *AppError) {
- if r, err := c.DoApiGet("/teams/members", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
- }
-}
-
-// GetMyTeamsUnread will return an array with TeamUnread objects that contain the amount of
-// unread messages and mentions the current user has for the teams it belongs to.
-// An optional team ID can be set to exclude that team from the results. Must be authenticated.
-func (c *Client) GetMyTeamsUnread(teamId string) (*Result, *AppError) {
- endpoint := "/teams/unread"
-
- if teamId != "" {
- endpoint += fmt.Sprintf("?id=%s", url.QueryEscape(teamId))
- }
- if r, err := c.DoApiGet(endpoint, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamsUnreadFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamMember will return a team member object based on the team id and user id provided.
-// Must be authenticated.
-func (c *Client) GetTeamMember(teamId string, userId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/members/%v", teamId, userId), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMemberFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamStats will return a team stats object containing the number of users on the team
-// based on the team id provided. Must be authenticated.
-func (c *Client) GetTeamStats(teamId string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/stats", teamId), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamStatsFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamByName will return a team object based on the team name provided. Must be authenticated.
-func (c *Client) GetTeamByName(teamName string) (*Result, *AppError) {
- if r, err := c.DoApiGet(fmt.Sprintf("/teams/name/%v", teamName), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil
- }
-}
-
-// GetTeamMembersByIds will return team member objects as an array based on the
-// team id and a list of user ids provided. Must be authenticated.
-func (c *Client) GetTeamMembersByIds(teamId string, userIds []string) (*Result, *AppError) {
- if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v/members/ids", teamId), ArrayToJson(userIds)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
- }
-}
-
-// RegisterApp creates a new OAuth2 app to be used with the OAuth2 Provider. On success
-// it returns the created app. Must be authenticated as a user.
-func (c *Client) RegisterApp(app *OAuthApp) (*Result, *AppError) {
- if r, err := c.DoApiPost("/oauth/register", app.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
- }
-}
-
-// AllowOAuth allows a new session by an OAuth2 App. On success
-// it returns the url to be redirected back to the app which initiated the oauth2 flow.
-// Must be authenticated as a user.
-func (c *Client) AllowOAuth(rspType, clientId, redirect, scope, state string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/allow?response_type="+rspType+"&client_id="+clientId+"&redirect_uri="+url.QueryEscape(redirect)+"&scope="+scope+"&state="+url.QueryEscape(state), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetOAuthAppsByUser returns the OAuth2 Apps registered by the user. On success
-// it returns a list of OAuth2 Apps from the same user or all the registered apps if the user
-// is a System Administrator. Must be authenticated as a user.
-func (c *Client) GetOAuthAppsByUser() (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppListFromJson(r.Body)}, nil
- }
-}
-
-// GetOAuthAppInfo lookup an OAuth2 App using the client_id. On success
-// it returns a Sanitized OAuth2 App. Must be authenticated as a user.
-func (c *Client) GetOAuthAppInfo(clientId string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/app/"+clientId, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
- }
-}
-
-// DeleteOAuthApp deletes an OAuth2 app, the app must be deleted by the same user who created it or
-// a System Administrator. On success returs Status OK. Must be authenticated as a user.
-func (c *Client) DeleteOAuthApp(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost("/oauth/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-// GetOAuthAuthorizedApps returns the OAuth2 Apps authorized by the user. On success
-// it returns a list of sanitized OAuth2 Authorized Apps by the user.
-func (c *Client) GetOAuthAuthorizedApps() (*Result, *AppError) {
- if r, err := c.DoApiGet("/oauth/authorized", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppListFromJson(r.Body)}, nil
- }
-}
-
-// OAuthDeauthorizeApp deauthorize a user an OAuth 2.0 app. On success
-// it returns status OK or an AppError on fail.
-func (c *Client) OAuthDeauthorizeApp(clientId string) *AppError {
- if r, err := c.DoApiPost("/oauth/"+clientId+"/deauthorize", ""); err != nil {
- return err
- } else {
- defer closeBody(r)
- return nil
- }
-}
-
-// RegenerateOAuthAppSecret generates a new OAuth App Client Secret. On success
-// it returns an OAuth2 App. Must be authenticated as a user and the same user who
-// registered the app or a System Admin.
-func (c *Client) RegenerateOAuthAppSecret(clientId string) (*Result, *AppError) {
- if r, err := c.DoApiPost("/oauth/"+clientId+"/regen_secret", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAccessToken(data url.Values) (*Result, *AppError) {
- if r, err := c.DoPost("/oauth/access_token", data.Encode(), "application/x-www-form-urlencoded"); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), AccessResponseFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) CreateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/create", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateIncomingWebhook(hook *IncomingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/update", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) PostToWebhook(id, payload string) (*Result, *AppError) {
- if r, err := c.DoPost("/hooks/"+id, payload, "application/x-www-form-urlencoded"); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), nil}, nil
- }
-}
-
-func (c *Client) DeleteIncomingWebhook(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/incoming/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListIncomingWebhooks() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/hooks/incoming/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), IncomingWebhookListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetAllPreferences() (*Result, *AppError) {
- if r, err := c.DoApiGet("/preferences/", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- preferences, _ := PreferencesFromJson(r.Body)
- return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
- }
-}
-
-func (c *Client) SetPreferences(preferences *Preferences) (*Result, *AppError) {
- if r, err := c.DoApiPost("/preferences/save", preferences.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
- }
-}
-
-func (c *Client) GetPreference(category string, name string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/preferences/"+category+"/"+name, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), PreferenceFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetPreferenceCategory(category string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/preferences/"+category, "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- preferences, _ := PreferencesFromJson(r.Body)
- return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil
- }
-}
-
-// DeletePreferences deletes a list of preferences owned by the current user. If successful,
-// it will return status=ok. Otherwise, an error will be returned.
-func (c *Client) DeletePreferences(preferences *Preferences) (bool, *AppError) {
- if r, err := c.DoApiPost("/preferences/delete", preferences.ToJson()); err != nil {
- return false, err
- } else {
- return c.CheckStatusOK(r), nil
- }
-}
-
-func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/create", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UpdateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/update", hook.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) DeleteOutgoingWebhook(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/delete", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) ListOutgoingWebhooks() (*Result, *AppError) {
- if r, err := c.DoApiGet(c.GetTeamRoute()+"/hooks/outgoing/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookListFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) RegenOutgoingWebhookToken(id string) (*Result, *AppError) {
- data := make(map[string]string)
- data["id"] = id
- if r, err := c.DoApiPost(c.GetTeamRoute()+"/hooks/outgoing/regen_token", MapToJson(data)); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) MockSession(sessionToken string) {
- c.AuthToken = sessionToken
- c.AuthType = HEADER_BEARER
-}
-
-func (c *Client) GetClientLicenceConfig(etag string) (*Result, *AppError) {
- if r, err := c.DoApiGet("/license/client_config", "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) GetInitialLoad() (*Result, *AppError) {
- if r, err := c.DoApiGet("/users/initial_load", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), InitialLoadFromJson(r.Body)}, nil
- }
-}
-
-// ListEmoji returns a list of all user-created emoji for the server.
-func (c *Client) ListEmoji() ([]*Emoji, *AppError) {
- if r, err := c.DoApiGet(c.GetEmojiRoute()+"/list", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return EmojiListFromJson(r.Body), nil
- }
-}
-
-// CreateEmoji will save an emoji to the server if the current user has permission
-// to do so. If successful, the provided emoji will be returned with its Id field
-// filled in. Otherwise, an error will be returned.
-func (c *Client) CreateEmoji(emoji *Emoji, image []byte, filename string) (*Emoji, *AppError) {
- c.clearExtraProperties()
-
- body := &bytes.Buffer{}
- writer := multipart.NewWriter(body)
-
- if part, err := writer.CreateFormFile("image", filename); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)
- } else if _, err = io.Copy(part, bytes.NewBuffer(image)); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.image.app_error", nil, err.Error(), 0)
- }
-
- if err := writer.WriteField("emoji", emoji.ToJson()); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.emoji.app_error", nil, err.Error(), 0)
- }
-
- if err := writer.Close(); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.create_emoji.writer.app_error", nil, err.Error(), 0)
- }
-
- rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetEmojiRoute()+"/create", body)
- rq.Header.Set("Content-Type", writer.FormDataContentType())
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if r, err := c.HttpClient.Do(rq); err != nil {
- return nil, NewAppError("CreateEmoji", "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if r.StatusCode >= 300 {
- return nil, AppErrorFromJson(r.Body)
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return EmojiFromJson(r.Body), nil
- }
-}
-
-// DeleteEmoji will delete an emoji from the server if the current user has permission
-// to do so. If successful, it will return status=ok. Otherwise, an error will be returned.
-func (c *Client) DeleteEmoji(id string) (bool, *AppError) {
- data := map[string]string{"id": id}
-
- if r, err := c.DoApiPost(c.GetEmojiRoute()+"/delete", MapToJson(data)); err != nil {
- return false, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return c.CheckStatusOK(r), nil
- }
-}
-
-// GetCustomEmojiImageUrl returns the API route that can be used to get the image used by
-// the given emoji.
-func (c *Client) GetCustomEmojiImageUrl(id string) string {
- return c.GetEmojiRoute() + "/" + id
-}
-
-// Uploads a x509 base64 Certificate or Private Key file to be used with SAML.
-// data byte array is required and needs to be a Multi-Part with 'certificate' as the field name
-// contentType is also required. Returns nil if succesful, otherwise returns an AppError
-func (c *Client) UploadCertificateFile(data []byte, contentType string) *AppError {
- url := c.ApiUrl + "/admin/add_certificate"
- rq, _ := http.NewRequest("POST", url, bytes.NewReader(data))
- rq.Header.Set("Content-Type", contentType)
- rq.Close = true
-
- if len(c.AuthToken) > 0 {
- rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
- }
-
- if rp, err := c.HttpClient.Do(rq); err != nil {
- return NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), 0)
- } else if rp.StatusCode >= 300 {
- return AppErrorFromJson(rp.Body)
- } else {
- defer closeBody(rp)
- c.fillInExtraProperties(rp)
- return nil
- }
-}
-
-// Removes a x509 base64 Certificate or Private Key file used with SAML.
-// filename is required. Returns nil if successful, otherwise returns an AppError
-func (c *Client) RemoveCertificateFile(filename string) *AppError {
- if r, err := c.DoApiPost("/admin/remove_certificate", MapToJson(map[string]string{"filename": filename})); err != nil {
- return err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return nil
- }
-}
-
-// Checks if the x509 base64 Certificates and Private Key files used with SAML exists on the file system.
-// Returns a map[string]interface{} if successful, otherwise returns an AppError. Must be System Admin authenticated.
-func (c *Client) SamlCertificateStatus(filename string) (map[string]interface{}, *AppError) {
- if r, err := c.DoApiGet("/admin/remove_certificate", "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return StringInterfaceFromJson(r.Body), nil
- }
-}
-
-// GetWebrtcToken if Successful returns a map with a valid token, stun server and turn server with credentials to use with
-// the Mattermost WebRTC service, otherwise returns an AppError. Must be authenticated user.
-func (c *Client) GetWebrtcToken() (map[string]string, *AppError) {
- if r, err := c.DoApiPost("/webrtc/token", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return MapFromJson(r.Body), nil
- }
-}
-
-// GetFileInfosForPost returns a list of FileInfo objects for a given post id, if successful.
-// Otherwise, it returns an error.
-func (c *Client) GetFileInfosForPost(channelId string, postId string, etag string) ([]*FileInfo, *AppError) {
- c.clearExtraProperties()
-
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/get_file_infos", postId), "", etag); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return FileInfosFromJson(r.Body), nil
- }
-}
-
-// Saves an emoji reaction for a post in the given channel. Returns the saved reaction if successful, otherwise returns an AppError.
-func (c *Client) SaveReaction(channelId string, reaction *Reaction) (*Reaction, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions/save", reaction.PostId), reaction.ToJson()); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return ReactionFromJson(r.Body), nil
- }
-}
-
-// Removes an emoji reaction for a post in the given channel. Returns nil if successful, otherwise returns an AppError.
-func (c *Client) DeleteReaction(channelId string, reaction *Reaction) *AppError {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions/delete", reaction.PostId), reaction.ToJson()); err != nil {
- return err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return nil
- }
-}
-
-// Lists all emoji reactions made for the given post in the given channel. Returns a list of Reactions if successful, otherwise returns an AppError.
-func (c *Client) ListReactions(channelId string, postId string) ([]*Reaction, *AppError) {
- if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/reactions", postId), "", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- c.fillInExtraProperties(r)
- return ReactionsFromJson(r.Body), nil
- }
-}
-
-// Updates the user's roles in the channel by replacing them with the roles provided.
-func (c *Client) UpdateChannelRoles(channelId string, userId string, roles string) (map[string]string, *ResponseMetadata) {
- data := make(map[string]string)
- data["new_roles"] = roles
- data["user_id"] = userId
-
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/update_member_roles", MapToJson(data)); err != nil {
- metadata := ResponseMetadata{Error: err}
- if r != nil {
- metadata.StatusCode = r.StatusCode
- }
- return nil, &metadata
- } else {
- defer closeBody(r)
- return MapFromJson(r.Body),
- &ResponseMetadata{
- StatusCode: r.StatusCode,
- RequestId: r.Header.Get(HEADER_REQUEST_ID),
- Etag: r.Header.Get(HEADER_ETAG_SERVER),
- }
- }
-}
-
-func (c *Client) PinPost(channelId string, postId string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/posts/"+postId+"/pin", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
-
-func (c *Client) UnpinPost(channelId string, postId string) (*Result, *AppError) {
- if r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/posts/"+postId+"/unpin", ""); err != nil {
- return nil, err
- } else {
- defer closeBody(r)
- return &Result{r.Header.Get(HEADER_REQUEST_ID),
- r.Header.Get(HEADER_ETAG_SERVER), PostFromJson(r.Body)}, nil
- }
-}
diff --git a/vendor/github.com/mattermost/platform/model/message_export.go b/vendor/github.com/mattermost/platform/model/message_export.go
deleted file mode 100644
index b59b114d..00000000
--- a/vendor/github.com/mattermost/platform/model/message_export.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-type MessageExport struct {
- ChannelId *string
- ChannelDisplayName *string
-
- UserId *string
- UserEmail *string
-
- PostId *string
- PostCreateAt *int64
- PostMessage *string
- PostType *string
- PostFileIds StringArray
-}
diff --git a/vendor/github.com/mattermost/platform/model/post.go b/vendor/github.com/mattermost/platform/model/post.go
deleted file mode 100644
index 6b282fbf..00000000
--- a/vendor/github.com/mattermost/platform/model/post.go
+++ /dev/null
@@ -1,394 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "encoding/json"
- "io"
- "net/http"
- "regexp"
- "strings"
- "unicode/utf8"
-)
-
-const (
- POST_SYSTEM_MESSAGE_PREFIX = "system_"
- POST_DEFAULT = ""
- POST_SLACK_ATTACHMENT = "slack_attachment"
- POST_SYSTEM_GENERIC = "system_generic"
- POST_JOIN_LEAVE = "system_join_leave" // Deprecated, use POST_JOIN_CHANNEL or POST_LEAVE_CHANNEL instead
- POST_JOIN_CHANNEL = "system_join_channel"
- POST_LEAVE_CHANNEL = "system_leave_channel"
- POST_JOIN_TEAM = "system_join_team"
- POST_LEAVE_TEAM = "system_leave_team"
- POST_ADD_REMOVE = "system_add_remove" // Deprecated, use POST_ADD_TO_CHANNEL or POST_REMOVE_FROM_CHANNEL instead
- POST_ADD_TO_CHANNEL = "system_add_to_channel"
- POST_REMOVE_FROM_CHANNEL = "system_remove_from_channel"
- POST_ADD_TO_TEAM = "system_add_to_team"
- POST_REMOVE_FROM_TEAM = "system_remove_from_team"
- POST_HEADER_CHANGE = "system_header_change"
- POST_DISPLAYNAME_CHANGE = "system_displayname_change"
- POST_PURPOSE_CHANGE = "system_purpose_change"
- POST_CHANNEL_DELETED = "system_channel_deleted"
- POST_EPHEMERAL = "system_ephemeral"
- POST_FILEIDS_MAX_RUNES = 150
- POST_FILENAMES_MAX_RUNES = 4000
- POST_HASHTAGS_MAX_RUNES = 1000
- POST_MESSAGE_MAX_RUNES = 4000
- POST_PROPS_MAX_RUNES = 8000
- POST_PROPS_MAX_USER_RUNES = POST_PROPS_MAX_RUNES - 400 // Leave some room for system / pre-save modifications
- POST_CUSTOM_TYPE_PREFIX = "custom_"
- PROPS_ADD_CHANNEL_MEMBER = "add_channel_member"
-)
-
-type Post struct {
- Id string `json:"id"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- EditAt int64 `json:"edit_at"`
- DeleteAt int64 `json:"delete_at"`
- IsPinned bool `json:"is_pinned"`
- UserId string `json:"user_id"`
- ChannelId string `json:"channel_id"`
- RootId string `json:"root_id"`
- ParentId string `json:"parent_id"`
- OriginalId string `json:"original_id"`
- Message string `json:"message"`
- Type string `json:"type"`
- Props StringInterface `json:"props"`
- Hashtags string `json:"hashtags"`
- Filenames StringArray `json:"filenames,omitempty"` // Deprecated, do not use this field any more
- FileIds StringArray `json:"file_ids,omitempty"`
- PendingPostId string `json:"pending_post_id" db:"-"`
- HasReactions bool `json:"has_reactions,omitempty"`
-}
-
-type PostPatch struct {
- IsPinned *bool `json:"is_pinned"`
- Message *string `json:"message"`
- Props *StringInterface `json:"props"`
- FileIds *StringArray `json:"file_ids"`
- HasReactions *bool `json:"has_reactions"`
-}
-
-type PostForIndexing struct {
- Post
- TeamId string `json:"team_id"`
- ParentCreateAt *int64 `json:"parent_create_at"`
-}
-
-type PostAction struct {
- Id string `json:"id"`
- Name string `json:"name"`
- Integration *PostActionIntegration `json:"integration,omitempty"`
-}
-
-type PostActionIntegration struct {
- URL string `json:"url,omitempty"`
- Context StringInterface `json:"context,omitempty"`
-}
-
-type PostActionIntegrationRequest struct {
- UserId string `json:"user_id"`
- Context StringInterface `json:"context,omitempty"`
-}
-
-type PostActionIntegrationResponse struct {
- Update *Post `json:"update"`
- EphemeralText string `json:"ephemeral_text"`
-}
-
-func (o *Post) ToJson() string {
- copy := *o
- copy.StripActionIntegrations()
- b, err := json.Marshal(&copy)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func PostFromJson(data io.Reader) *Post {
- decoder := json.NewDecoder(data)
- var o Post
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}
-
-func (o *Post) Etag() string {
- return Etag(o.Id, o.UpdateAt)
-}
-
-func (o *Post) IsValid() *AppError {
-
- if len(o.Id) != 26 {
- return NewAppError("Post.IsValid", "model.post.is_valid.id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if o.CreateAt == 0 {
- return NewAppError("Post.IsValid", "model.post.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if o.UpdateAt == 0 {
- return NewAppError("Post.IsValid", "model.post.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if len(o.UserId) != 26 {
- return NewAppError("Post.IsValid", "model.post.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if len(o.ChannelId) != 26 {
- return NewAppError("Post.IsValid", "model.post.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if !(len(o.RootId) == 26 || len(o.RootId) == 0) {
- return NewAppError("Post.IsValid", "model.post.is_valid.root_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if !(len(o.ParentId) == 26 || len(o.ParentId) == 0) {
- return NewAppError("Post.IsValid", "model.post.is_valid.parent_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if len(o.ParentId) == 26 && len(o.RootId) == 0 {
- return NewAppError("Post.IsValid", "model.post.is_valid.root_parent.app_error", nil, "", http.StatusBadRequest)
- }
-
- if !(len(o.OriginalId) == 26 || len(o.OriginalId) == 0) {
- return NewAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "", http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(o.Message) > POST_MESSAGE_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.msg.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(o.Hashtags) > POST_HASHTAGS_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.hashtags.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- switch o.Type {
- case
- POST_DEFAULT,
- POST_JOIN_LEAVE,
- POST_ADD_REMOVE,
- POST_JOIN_CHANNEL,
- POST_LEAVE_CHANNEL,
- POST_JOIN_TEAM,
- POST_LEAVE_TEAM,
- POST_ADD_TO_CHANNEL,
- POST_REMOVE_FROM_CHANNEL,
- POST_ADD_TO_TEAM,
- POST_REMOVE_FROM_TEAM,
- POST_SLACK_ATTACHMENT,
- POST_HEADER_CHANGE,
- POST_PURPOSE_CHANGE,
- POST_DISPLAYNAME_CHANGE,
- POST_CHANNEL_DELETED:
- default:
- if !strings.HasPrefix(o.Type, POST_CUSTOM_TYPE_PREFIX) {
- return NewAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type, http.StatusBadRequest)
- }
- }
-
- if utf8.RuneCountInString(ArrayToJson(o.Filenames)) > POST_FILENAMES_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(ArrayToJson(o.FileIds)) > POST_FILEIDS_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.file_ids.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > POST_PROPS_MAX_RUNES {
- return NewAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id, http.StatusBadRequest)
- }
-
- return nil
-}
-
-func (o *Post) SanitizeProps() {
- membersToSanitize := []string{
- PROPS_ADD_CHANNEL_MEMBER,
- }
-
- for _, member := range membersToSanitize {
- if _, ok := o.Props[member]; ok {
- delete(o.Props, member)
- }
- }
-}
-
-func (o *Post) PreSave() {
- if o.Id == "" {
- o.Id = NewId()
- }
-
- o.OriginalId = ""
-
- if o.CreateAt == 0 {
- o.CreateAt = GetMillis()
- }
-
- o.UpdateAt = o.CreateAt
- o.PreCommit()
-}
-
-func (o *Post) PreCommit() {
- if o.Props == nil {
- o.Props = make(map[string]interface{})
- }
-
- if o.Filenames == nil {
- o.Filenames = []string{}
- }
-
- if o.FileIds == nil {
- o.FileIds = []string{}
- }
-
- o.GenerateActionIds()
-}
-
-func (o *Post) MakeNonNil() {
- if o.Props == nil {
- o.Props = make(map[string]interface{})
- }
-}
-
-func (o *Post) AddProp(key string, value interface{}) {
-
- o.MakeNonNil()
-
- o.Props[key] = value
-}
-
-func (o *Post) IsSystemMessage() bool {
- return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
-}
-
-func (p *Post) Patch(patch *PostPatch) {
- if patch.IsPinned != nil {
- p.IsPinned = *patch.IsPinned
- }
-
- if patch.Message != nil {
- p.Message = *patch.Message
- }
-
- if patch.Props != nil {
- p.Props = *patch.Props
- }
-
- if patch.FileIds != nil {
- p.FileIds = *patch.FileIds
- }
-
- if patch.HasReactions != nil {
- p.HasReactions = *patch.HasReactions
- }
-}
-
-func (o *PostPatch) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- }
-
- return string(b)
-}
-
-func PostPatchFromJson(data io.Reader) *PostPatch {
- decoder := json.NewDecoder(data)
- var post PostPatch
- err := decoder.Decode(&post)
- if err != nil {
- return nil
- }
-
- return &post
-}
-
-var channelMentionRegexp = regexp.MustCompile(`\B~[a-zA-Z0-9\-_]+`)
-
-func (o *Post) ChannelMentions() (names []string) {
- if strings.Contains(o.Message, "~") {
- alreadyMentioned := make(map[string]bool)
- for _, match := range channelMentionRegexp.FindAllString(o.Message, -1) {
- name := match[1:]
- if !alreadyMentioned[name] {
- names = append(names, name)
- alreadyMentioned[name] = true
- }
- }
- }
- return
-}
-
-func (r *PostActionIntegrationRequest) ToJson() string {
- b, err := json.Marshal(r)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func (o *Post) Attachments() []*SlackAttachment {
- if attachments, ok := o.Props["attachments"].([]*SlackAttachment); ok {
- return attachments
- }
- var ret []*SlackAttachment
- if attachments, ok := o.Props["attachments"].([]interface{}); ok {
- for _, attachment := range attachments {
- if enc, err := json.Marshal(attachment); err == nil {
- var decoded SlackAttachment
- if json.Unmarshal(enc, &decoded) == nil {
- ret = append(ret, &decoded)
- }
- }
- }
- }
- return ret
-}
-
-func (o *Post) StripActionIntegrations() {
- attachments := o.Attachments()
- if o.Props["attachments"] != nil {
- o.Props["attachments"] = attachments
- }
- for _, attachment := range attachments {
- for _, action := range attachment.Actions {
- action.Integration = nil
- }
- }
-}
-
-func (o *Post) GetAction(id string) *PostAction {
- for _, attachment := range o.Attachments() {
- for _, action := range attachment.Actions {
- if action.Id == id {
- return action
- }
- }
- }
- return nil
-}
-
-func (o *Post) GenerateActionIds() {
- if o.Props["attachments"] != nil {
- o.Props["attachments"] = o.Attachments()
- }
- if attachments, ok := o.Props["attachments"].([]*SlackAttachment); ok {
- for _, attachment := range attachments {
- for _, action := range attachment.Actions {
- if action.Id == "" {
- action.Id = NewId()
- }
- }
- }
- }
-}
diff --git a/vendor/github.com/mattermost/platform/model/scheduled_task.go b/vendor/github.com/mattermost/platform/model/scheduled_task.go
deleted file mode 100644
index 453828bd..00000000
--- a/vendor/github.com/mattermost/platform/model/scheduled_task.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "fmt"
- "sync"
- "time"
-)
-
-type TaskFunc func()
-
-type ScheduledTask struct {
- Name string `json:"name"`
- Interval time.Duration `json:"interval"`
- Recurring bool `json:"recurring"`
- function TaskFunc
- timer *time.Timer
-}
-
-var taskMutex = sync.Mutex{}
-var tasks = make(map[string]*ScheduledTask)
-
-func addTask(task *ScheduledTask) {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- tasks[task.Name] = task
-}
-
-func removeTaskByName(name string) {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- delete(tasks, name)
-}
-
-func GetTaskByName(name string) *ScheduledTask {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- if task, ok := tasks[name]; ok {
- return task
- }
- return nil
-}
-
-func GetAllTasks() *map[string]*ScheduledTask {
- taskMutex.Lock()
- defer taskMutex.Unlock()
- return &tasks
-}
-
-func CreateTask(name string, function TaskFunc, timeToExecution time.Duration) *ScheduledTask {
- task := &ScheduledTask{
- Name: name,
- Interval: timeToExecution,
- Recurring: false,
- function: function,
- }
-
- taskRunner := func() {
- go task.function()
- removeTaskByName(task.Name)
- }
-
- task.timer = time.AfterFunc(timeToExecution, taskRunner)
-
- addTask(task)
-
- return task
-}
-
-func CreateRecurringTask(name string, function TaskFunc, interval time.Duration) *ScheduledTask {
- task := &ScheduledTask{
- Name: name,
- Interval: interval,
- Recurring: true,
- function: function,
- }
-
- taskRecurer := func() {
- go task.function()
- task.timer.Reset(task.Interval)
- }
-
- task.timer = time.AfterFunc(interval, taskRecurer)
-
- addTask(task)
-
- return task
-}
-
-func (task *ScheduledTask) Cancel() {
- task.timer.Stop()
- removeTaskByName(task.Name)
-}
-
-// Executes the task immediatly. A recurring task will be run regularally after interval.
-func (task *ScheduledTask) Execute() {
- task.function()
- task.timer.Reset(task.Interval)
-}
-
-func (task *ScheduledTask) String() string {
- return fmt.Sprintf(
- "%s\nInterval: %s\nRecurring: %t\n",
- task.Name,
- task.Interval.String(),
- task.Recurring,
- )
-}
diff --git a/vendor/github.com/mattermost/platform/model/system.go b/vendor/github.com/mattermost/platform/model/system.go
deleted file mode 100644
index e2f4283a..00000000
--- a/vendor/github.com/mattermost/platform/model/system.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "encoding/json"
- "io"
-)
-
-const (
- SYSTEM_DIAGNOSTIC_ID = "DiagnosticId"
- SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
- SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
- SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
- SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
-)
-
-type System struct {
- Name string `json:"name"`
- Value string `json:"value"`
-}
-
-func (o *System) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func SystemFromJson(data io.Reader) *System {
- decoder := json.NewDecoder(data)
- var o System
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}
diff --git a/vendor/github.com/mattermost/platform/model/websocket_message.go b/vendor/github.com/mattermost/platform/model/websocket_message.go
deleted file mode 100644
index bf2535dc..00000000
--- a/vendor/github.com/mattermost/platform/model/websocket_message.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package model
-
-import (
- "encoding/json"
- "io"
-)
-
-const (
- WEBSOCKET_EVENT_TYPING = "typing"
- WEBSOCKET_EVENT_POSTED = "posted"
- WEBSOCKET_EVENT_POST_EDITED = "post_edited"
- WEBSOCKET_EVENT_POST_DELETED = "post_deleted"
- WEBSOCKET_EVENT_CHANNEL_DELETED = "channel_deleted"
- WEBSOCKET_EVENT_CHANNEL_CREATED = "channel_created"
- WEBSOCKET_EVENT_CHANNEL_UPDATED = "channel_updated"
- WEBSOCKET_EVENT_DIRECT_ADDED = "direct_added"
- WEBSOCKET_EVENT_GROUP_ADDED = "group_added"
- WEBSOCKET_EVENT_NEW_USER = "new_user"
- WEBSOCKET_EVENT_ADDED_TO_TEAM = "added_to_team"
- WEBSOCKET_EVENT_LEAVE_TEAM = "leave_team"
- WEBSOCKET_EVENT_UPDATE_TEAM = "update_team"
- WEBSOCKET_EVENT_USER_ADDED = "user_added"
- WEBSOCKET_EVENT_USER_UPDATED = "user_updated"
- WEBSOCKET_EVENT_USER_ROLE_UPDATED = "user_role_updated"
- WEBSOCKET_EVENT_MEMBERROLE_UPDATED = "memberrole_updated"
- WEBSOCKET_EVENT_USER_REMOVED = "user_removed"
- WEBSOCKET_EVENT_PREFERENCE_CHANGED = "preference_changed"
- WEBSOCKET_EVENT_PREFERENCES_CHANGED = "preferences_changed"
- WEBSOCKET_EVENT_PREFERENCES_DELETED = "preferences_deleted"
- WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message"
- WEBSOCKET_EVENT_STATUS_CHANGE = "status_change"
- WEBSOCKET_EVENT_HELLO = "hello"
- WEBSOCKET_EVENT_WEBRTC = "webrtc"
- WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge"
- WEBSOCKET_EVENT_REACTION_ADDED = "reaction_added"
- WEBSOCKET_EVENT_REACTION_REMOVED = "reaction_removed"
- WEBSOCKET_EVENT_RESPONSE = "response"
- WEBSOCKET_EVENT_EMOJI_ADDED = "emoji_added"
- WEBSOCKET_EVENT_CHANNEL_VIEWED = "channel_viewed"
- WEBSOCKET_EVENT_PLUGIN_ACTIVATED = "plugin_activated" // EXPERIMENTAL - SUBJECT TO CHANGE
- WEBSOCKET_EVENT_PLUGIN_DEACTIVATED = "plugin_deactivated" // EXPERIMENTAL - SUBJECT TO CHANGE
-)
-
-type WebSocketMessage interface {
- ToJson() string
- IsValid() bool
- EventType() string
-}
-
-type WebsocketBroadcast struct {
- OmitUsers map[string]bool `json:"omit_users"` // broadcast is omitted for users listed here
- UserId string `json:"user_id"` // broadcast only occurs for this user
- ChannelId string `json:"channel_id"` // broadcast only occurs for users in this channel
- TeamId string `json:"team_id"` // broadcast only occurs for users in this team
-}
-
-type WebSocketEvent struct {
- Event string `json:"event"`
- Data map[string]interface{} `json:"data"`
- Broadcast *WebsocketBroadcast `json:"broadcast"`
- Sequence int64 `json:"seq"`
-}
-
-func (m *WebSocketEvent) Add(key string, value interface{}) {
- m.Data[key] = value
-}
-
-func NewWebSocketEvent(event, teamId, channelId, userId string, omitUsers map[string]bool) *WebSocketEvent {
- return &WebSocketEvent{Event: event, Data: make(map[string]interface{}),
- Broadcast: &WebsocketBroadcast{TeamId: teamId, ChannelId: channelId, UserId: userId, OmitUsers: omitUsers}}
-}
-
-func (o *WebSocketEvent) IsValid() bool {
- return o.Event != ""
-}
-
-func (o *WebSocketEvent) EventType() string {
- return o.Event
-}
-
-func (o *WebSocketEvent) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
- decoder := json.NewDecoder(data)
- var o WebSocketEvent
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}
-
-type WebSocketResponse struct {
- Status string `json:"status"`
- SeqReply int64 `json:"seq_reply,omitempty"`
- Data map[string]interface{} `json:"data,omitempty"`
- Error *AppError `json:"error,omitempty"`
-}
-
-func (m *WebSocketResponse) Add(key string, value interface{}) {
- m.Data[key] = value
-}
-
-func NewWebSocketResponse(status string, seqReply int64, data map[string]interface{}) *WebSocketResponse {
- return &WebSocketResponse{Status: status, SeqReply: seqReply, Data: data}
-}
-
-func NewWebSocketError(seqReply int64, err *AppError) *WebSocketResponse {
- return &WebSocketResponse{Status: STATUS_FAIL, SeqReply: seqReply, Error: err}
-}
-
-func (o *WebSocketResponse) IsValid() bool {
- return o.Status != ""
-}
-
-func (o *WebSocketResponse) EventType() string {
- return WEBSOCKET_EVENT_RESPONSE
-}
-
-func (o *WebSocketResponse) ToJson() string {
- b, err := json.Marshal(o)
- if err != nil {
- return ""
- } else {
- return string(b)
- }
-}
-
-func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse {
- decoder := json.NewDecoder(data)
- var o WebSocketResponse
- err := decoder.Decode(&o)
- if err == nil {
- return &o
- } else {
- return nil
- }
-}