diff options
Diffstat (limited to 'vendor/github.com/mattermost/platform/model')
42 files changed, 6796 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/platform/model/LICENSE.txt b/vendor/github.com/mattermost/platform/model/LICENSE.txt new file mode 100644 index 00000000..b05ccb40 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/LICENSE.txt @@ -0,0 +1,897 @@ +Mattermost Licensing + +SOFTWARE LICENSING + +You are licensed to use compiled versions of the Mattermost platform produced by Mattermost, Inc. under an MIT LICENSE + +- See MIT-COMPILED-LICENSE.md included in compiled versions for details + +You may be licensed to use source code to create compiled versions not produced by Mattermost, Inc. in one of two ways: + +1. Under the Free Software Foundation’s GNU AGPL v.3.0, subject to the exceptions outlined in this policy; or +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 (api/templates/, config/, model/, +web/react/utils/, web/static/, web/templates/ 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 +(b) you have not modified, added to or adapted the source code of Mattermost in a way that results in the creation of +a “modified version” or “work based on” Mattermost as these terms are defined in the AGPL v3.0 license. + +MATTERMOST TRADEMARK GUIDELINES + +Your use of the mark Mattermost is subject to Mattermost, Inc's prior written approval and our organization’s Trademark +Standards of Use at http://www.mattermost.org/trademark-standards-of-use/. For trademark approval or any questions +you have about using these trademarks, please email trademark@mattermost.com + +------------------------------------------------------------------------------------------------------------------------------ + + 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. + +------------------------------------------------------------------------------ + +The software is released under the terms of the GNU Affero General Public +License, version 3. + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<http://www.gnu.org/licenses/>. diff --git a/vendor/github.com/mattermost/platform/model/access.go b/vendor/github.com/mattermost/platform/model/access.go new file mode 100644 index 00000000..877b3c4f --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/access.go @@ -0,0 +1,93 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + ACCESS_TOKEN_GRANT_TYPE = "authorization_code" + ACCESS_TOKEN_TYPE = "bearer" + REFRESH_TOKEN_GRANT_TYPE = "refresh_token" +) + +type AccessData struct { + AuthCode string `json:"auth_code"` + Token string `json:"token"` + RefreshToken string `json:"refresh_token"` + RedirectUri string `json:"redirect_uri"` +} + +type AccessResponse struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int32 `json:"expires_in"` + Scope string `json:"scope"` + RefreshToken string `json:"refresh_token"` +} + +// IsValid validates the AccessData and returns an error if it isn't configured +// correctly. +func (ad *AccessData) IsValid() *AppError { + + if len(ad.AuthCode) == 0 || len(ad.AuthCode) > 128 { + return NewLocAppError("AccessData.IsValid", "model.access.is_valid.auth_code.app_error", nil, "") + } + + if len(ad.Token) != 26 { + return NewLocAppError("AccessData.IsValid", "model.access.is_valid.access_token.app_error", nil, "") + } + + if len(ad.RefreshToken) > 26 { + return NewLocAppError("AccessData.IsValid", "model.access.is_valid.refresh_token.app_error", nil, "") + } + + if len(ad.RedirectUri) > 256 { + return NewLocAppError("AccessData.IsValid", "model.access.is_valid.redirect_uri.app_error", nil, "") + } + + return nil +} + +func (ad *AccessData) ToJson() string { + b, err := json.Marshal(ad) + if err != nil { + return "" + } else { + 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 + } +} + +func (ar *AccessResponse) ToJson() string { + b, err := json.Marshal(ar) + if err != nil { + return "" + } else { + 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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/analytics_row.go b/vendor/github.com/mattermost/platform/model/analytics_row.go new file mode 100644 index 00000000..ed1d69dd --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/analytics_row.go @@ -0,0 +1,55 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type AnalyticsRow struct { + Name string `json:"name"` + Value float64 `json:"value"` +} + +type AnalyticsRows []*AnalyticsRow + +func (me *AnalyticsRow) ToJson() string { + b, err := json.Marshal(me) + if err != nil { + return "" + } else { + 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 + } +} + +func (me AnalyticsRows) ToJson() string { + if b, err := json.Marshal(me); err != nil { + return "[]" + } else { + return string(b) + } +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/audit.go b/vendor/github.com/mattermost/platform/model/audit.go new file mode 100644 index 00000000..8fa1d558 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/audit.go @@ -0,0 +1,39 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type Audit struct { + Id string `json:"id"` + CreateAt int64 `json:"create_at"` + UserId string `json:"user_id"` + Action string `json:"action"` + ExtraInfo string `json:"extra_info"` + IpAddress string `json:"ip_address"` + SessionId string `json:"session_id"` +} + +func (o *Audit) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/audits.go b/vendor/github.com/mattermost/platform/model/audits.go new file mode 100644 index 00000000..36c80629 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/audits.go @@ -0,0 +1,39 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type Audits []Audit + +func (o Audits) Etag() string { + if len(o) > 0 { + // the first in the list is always the most current + return Etag(o[0].CreateAt) + } else { + return "" + } +} + +func (o Audits) ToJson() string { + if b, err := json.Marshal(o); err != nil { + return "[]" + } else { + return string(b) + } +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/authorize.go b/vendor/github.com/mattermost/platform/model/authorize.go new file mode 100644 index 00000000..e0d665ba --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/authorize.go @@ -0,0 +1,103 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + AUTHCODE_EXPIRE_TIME = 60 * 10 // 10 minutes + AUTHCODE_RESPONSE_TYPE = "code" +) + +type AuthData struct { + ClientId string `json:"client_id"` + UserId string `json:"user_id"` + Code string `json:"code"` + ExpiresIn int32 `json:"expires_in"` + CreateAt int64 `json:"create_at"` + RedirectUri string `json:"redirect_uri"` + State string `json:"state"` + Scope string `json:"scope"` +} + +// IsValid validates the AuthData and returns an error if it isn't configured +// correctly. +func (ad *AuthData) IsValid() *AppError { + + if len(ad.ClientId) != 26 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.client_id.app_error", nil, "") + } + + if len(ad.UserId) != 26 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.user_id.app_error", nil, "") + } + + if len(ad.Code) == 0 || len(ad.Code) > 128 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.auth_code.app_error", nil, "client_id="+ad.ClientId) + } + + if ad.ExpiresIn == 0 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.expires.app_error", nil, "") + } + + if ad.CreateAt <= 0 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.create_at.app_error", nil, "client_id="+ad.ClientId) + } + + if len(ad.RedirectUri) > 256 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.redirect_uri.app_error", nil, "client_id="+ad.ClientId) + } + + if len(ad.State) > 128 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.state.app_error", nil, "client_id="+ad.ClientId) + } + + if len(ad.Scope) > 128 { + return NewLocAppError("AuthData.IsValid", "model.authorize.is_valid.scope.app_error", nil, "client_id="+ad.ClientId) + } + + return nil +} + +func (ad *AuthData) PreSave() { + if ad.ExpiresIn == 0 { + ad.ExpiresIn = AUTHCODE_EXPIRE_TIME + } + + if ad.CreateAt == 0 { + ad.CreateAt = GetMillis() + } +} + +func (ad *AuthData) ToJson() string { + b, err := json.Marshal(ad) + if err != nil { + return "" + } else { + 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 + } +} + +func (ad *AuthData) IsExpired() bool { + + if GetMillis() > ad.CreateAt+int64(ad.ExpiresIn*1000) { + return true + } + + return false +} diff --git a/vendor/github.com/mattermost/platform/model/channel.go b/vendor/github.com/mattermost/platform/model/channel.go new file mode 100644 index 00000000..e7002e3c --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/channel.go @@ -0,0 +1,136 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "unicode/utf8" +) + +const ( + CHANNEL_OPEN = "O" + CHANNEL_PRIVATE = "P" + CHANNEL_DIRECT = "D" + DEFAULT_CHANNEL = "town-square" +) + +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"` +} + +func (o *Channel) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} + +func (o *Channel) Etag() string { + return Etag(o.Id, o.UpdateAt) +} + +func (o *Channel) ExtraEtag(memberLimit int) string { + return Etag(o.Id, o.ExtraUpdateAt, memberLimit) +} + +func (o *Channel) IsValid() *AppError { + + if len(o.Id) != 26 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.id.app_error", nil, "") + } + + if o.CreateAt == 0 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.create_at.app_error", nil, "id="+o.Id) + } + + if o.UpdateAt == 0 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+o.Id) + } + + if utf8.RuneCountInString(o.DisplayName) > 64 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id) + } + + if len(o.Name) > 64 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id) + } + + if !IsValidChannelIdentifier(o.Name) { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.2_or_more.app_error", nil, "id="+o.Id) + } + + if !(o.Type == CHANNEL_OPEN || o.Type == CHANNEL_PRIVATE || o.Type == CHANNEL_DIRECT) { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id) + } + + if utf8.RuneCountInString(o.Header) > 1024 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+o.Id) + } + + if utf8.RuneCountInString(o.Purpose) > 128 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+o.Id) + } + + if len(o.CreatorId) > 26 { + return NewLocAppError("Channel.IsValid", "model.channel.is_valid.creator_id.app_error", nil, "") + } + + return nil +} + +func (o *Channel) PreSave() { + if o.Id == "" { + o.Id = NewId() + } + + o.CreateAt = GetMillis() + o.UpdateAt = o.CreateAt + o.ExtraUpdateAt = o.CreateAt +} + +func (o *Channel) PreUpdate() { + o.UpdateAt = GetMillis() +} + +func (o *Channel) ExtraUpdated() { + o.ExtraUpdateAt = GetMillis() +} + +func (o *Channel) PreExport() { +} + +func GetDMNameFromIds(userId1, userId2 string) string { + if userId1 > userId2 { + return userId2 + "__" + userId1 + } else { + return userId1 + "__" + userId2 + } +} diff --git a/vendor/github.com/mattermost/platform/model/channel_count.go b/vendor/github.com/mattermost/platform/model/channel_count.go new file mode 100644 index 00000000..6cc1b2f2 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/channel_count.go @@ -0,0 +1,63 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "crypto/md5" + "encoding/json" + "fmt" + "io" + "sort" + "strconv" +) + +type ChannelCounts struct { + Counts map[string]int64 `json:"counts"` + UpdateTimes map[string]int64 `json:"update_times"` +} + +func (o *ChannelCounts) Etag() string { + + ids := []string{} + for id := range o.Counts { + ids = append(ids, id) + } + sort.Strings(ids) + + str := "" + for _, id := range ids { + str += id + strconv.FormatInt(o.Counts[id], 10) + } + + md5Counts := fmt.Sprintf("%x", md5.Sum([]byte(str))) + + var update int64 = 0 + for _, u := range o.UpdateTimes { + if u > update { + update = u + } + } + + return Etag(md5Counts, update) +} + +func (o *ChannelCounts) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/channel_data.go b/vendor/github.com/mattermost/platform/model/channel_data.go new file mode 100644 index 00000000..731d50e7 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/channel_data.go @@ -0,0 +1,43 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type ChannelData struct { + Channel *Channel `json:"channel"` + Member *ChannelMember `json:"member"` +} + +func (o *ChannelData) Etag() string { + var mt int64 = 0 + if o.Member != nil { + mt = o.Member.LastUpdateAt + } + + return Etag(o.Channel.Id, o.Channel.UpdateAt, o.Channel.LastPostAt, mt) +} + +func (o *ChannelData) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/channel_extra.go b/vendor/github.com/mattermost/platform/model/channel_extra.go new file mode 100644 index 00000000..55da588a --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/channel_extra.go @@ -0,0 +1,49 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type ExtraMember struct { + Id string `json:"id"` + Nickname string `json:"nickname"` + Email string `json:"email"` + Roles string `json:"roles"` + Username string `json:"username"` +} + +func (o *ExtraMember) Sanitize(options map[string]bool) { + if len(options) == 0 || !options["email"] { + o.Email = "" + } +} + +type ChannelExtra struct { + Id string `json:"id"` + Members []ExtraMember `json:"members"` + MemberCount int64 `json:"member_count"` +} + +func (o *ChannelExtra) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func ChannelExtraFromJson(data io.Reader) *ChannelExtra { + decoder := json.NewDecoder(data) + var o ChannelExtra + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/channel_list.go b/vendor/github.com/mattermost/platform/model/channel_list.go new file mode 100644 index 00000000..49ba384a --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/channel_list.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type ChannelList struct { + Channels []*Channel `json:"channels"` + Members map[string]*ChannelMember `json:"members"` +} + +func (o *ChannelList) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func (o *ChannelList) Etag() string { + + id := "0" + var t int64 = 0 + var delta int64 = 0 + + for _, v := range o.Channels { + if v.LastPostAt > t { + t = v.LastPostAt + id = v.Id + } + + if v.UpdateAt > t { + t = v.UpdateAt + id = v.Id + } + + member := o.Members[v.Id] + + if member != nil { + max := v.LastPostAt + if v.UpdateAt > max { + max = v.UpdateAt + } + + delta += max - member.LastViewedAt + + if member.LastViewedAt > t { + t = member.LastViewedAt + id = v.Id + } + + if member.LastUpdateAt > t { + t = member.LastUpdateAt + id = v.Id + } + + } + } + + return Etag(id, t, delta, len(o.Channels)) +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/channel_member.go b/vendor/github.com/mattermost/platform/model/channel_member.go new file mode 100644 index 00000000..66e20da6 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/channel_member.go @@ -0,0 +1,108 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "strings" +) + +const ( + CHANNEL_ROLE_ADMIN = "admin" + CHANNEL_NOTIFY_DEFAULT = "default" + CHANNEL_NOTIFY_ALL = "all" + CHANNEL_NOTIFY_MENTION = "mention" + CHANNEL_NOTIFY_NONE = "none" + CHANNEL_MARK_UNREAD_ALL = "all" + CHANNEL_MARK_UNREAD_MENTION = "mention" +) + +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"` +} + +func (o *ChannelMember) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} + +func (o *ChannelMember) IsValid() *AppError { + + if len(o.ChannelId) != 26 { + return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.channel_id.app_error", nil, "") + } + + if len(o.UserId) != 26 { + return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "") + } + + for _, role := range strings.Split(o.Roles, " ") { + if !(role == "" || role == CHANNEL_ROLE_ADMIN) { + return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.role.app_error", nil, "role="+role) + } + } + + notifyLevel := o.NotifyProps["desktop"] + if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) { + return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error", + nil, "notify_level="+notifyLevel) + } + + markUnreadLevel := o.NotifyProps["mark_unread"] + if len(markUnreadLevel) > 20 || !IsChannelMarkUnreadLevelValid(markUnreadLevel) { + return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.unread_level.app_error", + nil, "mark_unread_level="+markUnreadLevel) + } + + return nil +} + +func (o *ChannelMember) PreSave() { + o.LastUpdateAt = GetMillis() +} + +func (o *ChannelMember) PreUpdate() { + o.LastUpdateAt = GetMillis() +} + +func IsChannelNotifyLevelValid(notifyLevel string) bool { + return notifyLevel == CHANNEL_NOTIFY_DEFAULT || + notifyLevel == CHANNEL_NOTIFY_ALL || + notifyLevel == CHANNEL_NOTIFY_MENTION || + notifyLevel == CHANNEL_NOTIFY_NONE +} + +func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool { + return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION +} + +func GetDefaultChannelNotifyProps() StringMap { + return StringMap{ + "desktop": CHANNEL_NOTIFY_DEFAULT, + "mark_unread": CHANNEL_MARK_UNREAD_ALL, + } +} diff --git a/vendor/github.com/mattermost/platform/model/client.go b/vendor/github.com/mattermost/platform/model/client.go new file mode 100644 index 00000000..fba4246e --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/client.go @@ -0,0 +1,1144 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "bytes" + "fmt" + l4g "github.com/alecthomas/log4go" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +const ( + HEADER_REQUEST_ID = "X-Request-ID" + HEADER_VERSION_ID = "X-Version-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" + API_URL_SUFFIX = "/api/v1" +) + +type Result struct { + RequestId string + Etag string + Data interface{} +} + +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/v1" + HttpClient *http.Client // The http client + AuthToken string + AuthType 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, &http.Client{}, "", ""} +} + +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) + + if rp, err := c.HttpClient.Do(rq); err != nil { + return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode >= 300 { + 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)) + + 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, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode >= 300 { + 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)) + + 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, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode == 304 { + return rp, nil + } else if rp.StatusCode >= 300 { + 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 +} + +func (c *Client) Must(result *Result, err *AppError) *Result { + if err != nil { + l4g.Close() + time.Sleep(time.Second) + panic(err) + } + + return result +} + +func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppError) { + m := make(map[string]string) + m["email"] = email + m["display_name"] = displayName + if r, err := c.DoApiPost("/teams/signup", MapToJson(m)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) CreateTeamFromSignup(teamSignup *TeamSignup) (*Result, *AppError) { + if r, err := c.DoApiPost("/teams/create_from_signup", teamSignup.ToJson()); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), TeamSignupFromJson(r.Body)}, nil + } +} + +func (c *Client) CreateTeam(team *Team) (*Result, *AppError) { + if r, err := c.DoApiPost("/teams/create", team.ToJson()); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil + } +} + +func (c *Client) GetAllTeams() (*Result, *AppError) { + if r, err := c.DoApiGet("/teams/all", "", ""); err != nil { + return nil, err + } else { + + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), TeamMapFromJson(r.Body)}, nil + } +} + +func (c *Client) FindTeamByName(name string, allServers bool) (*Result, *AppError) { + m := make(map[string]string) + m["name"] = name + m["all"] = fmt.Sprintf("%v", allServers) + 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 + } + + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), val}, nil + } +} + +func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) { + if r, err := c.DoApiPost("/teams/invite_members", invites.ToJson()); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), InvitesFromJson(r.Body)}, nil + } +} + +func (c *Client) UpdateTeam(team *Team) (*Result, *AppError) { + if r, err := c.DoApiPost("/teams/update", team.ToJson()); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +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 { + 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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil + } +} + +func (c *Client) GetUser(id string, etag string) (*Result, *AppError) { + if r, err := c.DoApiGet("/users/"+id, "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil + } +} + +func (c *Client) GetMe(etag string) (*Result, *AppError) { + if r, err := c.DoApiGet("/users/me", "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil + } +} + +func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) { + if r, err := c.DoApiGet("/users/profiles/"+teamId, "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil + } +} + +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) +} + +func (c *Client) LoginByEmail(name string, email string, password string) (*Result, *AppError) { + m := make(map[string]string) + m["name"] = name + m["email"] = email + m["password"] = password + return c.login(m) +} + +func (c *Client) LoginByUsername(name string, username string, password string) (*Result, *AppError) { + m := make(map[string]string) + m["name"] = name + m["username"] = username + m["password"] = password + return c.login(m) +} + +func (c *Client) LoginByEmailWithDevice(name string, email string, password string, deviceId string) (*Result, *AppError) { + m := make(map[string]string) + m["name"] = name + m["email"] = email + 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 { + NewLocAppError("/users/login", "model.client.login.app_error", nil, "") + } + + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil + } +} + +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 + + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) CheckMfa(method, teamName, loginId string) (*Result, *AppError) { + m := make(map[string]string) + m["method"] = method + m["team_name"] = teamName + m["login_id"] = loginId + + if r, err := c.DoApiPost("/users/mfa", MapToJson(m)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) GenerateMfaQrCode() (*Result, *AppError) { + if r, err := c.DoApiGet("/users/generate_mfa_qr", "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), r.Body}, nil + } +} + +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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +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) 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 { + 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 { + 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_sso", MapToJson(m)); err != nil { + return nil, err + } else { + 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 { + 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 { + 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 { + 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, suggest bool) (*Result, *AppError) { + m := make(map[string]string) + m["command"] = command + m["channelId"] = channelId + m["suggest"] = strconv.FormatBool(suggest) + if r, err := c.DoApiPost("/commands/execute", MapToJson(m)); err != nil { + return nil, err + } else { + 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("/commands/list", "", ""); err != nil { + return nil, err + } else { + 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("/commands/list_team_commands", "", ""); err != nil { + return nil, err + } else { + 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("/commands/create", cmd.ToJson()); err != nil { + return nil, err + } else { + 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("/commands/regen_token", MapToJson(data)); err != nil { + return nil, err + } else { + 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("/commands/delete", MapToJson(data)); err != nil { + return nil, err + } else { + 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 { + 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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ArrayFromJson(r.Body)}, nil + } +} + +func (c *Client) GetAllAudits() (*Result, *AppError) { + if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), AuditsFromJson(r.Body)}, nil + } +} + +func (c *Client) GetClientProperties() (*Result, *AppError) { + if r, err := c.DoApiGet("/admin/client_props", "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) GetConfig() (*Result, *AppError) { + if r, err := c.DoApiGet("/admin/config", "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ConfigFromJson(r.Body)}, 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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ConfigFromJson(r.Body)}, 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 { + 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 { + 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 { + 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) + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil { + return nil, NewLocAppError("/admin/download_compliance_report", "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode >= 300 { + return nil, AppErrorFromJson(rp.Body) + } else { + 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 { + 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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), AnalyticsRowsFromJson(r.Body)}, nil + } +} + +func (c *Client) CreateChannel(channel *Channel) (*Result, *AppError) { + if r, err := c.DoApiPost("/channels/create", channel.ToJson()); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ChannelFromJson(r.Body)}, nil + } +} + +func (c *Client) CreateDirectChannel(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/channels/create_direct", MapToJson(data)); err != nil { + return nil, err + } else { + 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("/channels/update", channel.ToJson()); err != nil { + return nil, err + } else { + 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("/channels/update_header", MapToJson(data)); err != nil { + return nil, err + } else { + 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("/channels/update_purpose", MapToJson(data)); err != nil { + return nil, err + } else { + 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("/channels/update_notify_props", MapToJson(data)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) GetChannels(etag string) (*Result, *AppError) { + if r, err := c.DoApiGet("/channels/", "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil + } +} + +func (c *Client) GetChannel(id, etag string) (*Result, *AppError) { + if r, err := c.DoApiGet("/channels/"+id+"/", "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ChannelDataFromJson(r.Body)}, nil + } +} + +func (c *Client) GetMoreChannels(etag string) (*Result, *AppError) { + if r, err := c.DoApiGet("/channels/more", "", etag); err != nil { + return nil, err + } else { + 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("/channels/counts", "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ChannelCountsFromJson(r.Body)}, nil + } +} + +func (c *Client) JoinChannel(id string) (*Result, *AppError) { + if r, err := c.DoApiPost("/channels/"+id+"/join", ""); err != nil { + return nil, err + } else { + 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("/channels/"+id+"/leave", ""); err != nil { + return nil, err + } else { + 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("/channels/"+id+"/delete", ""); err != nil { + return nil, err + } else { + 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("/channels/"+id+"/add", MapToJson(data)); err != nil { + return nil, err + } else { + 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("/channels/"+id+"/remove", MapToJson(data)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), nil}, nil + } +} + +func (c *Client) UpdateLastViewedAt(channelId string) (*Result, *AppError) { + if r, err := c.DoApiPost("/channels/"+channelId+"/update_last_viewed_at", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), nil}, nil + } +} + +func (c *Client) GetChannelExtraInfo(id string, memberLimit int, etag string) (*Result, *AppError) { + if r, err := c.DoApiGet("/channels/"+id+"/extra_info/"+strconv.FormatInt(int64(memberLimit), 10), "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), ChannelExtraFromJson(r.Body)}, nil + } +} + +func (c *Client) CreatePost(post *Post) (*Result, *AppError) { + if r, err := c.DoApiPost("/channels/"+post.ChannelId+"/create", post.ToJson()); err != nil { + return nil, err + } else { + 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("/channels/"+post.ChannelId+"/update", post.ToJson()); err != nil { + return nil, err + } else { + 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(fmt.Sprintf("/channels/%v/posts/%v/%v", channelId, offset, limit), "", etag); err != nil { + return nil, err + } else { + 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(fmt.Sprintf("/channels/%v/posts/%v", channelId, time), "", ""); err != nil { + return nil, err + } else { + 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(fmt.Sprintf("/channels/%v/post/%v/before/%v/%v", channelId, postid, offset, limit), "", etag); err != nil { + return nil, err + } else { + 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("/channels/%v/post/%v/after/%v/%v", channelId, postid, offset, limit), "", etag); err != nil { + return nil, err + } else { + 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(fmt.Sprintf("/channels/%v/post/%v", channelId, postId), "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil + } +} + +func (c *Client) DeletePost(channelId string, postId string) (*Result, *AppError) { + if r, err := c.DoApiPost(fmt.Sprintf("/channels/%v/post/%v/delete", channelId, postId), ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) SearchPosts(terms string) (*Result, *AppError) { + if r, err := c.DoApiGet("/posts/search?terms="+url.QueryEscape(terms), "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), PostListFromJson(r.Body)}, nil + } +} + +func (c *Client) UploadFile(url string, data []byte, contentType string) (*Result, *AppError) { + rq, _ := http.NewRequest("POST", c.ApiUrl+url, bytes.NewReader(data)) + rq.Header.Set("Content-Type", contentType) + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil { + return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode >= 300 { + return nil, AppErrorFromJson(rp.Body) + } else { + return &Result{rp.Header.Get(HEADER_REQUEST_ID), + rp.Header.Get(HEADER_ETAG_SERVER), FileUploadResponseFromJson(rp.Body)}, nil + } +} + +func (c *Client) GetFile(url string, isFullUrl bool) (*Result, *AppError) { + var rq *http.Request + if isFullUrl { + rq, _ = http.NewRequest("GET", url, nil) + } else { + rq, _ = http.NewRequest("GET", c.ApiUrl+"/files/get"+url, nil) + } + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil { + return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode >= 300 { + return nil, AppErrorFromJson(rp.Body) + } else { + return &Result{rp.Header.Get(HEADER_REQUEST_ID), + rp.Header.Get(HEADER_ETAG_SERVER), rp.Body}, nil + } +} + +func (c *Client) GetFileInfo(url string) (*Result, *AppError) { + var rq *http.Request + rq, _ = http.NewRequest("GET", c.ApiUrl+"/files/get_info"+url, nil) + + if len(c.AuthToken) > 0 { + rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) + } + + if rp, err := c.HttpClient.Do(rq); err != nil { + return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) + } else if rp.StatusCode >= 300 { + return nil, AppErrorFromJson(rp.Body) + } else { + return &Result{rp.Header.Get(HEADER_REQUEST_ID), + rp.Header.Get(HEADER_ETAG_SERVER), FileInfoFromJson(rp.Body)}, nil + } +} + +func (c *Client) GetPublicLink(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/files/get_public_link", MapToJson(data)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil + } +} + +func (c *Client) UpdateUserRoles(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/users/update_roles", MapToJson(data)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(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 { + 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 { + 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 { + 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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), UserFromJson(r.Body)}, nil + } +} + +func (c *Client) SendPasswordReset(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/users/send_password_reset", MapToJson(data)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) ResetPassword(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/users/reset_password", MapToJson(data)); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) GetStatuses(data []string) (*Result, *AppError) { + if r, err := c.DoApiPost("/users/status", ArrayToJson(data)); err != nil { + return nil, err + } else { + 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("/teams/me", "", etag); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), TeamFromJson(r.Body)}, nil + } +} + +func (c *Client) RegisterApp(app *OAuthApp) (*Result, *AppError) { + if r, err := c.DoApiPost("/oauth/register", app.ToJson()); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), OAuthAppFromJson(r.Body)}, nil + } +} + +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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) GetAccessToken(data url.Values) (*Result, *AppError) { + if r, err := c.DoApiPost("/oauth/access_token", data.Encode()); err != nil { + return nil, err + } else { + 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("/hooks/incoming/create", hook.ToJson()); err != nil { + return nil, err + } else { + 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 { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), nil}, nil + } +} + +func (c *Client) DeleteIncomingWebhook(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/hooks/incoming/delete", MapToJson(data)); err != nil { + return nil, err + } else { + 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("/hooks/incoming/list", "", ""); err != nil { + return nil, err + } else { + 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 { + 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 { + 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 { + 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 { + preferences, _ := PreferencesFromJson(r.Body) + return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil + } +} + +func (c *Client) CreateOutgoingWebhook(hook *OutgoingWebhook) (*Result, *AppError) { + if r, err := c.DoApiPost("/hooks/outgoing/create", hook.ToJson()); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookFromJson(r.Body)}, nil + } +} + +func (c *Client) DeleteOutgoingWebhook(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/hooks/outgoing/delete", MapToJson(data)); err != nil { + return nil, err + } else { + 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("/hooks/outgoing/list", "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), OutgoingWebhookListFromJson(r.Body)}, nil + } +} + +func (c *Client) RegenOutgoingWebhookToken(data map[string]string) (*Result, *AppError) { + if r, err := c.DoApiPost("/hooks/outgoing/regen_token", MapToJson(data)); err != nil { + return nil, err + } else { + 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() (*Result, *AppError) { + if r, err := c.DoApiGet("/license/client_config", "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} + +func (c *Client) GetMeLoggedIn() (*Result, *AppError) { + if r, err := c.DoApiGet("/users/me_logged_in", "", ""); err != nil { + return nil, err + } else { + return &Result{r.Header.Get(HEADER_REQUEST_ID), + r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/command.go b/vendor/github.com/mattermost/platform/model/command.go new file mode 100644 index 00000000..b854ae76 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/command.go @@ -0,0 +1,153 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + COMMAND_METHOD_POST = "P" + COMMAND_METHOD_GET = "G" +) + +type Command struct { + Id string `json:"id"` + Token string `json:"token"` + CreateAt int64 `json:"create_at"` + UpdateAt int64 `json:"update_at"` + DeleteAt int64 `json:"delete_at"` + CreatorId string `json:"creator_id"` + TeamId string `json:"team_id"` + Trigger string `json:"trigger"` + Method string `json:"method"` + Username string `json:"username"` + IconURL string `json:"icon_url"` + AutoComplete bool `json:"auto_complete"` + AutoCompleteDesc string `json:"auto_complete_desc"` + AutoCompleteHint string `json:"auto_complete_hint"` + DisplayName string `json:"display_name"` + Description string `json:"description"` + URL string `json:"url"` +} + +func (o *Command) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} + +func CommandListToJson(l []*Command) string { + b, err := json.Marshal(l) + if err != nil { + return "" + } else { + 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 + } +} + +func (o *Command) IsValid() *AppError { + + if len(o.Id) != 26 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.id.app_error", nil, "") + } + + if len(o.Token) != 26 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.token.app_error", nil, "") + } + + if o.CreateAt == 0 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.create_at.app_error", nil, "") + } + + if o.UpdateAt == 0 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.update_at.app_error", nil, "") + } + + if len(o.CreatorId) != 26 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.user_id.app_error", nil, "") + } + + if len(o.TeamId) != 26 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.team_id.app_error", nil, "") + } + + if len(o.Trigger) > 128 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.trigger.app_error", nil, "") + } + + if len(o.URL) == 0 || len(o.URL) > 1024 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.url.app_error", nil, "") + } + + if !IsValidHttpUrl(o.URL) { + return NewLocAppError("Command.IsValid", "model.command.is_valid.url_http.app_error", nil, "") + } + + if !(o.Method == COMMAND_METHOD_GET || o.Method == COMMAND_METHOD_POST) { + return NewLocAppError("Command.IsValid", "model.command.is_valid.method.app_error", nil, "") + } + + if len(o.DisplayName) > 64 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.display_name.app_error", nil, "") + } + + if len(o.Description) > 128 { + return NewLocAppError("Command.IsValid", "model.command.is_valid.description.app_error", nil, "") + } + + return nil +} + +func (o *Command) PreSave() { + if o.Id == "" { + o.Id = NewId() + } + + if o.Token == "" { + o.Token = NewId() + } + + o.CreateAt = GetMillis() + o.UpdateAt = o.CreateAt +} + +func (o *Command) PreUpdate() { + o.UpdateAt = GetMillis() +} + +func (o *Command) Sanitize() { + o.Token = "" + o.CreatorId = "" + o.Method = "" + o.URL = "" + o.Username = "" + o.IconURL = "" +} diff --git a/vendor/github.com/mattermost/platform/model/command_response.go b/vendor/github.com/mattermost/platform/model/command_response.go new file mode 100644 index 00000000..9314f38e --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/command_response.go @@ -0,0 +1,41 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + COMMAND_RESPONSE_TYPE_IN_CHANNEL = "in_channel" + COMMAND_RESPONSE_TYPE_EPHEMERAL = "ephemeral" +) + +type CommandResponse struct { + ResponseType string `json:"response_type"` + Text string `json:"text"` + GotoLocation string `json:"goto_location"` + Attachments interface{} `json:"attachments"` +} + +func (o *CommandResponse) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func CommandResponseFromJson(data io.Reader) *CommandResponse { + decoder := json.NewDecoder(data) + var o CommandResponse + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/compliance.go b/vendor/github.com/mattermost/platform/model/compliance.go new file mode 100644 index 00000000..4a96a597 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/compliance.go @@ -0,0 +1,132 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "strings" +) + +const ( + COMPLIANCE_STATUS_CREATED = "created" + COMPLIANCE_STATUS_RUNNING = "running" + COMPLIANCE_STATUS_FINISHED = "finished" + COMPLIANCE_STATUS_FAILED = "failed" + COMPLIANCE_STATUS_REMOVED = "removed" + + COMPLIANCE_TYPE_DAILY = "daily" + COMPLIANCE_TYPE_ADHOC = "adhoc" +) + +type Compliance struct { + Id string `json:"id"` + CreateAt int64 `json:"create_at"` + UserId string `json:"user_id"` + Status string `json:"status"` + Count int `json:"count"` + Desc string `json:"desc"` + Type string `json:"type"` + StartAt int64 `json:"start_at"` + EndAt int64 `json:"end_at"` + Keywords string `json:"keywords"` + Emails string `json:"emails"` +} + +type Compliances []Compliance + +func (o *Compliance) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func (me *Compliance) PreSave() { + if me.Id == "" { + me.Id = NewId() + } + + if me.Status == "" { + me.Status = COMPLIANCE_STATUS_CREATED + } + + me.Count = 0 + me.Emails = strings.ToLower(me.Emails) + me.Keywords = strings.ToLower(me.Keywords) + + me.CreateAt = GetMillis() +} + +func (me *Compliance) JobName() string { + jobName := me.Type + if me.Type == COMPLIANCE_TYPE_DAILY { + jobName += "-" + me.Desc + } + + jobName += "-" + me.Id + + return jobName +} + +func (me *Compliance) IsValid() *AppError { + + if len(me.Id) != 26 { + return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.id.app_error", nil, "") + } + + if me.CreateAt == 0 { + return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.create_at.app_error", nil, "") + } + + if len(me.Desc) > 512 || len(me.Desc) == 0 { + return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.desc.app_error", nil, "") + } + + if me.StartAt == 0 { + return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.start_at.app_error", nil, "") + } + + if me.EndAt == 0 { + return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.end_at.app_error", nil, "") + } + + if me.EndAt <= me.StartAt { + return NewLocAppError("Compliance.IsValid", "model.compliance.is_valid.start_end_at.app_error", nil, "") + } + + return nil +} + +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 + } +} + +func (o Compliances) ToJson() string { + if b, err := json.Marshal(o); err != nil { + return "[]" + } else { + return string(b) + } +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/compliance_post.go b/vendor/github.com/mattermost/platform/model/compliance_post.go new file mode 100644 index 00000000..ce26a366 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/compliance_post.go @@ -0,0 +1,104 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "time" +) + +type CompliancePost struct { + + // From Team + TeamName string + TeamDisplayName string + + // From Channel + ChannelName string + ChannelDisplayName string + + // From User + UserUsername string + UserEmail string + UserNickname string + + // From Post + PostId string + PostCreateAt int64 + PostUpdateAt int64 + PostDeleteAt int64 + PostRootId string + PostParentId string + PostOriginalId string + PostMessage string + PostType string + PostProps string + PostHashtags string + PostFilenames string +} + +func CompliancePostHeader() []string { + return []string{ + "TeamName", + "TeamDisplayName", + + "ChannelName", + "ChannelDisplayName", + + "UserUsername", + "UserEmail", + "UserNickname", + + "PostId", + "PostCreateAt", + "PostUpdateAt", + "PostDeleteAt", + "PostRootId", + "PostParentId", + "PostOriginalId", + "PostMessage", + "PostType", + "PostProps", + "PostHashtags", + "PostFilenames", + } +} + +func (me *CompliancePost) Row() []string { + + postDeleteAt := "" + if me.PostDeleteAt > 0 { + postDeleteAt = time.Unix(0, me.PostDeleteAt*int64(1000*1000)).Format(time.RFC3339) + } + + postUpdateAt := "" + if me.PostUpdateAt != me.PostCreateAt { + postUpdateAt = time.Unix(0, me.PostUpdateAt*int64(1000*1000)).Format(time.RFC3339) + } + + return []string{ + me.TeamName, + me.TeamDisplayName, + + me.ChannelName, + me.ChannelDisplayName, + + me.UserUsername, + me.UserEmail, + me.UserNickname, + + me.PostId, + time.Unix(0, me.PostCreateAt*int64(1000*1000)).Format(time.RFC3339), + postUpdateAt, + postDeleteAt, + + me.PostRootId, + me.PostParentId, + me.PostOriginalId, + me.PostMessage, + me.PostType, + me.PostProps, + me.PostHashtags, + me.PostFilenames, + } +} diff --git a/vendor/github.com/mattermost/platform/model/config.go b/vendor/github.com/mattermost/platform/model/config.go new file mode 100644 index 00000000..a8974359 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/config.go @@ -0,0 +1,554 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + CONN_SECURITY_NONE = "" + CONN_SECURITY_TLS = "TLS" + CONN_SECURITY_STARTTLS = "STARTTLS" + + IMAGE_DRIVER_LOCAL = "local" + IMAGE_DRIVER_S3 = "amazons3" + + DATABASE_DRIVER_MYSQL = "mysql" + DATABASE_DRIVER_POSTGRES = "postgres" + + SERVICE_GITLAB = "gitlab" + SERVICE_GOOGLE = "google" + + WEBSERVER_MODE_REGULAR = "regular" + WEBSERVER_MODE_GZIP = "gzip" + WEBSERVER_MODE_DISABLED = "disabled" + + GENERIC_NOTIFICATION = "generic" + FULL_NOTIFICATION = "full" +) + +type ServiceSettings struct { + ListenAddress string + MaximumLoginAttempts int + SegmentDeveloperKey string + GoogleDeveloperKey string + EnableOAuthServiceProvider bool + EnableIncomingWebhooks bool + EnableOutgoingWebhooks bool + EnableCommands *bool + EnableOnlyAdminIntegrations *bool + EnablePostUsernameOverride bool + EnablePostIconOverride bool + EnableTesting bool + EnableDeveloper *bool + EnableSecurityFixAlert *bool + EnableInsecureOutgoingConnections *bool + EnableMultifactorAuthentication *bool + AllowCorsFrom *string + SessionLengthWebInDays *int + SessionLengthMobileInDays *int + SessionLengthSSOInDays *int + SessionCacheInMinutes *int + WebsocketSecurePort *int + WebsocketPort *int + WebserverMode *string +} + +type SSOSettings struct { + Enable bool + Secret string + Id string + Scope string + AuthEndpoint string + TokenEndpoint string + UserApiEndpoint string +} + +type SqlSettings struct { + DriverName string + DataSource string + DataSourceReplicas []string + MaxIdleConns int + MaxOpenConns int + Trace bool + AtRestEncryptKey string +} + +type LogSettings struct { + EnableConsole bool + ConsoleLevel string + EnableFile bool + FileLevel string + FileFormat string + FileLocation string +} + +type FileSettings struct { + DriverName string + Directory string + EnablePublicLink bool + PublicLinkSalt string + ThumbnailWidth int + ThumbnailHeight int + PreviewWidth int + PreviewHeight int + ProfileWidth int + ProfileHeight int + InitialFont string + AmazonS3AccessKeyId string + AmazonS3SecretAccessKey string + AmazonS3Bucket string + AmazonS3Region string + AmazonS3Endpoint string + AmazonS3BucketEndpoint string + AmazonS3LocationConstraint *bool + AmazonS3LowercaseBucket *bool +} + +type EmailSettings struct { + EnableSignUpWithEmail bool + EnableSignInWithEmail *bool + EnableSignInWithUsername *bool + SendEmailNotifications bool + RequireEmailVerification bool + FeedbackName string + FeedbackEmail string + SMTPUsername string + SMTPPassword string + SMTPServer string + SMTPPort string + ConnectionSecurity string + InviteSalt string + PasswordResetSalt string + SendPushNotifications *bool + PushNotificationServer *string + PushNotificationContents *string +} + +type RateLimitSettings struct { + EnableRateLimiter bool + PerSec int + MemoryStoreSize int + VaryByRemoteAddr bool + VaryByHeader string +} + +type PrivacySettings struct { + ShowEmailAddress bool + ShowFullName bool +} + +type SupportSettings struct { + TermsOfServiceLink *string + PrivacyPolicyLink *string + AboutLink *string + HelpLink *string + ReportAProblemLink *string + SupportEmail *string +} + +type TeamSettings struct { + SiteName string + MaxUsersPerTeam int + EnableTeamCreation bool + EnableUserCreation bool + RestrictCreationToDomains string + RestrictTeamNames *bool + EnableTeamListing *bool +} + +type LdapSettings struct { + // Basic + Enable *bool + LdapServer *string + LdapPort *int + ConnectionSecurity *string + BaseDN *string + BindUsername *string + BindPassword *string + + // Filtering + UserFilter *string + + // User Mapping + FirstNameAttribute *string + LastNameAttribute *string + EmailAttribute *string + UsernameAttribute *string + IdAttribute *string + + // Advanced + SkipCertificateVerification *bool + QueryTimeout *int +} + +type ComplianceSettings struct { + Enable *bool + Directory *string + EnableDaily *bool +} + +type Config struct { + ServiceSettings ServiceSettings + TeamSettings TeamSettings + SqlSettings SqlSettings + LogSettings LogSettings + FileSettings FileSettings + EmailSettings EmailSettings + RateLimitSettings RateLimitSettings + PrivacySettings PrivacySettings + SupportSettings SupportSettings + GitLabSettings SSOSettings + GoogleSettings SSOSettings + LdapSettings LdapSettings + ComplianceSettings ComplianceSettings +} + +func (o *Config) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func (o *Config) GetSSOService(service string) *SSOSettings { + switch service { + case SERVICE_GITLAB: + return &o.GitLabSettings + case SERVICE_GOOGLE: + return &o.GoogleSettings + } + + return nil +} + +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 + } +} + +func (o *Config) SetDefaults() { + + if len(o.SqlSettings.AtRestEncryptKey) == 0 { + o.SqlSettings.AtRestEncryptKey = NewRandomString(32) + } + + if len(o.FileSettings.PublicLinkSalt) == 0 { + o.FileSettings.PublicLinkSalt = NewRandomString(32) + } + + if o.FileSettings.AmazonS3LocationConstraint == nil { + o.FileSettings.AmazonS3LocationConstraint = new(bool) + *o.FileSettings.AmazonS3LocationConstraint = false + } + + if o.FileSettings.AmazonS3LowercaseBucket == nil { + o.FileSettings.AmazonS3LowercaseBucket = new(bool) + *o.FileSettings.AmazonS3LowercaseBucket = false + } + + if len(o.EmailSettings.InviteSalt) == 0 { + o.EmailSettings.InviteSalt = NewRandomString(32) + } + + if len(o.EmailSettings.PasswordResetSalt) == 0 { + o.EmailSettings.PasswordResetSalt = NewRandomString(32) + } + + if o.ServiceSettings.EnableDeveloper == nil { + o.ServiceSettings.EnableDeveloper = new(bool) + *o.ServiceSettings.EnableDeveloper = false + } + + if o.ServiceSettings.EnableSecurityFixAlert == nil { + o.ServiceSettings.EnableSecurityFixAlert = new(bool) + *o.ServiceSettings.EnableSecurityFixAlert = true + } + + if o.ServiceSettings.EnableInsecureOutgoingConnections == nil { + o.ServiceSettings.EnableInsecureOutgoingConnections = new(bool) + *o.ServiceSettings.EnableInsecureOutgoingConnections = false + } + + if o.ServiceSettings.EnableMultifactorAuthentication == nil { + o.ServiceSettings.EnableMultifactorAuthentication = new(bool) + *o.ServiceSettings.EnableMultifactorAuthentication = false + } + + if o.TeamSettings.RestrictTeamNames == nil { + o.TeamSettings.RestrictTeamNames = new(bool) + *o.TeamSettings.RestrictTeamNames = true + } + + if o.TeamSettings.EnableTeamListing == nil { + o.TeamSettings.EnableTeamListing = new(bool) + *o.TeamSettings.EnableTeamListing = false + } + + if o.EmailSettings.EnableSignInWithEmail == nil { + o.EmailSettings.EnableSignInWithEmail = new(bool) + + if o.EmailSettings.EnableSignUpWithEmail == true { + *o.EmailSettings.EnableSignInWithEmail = true + } else { + *o.EmailSettings.EnableSignInWithEmail = false + } + } + + if o.EmailSettings.EnableSignInWithUsername == nil { + o.EmailSettings.EnableSignInWithUsername = new(bool) + *o.EmailSettings.EnableSignInWithUsername = false + } + + if o.EmailSettings.SendPushNotifications == nil { + o.EmailSettings.SendPushNotifications = new(bool) + *o.EmailSettings.SendPushNotifications = false + } + + if o.EmailSettings.PushNotificationServer == nil { + o.EmailSettings.PushNotificationServer = new(string) + *o.EmailSettings.PushNotificationServer = "" + } + + if o.EmailSettings.PushNotificationContents == nil { + o.EmailSettings.PushNotificationContents = new(string) + *o.EmailSettings.PushNotificationContents = GENERIC_NOTIFICATION + } + + if o.SupportSettings.TermsOfServiceLink == nil { + o.SupportSettings.TermsOfServiceLink = new(string) + *o.SupportSettings.TermsOfServiceLink = "/static/help/terms.html" + } + + if o.SupportSettings.PrivacyPolicyLink == nil { + o.SupportSettings.PrivacyPolicyLink = new(string) + *o.SupportSettings.PrivacyPolicyLink = "/static/help/privacy.html" + } + + if o.SupportSettings.AboutLink == nil { + o.SupportSettings.AboutLink = new(string) + *o.SupportSettings.AboutLink = "/static/help/about.html" + } + + if o.SupportSettings.HelpLink == nil { + o.SupportSettings.HelpLink = new(string) + *o.SupportSettings.HelpLink = "/static/help/help.html" + } + + if o.SupportSettings.ReportAProblemLink == nil { + o.SupportSettings.ReportAProblemLink = new(string) + *o.SupportSettings.ReportAProblemLink = "/static/help/report_problem.html" + } + + if o.SupportSettings.SupportEmail == nil { + o.SupportSettings.SupportEmail = new(string) + *o.SupportSettings.SupportEmail = "feedback@mattermost.com" + } + + if o.LdapSettings.LdapPort == nil { + o.LdapSettings.LdapPort = new(int) + *o.LdapSettings.LdapPort = 389 + } + + if o.LdapSettings.QueryTimeout == nil { + o.LdapSettings.QueryTimeout = new(int) + *o.LdapSettings.QueryTimeout = 60 + } + + if o.LdapSettings.Enable == nil { + o.LdapSettings.Enable = new(bool) + *o.LdapSettings.Enable = false + } + + if o.LdapSettings.UserFilter == nil { + o.LdapSettings.UserFilter = new(string) + *o.LdapSettings.UserFilter = "" + } + + if o.ServiceSettings.SessionLengthWebInDays == nil { + o.ServiceSettings.SessionLengthWebInDays = new(int) + *o.ServiceSettings.SessionLengthWebInDays = 30 + } + + if o.ServiceSettings.SessionLengthMobileInDays == nil { + o.ServiceSettings.SessionLengthMobileInDays = new(int) + *o.ServiceSettings.SessionLengthMobileInDays = 30 + } + + if o.ServiceSettings.SessionLengthSSOInDays == nil { + o.ServiceSettings.SessionLengthSSOInDays = new(int) + *o.ServiceSettings.SessionLengthSSOInDays = 30 + } + + if o.ServiceSettings.SessionCacheInMinutes == nil { + o.ServiceSettings.SessionCacheInMinutes = new(int) + *o.ServiceSettings.SessionCacheInMinutes = 10 + } + + if o.ServiceSettings.EnableCommands == nil { + o.ServiceSettings.EnableCommands = new(bool) + *o.ServiceSettings.EnableCommands = false + } + + if o.ServiceSettings.EnableOnlyAdminIntegrations == nil { + o.ServiceSettings.EnableOnlyAdminIntegrations = new(bool) + *o.ServiceSettings.EnableOnlyAdminIntegrations = true + } + + if o.ServiceSettings.WebsocketPort == nil { + o.ServiceSettings.WebsocketPort = new(int) + *o.ServiceSettings.WebsocketPort = 80 + } + + if o.ServiceSettings.WebsocketSecurePort == nil { + o.ServiceSettings.WebsocketSecurePort = new(int) + *o.ServiceSettings.WebsocketSecurePort = 443 + } + + if o.ServiceSettings.AllowCorsFrom == nil { + o.ServiceSettings.AllowCorsFrom = new(string) + *o.ServiceSettings.AllowCorsFrom = "" + } + + if o.ServiceSettings.WebserverMode == nil { + o.ServiceSettings.WebserverMode = new(string) + *o.ServiceSettings.WebserverMode = "regular" + } + + if o.ComplianceSettings.Enable == nil { + o.ComplianceSettings.Enable = new(bool) + *o.ComplianceSettings.Enable = false + } + + if o.ComplianceSettings.Directory == nil { + o.ComplianceSettings.Directory = new(string) + *o.ComplianceSettings.Directory = "./data/" + } + + if o.ComplianceSettings.EnableDaily == nil { + o.ComplianceSettings.EnableDaily = new(bool) + *o.ComplianceSettings.EnableDaily = false + } + + if o.LdapSettings.ConnectionSecurity == nil { + o.LdapSettings.ConnectionSecurity = new(string) + *o.LdapSettings.ConnectionSecurity = "" + } + + if o.LdapSettings.SkipCertificateVerification == nil { + o.LdapSettings.SkipCertificateVerification = new(bool) + *o.LdapSettings.SkipCertificateVerification = false + } +} + +func (o *Config) IsValid() *AppError { + + if o.ServiceSettings.MaximumLoginAttempts <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.login_attempts.app_error", nil, "") + } + + if len(o.ServiceSettings.ListenAddress) == 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.listen_address.app_error", nil, "") + } + + if o.TeamSettings.MaxUsersPerTeam <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.max_users.app_error", nil, "") + } + + if len(o.SqlSettings.AtRestEncryptKey) < 32 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.encrypt_sql.app_error", nil, "") + } + + if !(o.SqlSettings.DriverName == DATABASE_DRIVER_MYSQL || o.SqlSettings.DriverName == DATABASE_DRIVER_POSTGRES) { + return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_driver.app_error", nil, "") + } + + if o.SqlSettings.MaxIdleConns <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_idle.app_error", nil, "") + } + + if len(o.SqlSettings.DataSource) == 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_data_src.app_error", nil, "") + } + + if o.SqlSettings.MaxOpenConns <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.sql_max_conn.app_error", nil, "") + } + + if !(o.FileSettings.DriverName == IMAGE_DRIVER_LOCAL || o.FileSettings.DriverName == IMAGE_DRIVER_S3) { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_driver.app_error", nil, "") + } + + if o.FileSettings.PreviewHeight < 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_preview_height.app_error", nil, "") + } + + if o.FileSettings.PreviewWidth <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_preview_width.app_error", nil, "") + } + + if o.FileSettings.ProfileHeight <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_profile_height.app_error", nil, "") + } + + if o.FileSettings.ProfileWidth <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_profile_width.app_error", nil, "") + } + + if o.FileSettings.ThumbnailHeight <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_thumb_height.app_error", nil, "") + } + + if o.FileSettings.ThumbnailWidth <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_thumb_width.app_error", nil, "") + } + + if len(o.FileSettings.PublicLinkSalt) < 32 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.file_salt.app_error", nil, "") + } + + if !(o.EmailSettings.ConnectionSecurity == CONN_SECURITY_NONE || o.EmailSettings.ConnectionSecurity == CONN_SECURITY_TLS || o.EmailSettings.ConnectionSecurity == CONN_SECURITY_STARTTLS) { + return NewLocAppError("Config.IsValid", "model.config.is_valid.email_security.app_error", nil, "") + } + + if len(o.EmailSettings.InviteSalt) < 32 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.email_salt.app_error", nil, "") + } + + if len(o.EmailSettings.PasswordResetSalt) < 32 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.email_reset_salt.app_error", nil, "") + } + + if o.RateLimitSettings.MemoryStoreSize <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.rate_mem.app_error", nil, "") + } + + if o.RateLimitSettings.PerSec <= 0 { + return NewLocAppError("Config.IsValid", "model.config.is_valid.rate_sec.app_error", nil, "") + } + + if !(*o.LdapSettings.ConnectionSecurity == CONN_SECURITY_NONE || *o.LdapSettings.ConnectionSecurity == CONN_SECURITY_TLS || *o.LdapSettings.ConnectionSecurity == CONN_SECURITY_STARTTLS) { + return NewLocAppError("Config.IsValid", "model.config.is_valid.ldap_security.app_error", nil, "") + } + + return nil +} + +func (me *Config) GetSanitizeOptions() map[string]bool { + options := map[string]bool{} + options["fullname"] = me.PrivacySettings.ShowFullName + options["email"] = me.PrivacySettings.ShowEmailAddress + + return options +} diff --git a/vendor/github.com/mattermost/platform/model/file.go b/vendor/github.com/mattermost/platform/model/file.go new file mode 100644 index 00000000..b7806b3b --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/file.go @@ -0,0 +1,43 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + MAX_FILE_SIZE = 50000000 // 50 MB +) + +var ( + IMAGE_EXTENSIONS = [5]string{".jpg", ".jpeg", ".gif", ".bmp", ".png"} + IMAGE_MIME_TYPES = map[string]string{".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".bmp": "image/bmp", ".png": "image/png", ".tiff": "image/tiff"} +) + +type FileUploadResponse struct { + Filenames []string `json:"filenames"` + ClientIds []string `json:"client_ids"` +} + +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 + } +} + +func (o *FileUploadResponse) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} diff --git a/vendor/github.com/mattermost/platform/model/file_info.go b/vendor/github.com/mattermost/platform/model/file_info.go new file mode 100644 index 00000000..f785042b --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/file_info.go @@ -0,0 +1,77 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "bytes" + "encoding/json" + "image/gif" + "io" + "mime" + "path/filepath" +) + +type FileInfo struct { + Filename string `json:"filename"` + Size int `json:"size"` + Extension string `json:"extension"` + MimeType string `json:"mime_type"` + HasPreviewImage bool `json:"has_preview_image"` +} + +func GetInfoForBytes(filename string, data []byte) (*FileInfo, *AppError) { + size := len(data) + + var mimeType string + extension := filepath.Ext(filename) + isImage := IsFileExtImage(extension) + if isImage { + mimeType = GetImageMimeType(extension) + } else { + mimeType = mime.TypeByExtension(extension) + } + + if extension != "" && extension[0] == '.' { + // the client expects a file extension without the leading period + extension = extension[1:] + } + + hasPreviewImage := isImage + if mimeType == "image/gif" { + // just show the gif itself instead of a preview image for animated gifs + if gifImage, err := gif.DecodeAll(bytes.NewReader(data)); err != nil { + return nil, NewLocAppError("GetInfoForBytes", "model.file_info.get.gif.app_error", nil, "filename="+filename) + } else { + hasPreviewImage = len(gifImage.Image) == 1 + } + } + + return &FileInfo{ + Filename: filename, + Size: size, + Extension: extension, + MimeType: mimeType, + HasPreviewImage: hasPreviewImage, + }, nil +} + +func (info *FileInfo) ToJson() string { + b, err := json.Marshal(info) + if err != nil { + return "" + } else { + return string(b) + } +} + +func FileInfoFromJson(data io.Reader) *FileInfo { + decoder := json.NewDecoder(data) + + var info FileInfo + if err := decoder.Decode(&info); err != nil { + return nil + } else { + return &info + } +} diff --git a/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go b/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go new file mode 100644 index 00000000..3ca49997 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/gitlab/gitlab.go @@ -0,0 +1,107 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package oauthgitlab + +import ( + "encoding/json" + "github.com/mattermost/platform/einterfaces" + "github.com/mattermost/platform/model" + "io" + "strconv" + "strings" +) + +const ( + USER_AUTH_SERVICE_GITLAB = "gitlab" +) + +type GitLabProvider struct { +} + +type GitLabUser struct { + Id int64 `json:"id"` + Username string `json:"username"` + Login string `json:"login"` + Email string `json:"email"` + Name string `json:"name"` +} + +func init() { + provider := &GitLabProvider{} + einterfaces.RegisterOauthProvider(USER_AUTH_SERVICE_GITLAB, provider) +} + +func userFromGitLabUser(glu *GitLabUser) *model.User { + user := &model.User{} + username := glu.Username + if username == "" { + username = glu.Login + } + user.Username = model.CleanUsername(username) + splitName := strings.Split(glu.Name, " ") + if len(splitName) == 2 { + user.FirstName = splitName[0] + user.LastName = splitName[1] + } else if len(splitName) >= 2 { + user.FirstName = splitName[0] + user.LastName = strings.Join(splitName[1:], " ") + } else { + user.FirstName = glu.Name + } + user.Email = glu.Email + user.AuthData = strconv.FormatInt(glu.Id, 10) + user.AuthService = USER_AUTH_SERVICE_GITLAB + + return user +} + +func gitLabUserFromJson(data io.Reader) *GitLabUser { + decoder := json.NewDecoder(data) + var glu GitLabUser + err := decoder.Decode(&glu) + if err == nil { + return &glu + } else { + return nil + } +} + +func (glu *GitLabUser) IsValid() bool { + if glu.Id == 0 { + return false + } + + if len(glu.Email) == 0 { + return false + } + + return true +} + +func (glu *GitLabUser) getAuthData() string { + return strconv.FormatInt(glu.Id, 10) +} + +func (m *GitLabProvider) GetIdentifier() string { + return USER_AUTH_SERVICE_GITLAB +} + +func (m *GitLabProvider) GetUserFromJson(data io.Reader) *model.User { + glu := gitLabUserFromJson(data) + if glu.IsValid() { + return userFromGitLabUser(glu) + } + + return &model.User{} +} + +func (m *GitLabProvider) GetAuthDataFromJson(data io.Reader) string { + glu := gitLabUserFromJson(data) + + if glu.IsValid() { + return glu.getAuthData() + } + + return "" +} diff --git a/vendor/github.com/mattermost/platform/model/incoming_webhook.go b/vendor/github.com/mattermost/platform/model/incoming_webhook.go new file mode 100644 index 00000000..0763b443 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/incoming_webhook.go @@ -0,0 +1,137 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + DEFAULT_WEBHOOK_USERNAME = "webhook" + DEFAULT_WEBHOOK_ICON = "/static/images/webhook_icon.jpg" +) + +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"` +} + +type IncomingWebhookRequest struct { + Text string `json:"text"` + Username string `json:"username"` + IconURL string `json:"icon_url"` + ChannelName string `json:"channel"` + Props StringInterface `json:"props"` + Attachments interface{} `json:"attachments"` + Type string `json:"type"` +} + +func (o *IncomingWebhook) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} + +func IncomingWebhookListToJson(l []*IncomingWebhook) string { + b, err := json.Marshal(l) + if err != nil { + return "" + } else { + 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 + } +} + +func (o *IncomingWebhook) IsValid() *AppError { + + if len(o.Id) != 26 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.id.app_error", nil, "") + } + + if o.CreateAt == 0 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.create_at.app_error", nil, "id="+o.Id) + } + + if o.UpdateAt == 0 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.update_at.app_error", nil, "id="+o.Id) + } + + if len(o.UserId) != 26 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.user_id.app_error", nil, "") + } + + if len(o.ChannelId) != 26 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.channel_id.app_error", nil, "") + } + + if len(o.TeamId) != 26 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.team_id.app_error", nil, "") + } + + if len(o.DisplayName) > 64 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.display_name.app_error", nil, "") + } + + if len(o.Description) > 128 { + return NewLocAppError("IncomingWebhook.IsValid", "model.incoming_hook.description.app_error", nil, "") + } + + return nil +} + +func (o *IncomingWebhook) PreSave() { + if o.Id == "" { + o.Id = NewId() + } + + o.CreateAt = GetMillis() + o.UpdateAt = o.CreateAt +} + +func (o *IncomingWebhook) PreUpdate() { + o.UpdateAt = GetMillis() +} + +func IncomingWebhookRequestFromJson(data io.Reader) *IncomingWebhookRequest { + decoder := json.NewDecoder(data) + var o IncomingWebhookRequest + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/ldap.go b/vendor/github.com/mattermost/platform/model/ldap.go new file mode 100644 index 00000000..5fde06a6 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/ldap.go @@ -0,0 +1,8 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +const ( + USER_AUTH_SERVICE_LDAP = "ldap" +) diff --git a/vendor/github.com/mattermost/platform/model/license.go b/vendor/github.com/mattermost/platform/model/license.go new file mode 100644 index 00000000..cab22a68 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/license.go @@ -0,0 +1,123 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type LicenseRecord struct { + Id string `json:"id"` + CreateAt int64 `json:"create_at"` + Bytes string `json:"-"` +} + +type License struct { + Id string `json:"id"` + IssuedAt int64 `json:"issued_at"` + StartsAt int64 `json:"starts_at"` + ExpiresAt int64 `json:"expires_at"` + Customer *Customer `json:"customer"` + Features *Features `json:"features"` +} + +type Customer struct { + Id string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Company string `json:"company"` + PhoneNumber string `json:"phone_number"` +} + +type Features struct { + Users *int `json:"users"` + LDAP *bool `json:"ldap"` + MFA *bool `json:"mfa"` + GoogleSSO *bool `json:"google_sso"` + Compliance *bool `json:"compliance"` +} + +func (f *Features) SetDefaults() { + if f.Users == nil { + f.Users = new(int) + *f.Users = 0 + } + + if f.LDAP == nil { + f.LDAP = new(bool) + *f.LDAP = true + } + + if f.MFA == nil { + f.MFA = new(bool) + *f.MFA = true + } + + if f.GoogleSSO == nil { + f.GoogleSSO = new(bool) + *f.GoogleSSO = true + } + + if f.Compliance == nil { + f.Compliance = new(bool) + *f.Compliance = true + } +} + +func (l *License) IsExpired() bool { + now := GetMillis() + if l.ExpiresAt < now { + return true + } + return false +} + +func (l *License) IsStarted() bool { + now := GetMillis() + if l.StartsAt < now { + return true + } + return false +} + +func (l *License) ToJson() string { + b, err := json.Marshal(l) + if err != nil { + return "" + } else { + return string(b) + } +} + +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 + } +} + +func (lr *LicenseRecord) IsValid() *AppError { + if len(lr.Id) != 26 { + return NewLocAppError("LicenseRecord.IsValid", "model.license_record.is_valid.id.app_error", nil, "") + } + + if lr.CreateAt == 0 { + return NewLocAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "") + } + + if len(lr.Bytes) == 0 || len(lr.Bytes) > 10000 { + return NewLocAppError("LicenseRecord.IsValid", "model.license_record.is_valid.create_at.app_error", nil, "") + } + + return nil +} + +func (lr *LicenseRecord) PreSave() { + lr.CreateAt = GetMillis() +} diff --git a/vendor/github.com/mattermost/platform/model/message.go b/vendor/github.com/mattermost/platform/model/message.go new file mode 100644 index 00000000..cce0ec09 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/message.go @@ -0,0 +1,58 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + ACTION_TYPING = "typing" + ACTION_POSTED = "posted" + ACTION_POST_EDITED = "post_edited" + ACTION_POST_DELETED = "post_deleted" + ACTION_CHANNEL_VIEWED = "channel_viewed" + ACTION_NEW_USER = "new_user" + ACTION_USER_ADDED = "user_added" + ACTION_USER_REMOVED = "user_removed" + ACTION_PREFERENCE_CHANGED = "preference_changed" + ACTION_EPHEMERAL_MESSAGE = "ephemeral_message" +) + +type Message struct { + TeamId string `json:"team_id"` + ChannelId string `json:"channel_id"` + UserId string `json:"user_id"` + Action string `json:"action"` + Props map[string]string `json:"props"` +} + +func (m *Message) Add(key string, value string) { + m.Props[key] = value +} + +func NewMessage(teamId string, channelId string, userId string, action string) *Message { + return &Message{TeamId: teamId, ChannelId: channelId, UserId: userId, Action: action, Props: make(map[string]string)} +} + +func (o *Message) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func MessageFromJson(data io.Reader) *Message { + decoder := json.NewDecoder(data) + var o Message + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/vendor/github.com/mattermost/platform/model/oauth.go b/vendor/github.com/mattermost/platform/model/oauth.go new file mode 100644 index 00000000..c54df107 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/oauth.go @@ -0,0 +1,159 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "fmt" + "io" + "unicode/utf8" +) + +const ( + OAUTH_ACTION_SIGNUP = "signup" + OAUTH_ACTION_LOGIN = "login" + OAUTH_ACTION_EMAIL_TO_SSO = "email_to_sso" + OAUTH_ACTION_SSO_TO_EMAIL = "sso_to_email" +) + +type OAuthApp struct { + Id string `json:"id"` + CreatorId string `json:"creator_id"` + CreateAt int64 `json:"create_at"` + UpdateAt int64 `json:"update_at"` + ClientSecret string `json:"client_secret"` + Name string `json:"name"` + Description string `json:"description"` + CallbackUrls StringArray `json:"callback_urls"` + Homepage string `json:"homepage"` +} + +// IsValid validates the app and returns an error if it isn't configured +// correctly. +func (a *OAuthApp) IsValid() *AppError { + + if len(a.Id) != 26 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.app_id.app_error", nil, "") + } + + if a.CreateAt == 0 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.create_at.app_error", nil, "app_id="+a.Id) + } + + if a.UpdateAt == 0 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.update_at.app_error", nil, "app_id="+a.Id) + } + + if len(a.CreatorId) != 26 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.creator_id.app_error", nil, "app_id="+a.Id) + } + + if len(a.ClientSecret) == 0 || len(a.ClientSecret) > 128 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.client_secret.app_error", nil, "app_id="+a.Id) + } + + if len(a.Name) == 0 || len(a.Name) > 64 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.name.app_error", nil, "app_id="+a.Id) + } + + if len(a.CallbackUrls) == 0 || len(fmt.Sprintf("%s", a.CallbackUrls)) > 1024 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.callback.app_error", nil, "app_id="+a.Id) + } + + if len(a.Homepage) == 0 || len(a.Homepage) > 256 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.homepage.app_error", nil, "app_id="+a.Id) + } + + if utf8.RuneCountInString(a.Description) > 512 { + return NewLocAppError("OAuthApp.IsValid", "model.oauth.is_valid.description.app_error", nil, "app_id="+a.Id) + } + + return nil +} + +// PreSave will set the Id and ClientSecret if missing. It will also fill +// in the CreateAt, UpdateAt times. It should be run before saving the app to the db. +func (a *OAuthApp) PreSave() { + if a.Id == "" { + a.Id = NewId() + } + + if a.ClientSecret == "" { + a.ClientSecret = NewId() + } + + a.CreateAt = GetMillis() + a.UpdateAt = a.CreateAt + + if len(a.ClientSecret) > 0 { + a.ClientSecret = HashPassword(a.ClientSecret) + } +} + +// PreUpdate should be run before updating the app in the db. +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) + } +} + +// Generate a valid strong etag so the browser can cache the results +func (a *OAuthApp) Etag() string { + return Etag(a.Id, a.UpdateAt) +} + +// Remove any private data from the app object +func (a *OAuthApp) Sanitize() { + a.ClientSecret = "" +} + +func (a *OAuthApp) IsValidRedirectURL(url string) bool { + for _, u := range a.CallbackUrls { + if u == url { + return true + } + } + + 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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/outgoing_webhook.go b/vendor/github.com/mattermost/platform/model/outgoing_webhook.go new file mode 100644 index 00000000..e13de908 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/outgoing_webhook.go @@ -0,0 +1,151 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "fmt" + "io" +) + +type OutgoingWebhook struct { + Id string `json:"id"` + Token string `json:"token"` + CreateAt int64 `json:"create_at"` + UpdateAt int64 `json:"update_at"` + DeleteAt int64 `json:"delete_at"` + CreatorId string `json:"creator_id"` + ChannelId string `json:"channel_id"` + TeamId string `json:"team_id"` + TriggerWords StringArray `json:"trigger_words"` + CallbackURLs StringArray `json:"callback_urls"` + DisplayName string `json:"display_name"` + Description string `json:"description"` +} + +func (o *OutgoingWebhook) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} + +func OutgoingWebhookListToJson(l []*OutgoingWebhook) string { + b, err := json.Marshal(l) + if err != nil { + return "" + } else { + 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 + } +} + +func (o *OutgoingWebhook) IsValid() *AppError { + + if len(o.Id) != 26 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.id.app_error", nil, "") + } + + if len(o.Token) != 26 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.token.app_error", nil, "") + } + + if o.CreateAt == 0 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.create_at.app_error", nil, "id="+o.Id) + } + + if o.UpdateAt == 0 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.update_at.app_error", nil, "id="+o.Id) + } + + if len(o.CreatorId) != 26 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.user_id.app_error", nil, "") + } + + if len(o.ChannelId) != 0 && len(o.ChannelId) != 26 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.channel_id.app_error", nil, "") + } + + if len(o.TeamId) != 26 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.team_id.app_error", nil, "") + } + + if len(fmt.Sprintf("%s", o.TriggerWords)) > 1024 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.words.app_error", nil, "") + } + + if len(o.CallbackURLs) == 0 || len(fmt.Sprintf("%s", o.CallbackURLs)) > 1024 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.callback.app_error", nil, "") + } + + for _, callback := range o.CallbackURLs { + if !IsValidHttpUrl(callback) { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.url.app_error", nil, "") + } + } + + if len(o.DisplayName) > 64 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.display_name.app_error", nil, "") + } + + if len(o.Description) > 128 { + return NewLocAppError("OutgoingWebhook.IsValid", "model.outgoing_hook.is_valid.description.app_error", nil, "") + } + + return nil +} + +func (o *OutgoingWebhook) PreSave() { + if o.Id == "" { + o.Id = NewId() + } + + if o.Token == "" { + o.Token = NewId() + } + + o.CreateAt = GetMillis() + o.UpdateAt = o.CreateAt +} + +func (o *OutgoingWebhook) PreUpdate() { + o.UpdateAt = GetMillis() +} + +func (o *OutgoingWebhook) HasTriggerWord(word string) bool { + if len(o.TriggerWords) == 0 || len(word) == 0 { + return false + } + + for _, trigger := range o.TriggerWords { + if trigger == word { + return true + } + } + + return false +} diff --git a/vendor/github.com/mattermost/platform/model/post.go b/vendor/github.com/mattermost/platform/model/post.go new file mode 100644 index 00000000..8a451831 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/post.go @@ -0,0 +1,169 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "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" + POST_HEADER_CHANGE = "system_header_change" + POST_EPHEMERAL = "system_ephemeral" +) + +type Post 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"` + 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"` + PendingPostId string `json:"pending_post_id" db:"-"` +} + +func (o *Post) ToJson() string { + b, err := json.Marshal(o) + 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 NewLocAppError("Post.IsValid", "model.post.is_valid.id.app_error", nil, "") + } + + if o.CreateAt == 0 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.create_at.app_error", nil, "id="+o.Id) + } + + if o.UpdateAt == 0 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.update_at.app_error", nil, "id="+o.Id) + } + + if len(o.UserId) != 26 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.user_id.app_error", nil, "") + } + + if len(o.ChannelId) != 26 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.channel_id.app_error", nil, "") + } + + if !(len(o.RootId) == 26 || len(o.RootId) == 0) { + return NewLocAppError("Post.IsValid", "model.post.is_valid.root_id.app_error", nil, "") + } + + if !(len(o.ParentId) == 26 || len(o.ParentId) == 0) { + return NewLocAppError("Post.IsValid", "model.post.is_valid.parent_id.app_error", nil, "") + } + + if len(o.ParentId) == 26 && len(o.RootId) == 0 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.root_parent.app_error", nil, "") + } + + if !(len(o.OriginalId) == 26 || len(o.OriginalId) == 0) { + return NewLocAppError("Post.IsValid", "model.post.is_valid.original_id.app_error", nil, "") + } + + if utf8.RuneCountInString(o.Message) > 4000 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.msg.app_error", nil, "id="+o.Id) + } + + if utf8.RuneCountInString(o.Hashtags) > 1000 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.hashtags.app_error", nil, "id="+o.Id) + } + + // should be removed once more message types are supported + if !(o.Type == POST_DEFAULT || o.Type == POST_JOIN_LEAVE || o.Type == POST_SLACK_ATTACHMENT || o.Type == POST_HEADER_CHANGE) { + return NewLocAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type) + } + + if utf8.RuneCountInString(ArrayToJson(o.Filenames)) > 4000 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id) + } + + if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > 8000 { + return NewLocAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id) + } + + return nil +} + +func (o *Post) PreSave() { + if o.Id == "" { + o.Id = NewId() + } + + o.OriginalId = "" + + if o.CreateAt == 0 { + o.CreateAt = GetMillis() + } + + o.UpdateAt = o.CreateAt + + if o.Props == nil { + o.Props = make(map[string]interface{}) + } + + if o.Filenames == nil { + o.Filenames = []string{} + } +} + +func (o *Post) MakeNonNil() { + if o.Props == nil { + o.Props = make(map[string]interface{}) + } + if o.Filenames == nil { + o.Filenames = []string{} + } +} + +func (o *Post) AddProp(key string, value interface{}) { + + o.MakeNonNil() + + o.Props[key] = value +} + +func (o *Post) PreExport() { +} + +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 +} diff --git a/vendor/github.com/mattermost/platform/model/post_list.go b/vendor/github.com/mattermost/platform/model/post_list.go new file mode 100644 index 00000000..4c0f5408 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/post_list.go @@ -0,0 +1,100 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type PostList struct { + Order []string `json:"order"` + Posts map[string]*Post `json:"posts"` +} + +func (o *PostList) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func (o *PostList) MakeNonNil() { + if o.Order == nil { + o.Order = make([]string, 0) + } + + if o.Posts == nil { + o.Posts = make(map[string]*Post) + } + + for _, v := range o.Posts { + v.MakeNonNil() + } +} + +func (o *PostList) AddOrder(id string) { + + if o.Order == nil { + o.Order = make([]string, 0, 128) + } + + o.Order = append(o.Order, id) +} + +func (o *PostList) AddPost(post *Post) { + + if o.Posts == nil { + o.Posts = make(map[string]*Post) + } + + o.Posts[post.Id] = post +} + +func (o *PostList) Extend(other *PostList) { + for _, postId := range other.Order { + if _, ok := o.Posts[postId]; !ok { + o.AddPost(other.Posts[postId]) + o.AddOrder(postId) + } + } +} + +func (o *PostList) Etag() string { + + id := "0" + var t int64 = 0 + + for _, v := range o.Posts { + if v.UpdateAt > t { + t = v.UpdateAt + id = v.Id + } + } + + return Etag(id, t) +} + +func (o *PostList) IsChannelId(channelId string) bool { + for _, v := range o.Posts { + if v.ChannelId != channelId { + return false + } + } + + return true +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/preference.go b/vendor/github.com/mattermost/platform/model/preference.go new file mode 100644 index 00000000..b2ec9310 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/preference.go @@ -0,0 +1,66 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" + "unicode/utf8" +) + +const ( + PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show" + PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step" + PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings" + + PREFERENCE_CATEGORY_LAST = "last" + PREFERENCE_NAME_LAST_CHANNEL = "channel" +) + +type Preference struct { + UserId string `json:"user_id"` + Category string `json:"category"` + Name string `json:"name"` + Value string `json:"value"` +} + +func (o *Preference) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} + +func (o *Preference) IsValid() *AppError { + if len(o.UserId) != 26 { + return NewLocAppError("Preference.IsValid", "model.preference.is_valid.id.app_error", nil, "user_id="+o.UserId) + } + + if len(o.Category) == 0 || len(o.Category) > 32 { + return NewLocAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category) + } + + if len(o.Name) == 0 || len(o.Name) > 32 { + return NewLocAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name) + } + + if utf8.RuneCountInString(o.Value) > 128 { + return NewLocAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value) + } + + return nil +} diff --git a/vendor/github.com/mattermost/platform/model/preferences.go b/vendor/github.com/mattermost/platform/model/preferences.go new file mode 100644 index 00000000..f11b5fd8 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/preferences.go @@ -0,0 +1,31 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type Preferences []Preference + +func (o *Preferences) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func PreferencesFromJson(data io.Reader) (Preferences, error) { + decoder := json.NewDecoder(data) + var o Preferences + err := decoder.Decode(&o) + if err == nil { + return o, nil + } else { + return nil, err + } +} diff --git a/vendor/github.com/mattermost/platform/model/push_notification.go b/vendor/github.com/mattermost/platform/model/push_notification.go new file mode 100644 index 00000000..9196a44d --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/push_notification.go @@ -0,0 +1,49 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + PUSH_NOTIFY_APPLE = "apple" + PUSH_NOTIFY_ANDROID = "android" + + CATEGORY_DM = "DIRECT_MESSAGE" +) + +type PushNotification struct { + Platform string `json:"platform"` + ServerId string `json:"server_id"` + DeviceId string `json:"device_id"` + Category string `json:"category"` + Sound string `json:"sound"` + Message string `json:"message"` + Badge int `json:"badge"` + ContentAvailable int `json:"cont_ava"` + ChannelId string `json:"channel_id"` + ChannelName string `json:"channel_name"` +} + +func (me *PushNotification) ToJson() string { + b, err := json.Marshal(me) + if err != nil { + return "" + } else { + return string(b) + } +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/search_params.go b/vendor/github.com/mattermost/platform/model/search_params.go new file mode 100644 index 00000000..d3178269 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/search_params.go @@ -0,0 +1,170 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "strings" +) + +type SearchParams struct { + Terms string + IsHashtag bool + InChannels []string + FromUsers []string +} + +var searchFlags = [...]string{"from", "channel", "in"} + +func splitWordsNoQuotes(text string) []string { + words := []string{} + + for _, word := range strings.Fields(text) { + words = append(words, word) + } + + return words +} + +func splitWords(text string) []string { + words := []string{} + + foundQuote := false + location := 0 + for i, char := range text { + if char == '"' { + if foundQuote { + // Grab the quoted section + word := text[location : i+1] + words = append(words, word) + foundQuote = false + location = i + 1 + } else { + words = append(words, splitWordsNoQuotes(text[location:i])...) + foundQuote = true + location = i + } + } + } + + words = append(words, splitWordsNoQuotes(text[location:])...) + + return words +} + +func parseSearchFlags(input []string) ([]string, [][2]string) { + words := []string{} + flags := [][2]string{} + + skipNextWord := false + for i, word := range input { + if skipNextWord { + skipNextWord = false + continue + } + + isFlag := false + + if colon := strings.Index(word, ":"); colon != -1 { + flag := word[:colon] + value := word[colon+1:] + + for _, searchFlag := range searchFlags { + // check for case insensitive equality + if strings.EqualFold(flag, searchFlag) { + if value != "" { + flags = append(flags, [2]string{searchFlag, value}) + isFlag = true + } else if i < len(input)-1 { + flags = append(flags, [2]string{searchFlag, input[i+1]}) + skipNextWord = true + isFlag = true + } + + if isFlag { + break + } + } + } + } + + if !isFlag { + // trim off surrounding punctuation (note that we leave trailing asterisks to allow wildcards) + word = puncStart.ReplaceAllString(word, "") + word = puncEndWildcard.ReplaceAllString(word, "") + + // and remove extra pound #s + word = hashtagStart.ReplaceAllString(word, "#") + + if len(word) != 0 { + words = append(words, word) + } + } + } + + return words, flags +} + +func ParseSearchParams(text string) []*SearchParams { + words, flags := parseSearchFlags(splitWords(text)) + + hashtagTermList := []string{} + plainTermList := []string{} + + for _, word := range words { + if validHashtag.MatchString(word) { + hashtagTermList = append(hashtagTermList, word) + } else { + plainTermList = append(plainTermList, word) + } + } + + hashtagTerms := strings.Join(hashtagTermList, " ") + plainTerms := strings.Join(plainTermList, " ") + + inChannels := []string{} + fromUsers := []string{} + + for _, flagPair := range flags { + flag := flagPair[0] + value := flagPair[1] + + if flag == "in" || flag == "channel" { + inChannels = append(inChannels, value) + } else if flag == "from" { + fromUsers = append(fromUsers, value) + } + } + + paramsList := []*SearchParams{} + + if len(plainTerms) > 0 { + paramsList = append(paramsList, &SearchParams{ + Terms: plainTerms, + IsHashtag: false, + InChannels: inChannels, + FromUsers: fromUsers, + }) + } + + if len(hashtagTerms) > 0 { + paramsList = append(paramsList, &SearchParams{ + Terms: hashtagTerms, + IsHashtag: true, + InChannels: inChannels, + FromUsers: fromUsers, + }) + } + + // 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) { + paramsList = append(paramsList, &SearchParams{ + Terms: "", + IsHashtag: true, + InChannels: inChannels, + FromUsers: fromUsers, + }) + } + + return paramsList +} diff --git a/vendor/github.com/mattermost/platform/model/security_bulletin.go b/vendor/github.com/mattermost/platform/model/security_bulletin.go new file mode 100644 index 00000000..8d9be6d3 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/security_bulletin.go @@ -0,0 +1,55 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type SecurityBulletin struct { + Id string `json:"id"` + AppliesToVersion string `json:"applies_to_version"` +} + +type SecurityBulletins []SecurityBulletin + +func (me *SecurityBulletin) ToJson() string { + b, err := json.Marshal(me) + if err != nil { + return "" + } else { + 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 + } +} + +func (me SecurityBulletins) ToJson() string { + if b, err := json.Marshal(me); err != nil { + return "[]" + } else { + return string(b) + } +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/session.go b/vendor/github.com/mattermost/platform/model/session.go new file mode 100644 index 00000000..bf0d9531 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/session.go @@ -0,0 +1,115 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" + SESSION_CACHE_SIZE = 10000 + SESSION_PROP_PLATFORM = "platform" + SESSION_PROP_OS = "os" + SESSION_PROP_BROWSER = "browser" +) + +type Session struct { + Id string `json:"id"` + Token string `json:"token"` + CreateAt int64 `json:"create_at"` + ExpiresAt int64 `json:"expires_at"` + LastActivityAt int64 `json:"last_activity_at"` + UserId string `json:"user_id"` + TeamId string `json:"team_id"` + DeviceId string `json:"device_id"` + Roles string `json:"roles"` + IsOAuth bool `json:"is_oauth"` + Props StringMap `json:"props"` +} + +func (me *Session) ToJson() string { + b, err := json.Marshal(me) + if err != nil { + return "" + } else { + 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 + } +} + +func (me *Session) PreSave() { + if me.Id == "" { + me.Id = NewId() + } + + me.Token = NewId() + + me.CreateAt = GetMillis() + me.LastActivityAt = me.CreateAt + + if me.Props == nil { + me.Props = make(map[string]string) + } +} + +func (me *Session) Sanitize() { + me.Token = "" +} + +func (me *Session) IsExpired() bool { + + if me.ExpiresAt <= 0 { + return false + } + + if GetMillis() > me.ExpiresAt { + return true + } + + return false +} + +func (me *Session) SetExpireInDays(days int) { + me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days)) +} + +func (me *Session) AddProp(key string, value string) { + + if me.Props == nil { + me.Props = make(map[string]string) + } + + me.Props[key] = value +} + +func SessionsToJson(o []*Session) string { + if b, err := json.Marshal(o); err != nil { + return "[]" + } else { + return string(b) + } +} + +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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/suggest_command.go b/vendor/github.com/mattermost/platform/model/suggest_command.go new file mode 100644 index 00000000..7bc35369 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/suggest_command.go @@ -0,0 +1,34 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +type SuggestCommand struct { + Suggestion string `json:"suggestion"` + Description string `json:"description"` +} + +func (o *SuggestCommand) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} diff --git a/vendor/github.com/mattermost/platform/model/system.go b/vendor/github.com/mattermost/platform/model/system.go new file mode 100644 index 00000000..68d542c1 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/system.go @@ -0,0 +1,42 @@ +// Copyright (c) 2015 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/team.go b/vendor/github.com/mattermost/platform/model/team.go new file mode 100644 index 00000000..d95dea11 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/team.go @@ -0,0 +1,243 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "fmt" + "io" + "regexp" + "strings" + "unicode/utf8" +) + +const ( + TEAM_OPEN = "O" + TEAM_INVITE = "I" +) + +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"` + 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"` + AllowTeamListing bool `json:"allow_team_listing"` +} + +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 + } +} + +func (o *Invites) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func (o *Team) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + 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 + } +} + +func TeamMapToJson(u map[string]*Team) string { + b, err := json.Marshal(u) + if err != nil { + return "" + } else { + 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 + } +} + +func (o *Team) Etag() string { + return Etag(o.Id, o.UpdateAt) +} + +func (o *Team) IsValid(restrictTeamNames bool) *AppError { + + if len(o.Id) != 26 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "") + } + + if o.CreateAt == 0 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id) + } + + if o.UpdateAt == 0 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id) + } + + if len(o.Email) > 128 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id) + } + + if len(o.Email) > 0 && !IsValidEmail(o.Email) { + return NewLocAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id) + } + + if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > 64 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id) + } + + if len(o.Name) > 64 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id) + } + + if restrictTeamNames && IsReservedTeamName(o.Name) { + return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id) + } + + if !IsValidTeamName(o.Name) { + return NewLocAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id) + } + + if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) { + return NewLocAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id) + } + + if len(o.CompanyName) > 64 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id) + } + + if len(o.AllowedDomains) > 500 { + return NewLocAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id) + } + + return nil +} + +func (o *Team) PreSave() { + if o.Id == "" { + o.Id = NewId() + } + + o.CreateAt = GetMillis() + o.UpdateAt = o.CreateAt + + if len(o.InviteId) == 0 { + o.InviteId = NewId() + } +} + +func (o *Team) PreUpdate() { + o.UpdateAt = GetMillis() +} + +func IsReservedTeamName(s string) bool { + s = strings.ToLower(s) + + for _, value := range reservedName { + if strings.Index(s, value) == 0 { + return true + } + } + + return false +} + +func IsValidTeamName(s string) bool { + + if !IsValidAlphaNum(s, false) { + return false + } + + if len(s) <= 3 { + return false + } + + return true +} + +var validTeamNameCharacter = regexp.MustCompile(`^[a-z0-9-]$`) + +func CleanTeamName(s string) string { + s = strings.ToLower(strings.Replace(s, " ", "-", -1)) + + for _, value := range reservedName { + if strings.Index(s, value) == 0 { + s = strings.Replace(s, value, "", -1) + } + } + + s = strings.TrimSpace(s) + + for _, c := range s { + char := fmt.Sprintf("%c", c) + if !validTeamNameCharacter.MatchString(char) { + s = strings.Replace(s, char, "", -1) + } + } + + s = strings.Trim(s, "-") + + if !IsValidTeamName(s) { + s = NewId() + } + + return s +} + +func (o *Team) PreExport() { +} + +func (o *Team) Sanitize() { + o.Email = "" + o.AllowedDomains = "" +} + +func (o *Team) SanitizeForNotLoggedIn() { + o.Email = "" + o.AllowedDomains = "" + o.CompanyName = "" + if !o.AllowOpenInvite { + o.InviteId = "" + } +} diff --git a/vendor/github.com/mattermost/platform/model/team_signup.go b/vendor/github.com/mattermost/platform/model/team_signup.go new file mode 100644 index 00000000..e3642044 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/team_signup.go @@ -0,0 +1,40 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "fmt" + "io" +) + +type TeamSignup struct { + Team Team `json:"team"` + User User `json:"user"` + Invites []string `json:"invites"` + Data string `json:"data"` + Hash string `json:"hash"` +} + +func TeamSignupFromJson(data io.Reader) *TeamSignup { + decoder := json.NewDecoder(data) + var o TeamSignup + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + fmt.Println(err) + + return nil + } +} + +func (o *TeamSignup) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} diff --git a/vendor/github.com/mattermost/platform/model/user.go b/vendor/github.com/mattermost/platform/model/user.go new file mode 100644 index 00000000..173fe2b4 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/user.go @@ -0,0 +1,481 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "fmt" + "io" + "regexp" + "strings" + "unicode/utf8" + + "golang.org/x/crypto/bcrypt" +) + +const ( + ROLE_TEAM_ADMIN = "admin" + ROLE_SYSTEM_ADMIN = "system_admin" + USER_AWAY_TIMEOUT = 5 * 60 * 1000 // 5 minutes + USER_OFFLINE_TIMEOUT = 1 * 60 * 1000 // 1 minute + USER_OFFLINE = "offline" + USER_AWAY = "away" + USER_ONLINE = "online" + USER_NOTIFY_ALL = "all" + USER_NOTIFY_MENTION = "mention" + USER_NOTIFY_NONE = "none" + DEFAULT_LOCALE = "en" + USER_AUTH_SERVICE_EMAIL = "email" + USER_AUTH_SERVICE_USERNAME = "username" +) + +type User struct { + Id string `json:"id"` + CreateAt int64 `json:"create_at,omitempty"` + UpdateAt int64 `json:"update_at,omitempty"` + DeleteAt int64 `json:"delete_at"` + TeamId string `json:"team_id"` + 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"` + Roles string `json:"roles"` + LastActivityAt int64 `json:"last_activity_at,omitempty"` + LastPingAt int64 `json:"last_ping_at,omitempty"` + AllowMarketing bool `json:"allow_marketing,omitempty"` + Props StringMap `json:"props,omitempty"` + NotifyProps StringMap `json:"notify_props,omitempty"` + ThemeProps StringMap `json:"theme_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"` +} + +// IsValid validates the user and returns an error if it isn't configured +// correctly. +func (u *User) IsValid() *AppError { + + if len(u.Id) != 26 { + return NewLocAppError("User.IsValid", "model.user.is_valid.id.app_error", nil, "") + } + + if u.CreateAt == 0 { + return NewLocAppError("User.IsValid", "model.user.is_valid.create_at.app_error", nil, "user_id="+u.Id) + } + + if u.UpdateAt == 0 { + return NewLocAppError("User.IsValid", "model.user.is_valid.update_at.app_error", nil, "user_id="+u.Id) + } + + if len(u.TeamId) != 26 { + return NewLocAppError("User.IsValid", "model.user.is_valid.team_id.app_error", nil, "") + } + + if !IsValidUsername(u.Username) { + return NewLocAppError("User.IsValid", "model.user.is_valid.username.app_error", nil, "user_id="+u.Id) + } + + if len(u.Email) > 128 || len(u.Email) == 0 { + return NewLocAppError("User.IsValid", "model.user.is_valid.email.app_error", nil, "user_id="+u.Id) + } + + if utf8.RuneCountInString(u.Nickname) > 64 { + return NewLocAppError("User.IsValid", "model.user.is_valid.nickname.app_error", nil, "user_id="+u.Id) + } + + if utf8.RuneCountInString(u.FirstName) > 64 { + return NewLocAppError("User.IsValid", "model.user.is_valid.first_name.app_error", nil, "user_id="+u.Id) + } + + if utf8.RuneCountInString(u.LastName) > 64 { + return NewLocAppError("User.IsValid", "model.user.is_valid.last_name.app_error", nil, "user_id="+u.Id) + } + + if len(u.Password) > 128 { + return NewLocAppError("User.IsValid", "model.user.is_valid.pwd.app_error", nil, "user_id="+u.Id) + } + + if len(u.AuthData) > 128 { + return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data.app_error", nil, "user_id="+u.Id) + } + + if len(u.AuthData) > 0 && len(u.AuthService) == 0 { + return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_type.app_error", nil, "user_id="+u.Id) + } + + if len(u.Password) > 0 && len(u.AuthData) > 0 { + return NewLocAppError("User.IsValid", "model.user.is_valid.auth_data_pwd.app_error", nil, "user_id="+u.Id) + } + + if len(u.ThemeProps) > 2000 { + return NewLocAppError("User.IsValid", "model.user.is_valid.theme.app_error", nil, "user_id="+u.Id) + } + + return nil +} + +// 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. +func (u *User) PreSave() { + if u.Id == "" { + u.Id = NewId() + } + + if u.Username == "" { + u.Username = NewId() + } + + u.Username = strings.ToLower(u.Username) + u.Email = strings.ToLower(u.Email) + u.Locale = strings.ToLower(u.Locale) + + u.CreateAt = GetMillis() + u.UpdateAt = u.CreateAt + + u.LastPasswordUpdate = u.CreateAt + + u.MfaActive = false + + if u.Locale == "" { + u.Locale = DEFAULT_LOCALE + } + + if u.Props == nil { + u.Props = make(map[string]string) + } + + if u.NotifyProps == nil || len(u.NotifyProps) == 0 { + u.SetDefaultNotifications() + } + + if len(u.Password) > 0 { + u.Password = HashPassword(u.Password) + } +} + +// 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.Locale = strings.ToLower(u.Locale) + u.UpdateAt = GetMillis() + + if u.NotifyProps == nil || len(u.NotifyProps) == 0 { + u.SetDefaultNotifications() + } else if _, ok := u.NotifyProps["mention_keys"]; ok { + // Remove any blank mention keys + splitKeys := strings.Split(u.NotifyProps["mention_keys"], ",") + goodKeys := []string{} + for _, key := range splitKeys { + if len(key) > 0 { + goodKeys = append(goodKeys, strings.ToLower(key)) + } + } + u.NotifyProps["mention_keys"] = strings.Join(goodKeys, ",") + } +} + +func (u *User) SetDefaultNotifications() { + u.NotifyProps = make(map[string]string) + u.NotifyProps["email"] = "true" + u.NotifyProps["desktop"] = USER_NOTIFY_ALL + u.NotifyProps["desktop_sound"] = "true" + u.NotifyProps["mention_keys"] = u.Username + ",@" + u.Username + u.NotifyProps["first_name"] = "false" + u.NotifyProps["all"] = "true" + u.NotifyProps["channel"] = "true" + splitName := strings.Split(u.Nickname, " ") + if len(splitName) > 0 && splitName[0] != "" { + u.NotifyProps["first_name"] = "true" + u.NotifyProps["mention_keys"] += "," + splitName[0] + } +} + +// 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) + } +} + +// Generate a valid strong etag so the browser can cache the results +func (u *User) Etag() string { + return Etag(u.Id, u.UpdateAt) +} + +func (u *User) IsOffline() bool { + return (GetMillis()-u.LastPingAt) > USER_OFFLINE_TIMEOUT && (GetMillis()-u.LastActivityAt) > USER_OFFLINE_TIMEOUT +} + +func (u *User) IsAway() bool { + return (GetMillis() - u.LastActivityAt) > USER_AWAY_TIMEOUT +} + +// Remove any private data from the user object +func (u *User) Sanitize(options map[string]bool) { + u.Password = "" + u.AuthData = "" + + if len(options) != 0 && !options["email"] { + u.Email = "" + } + if len(options) != 0 && !options["fullname"] { + u.FirstName = "" + u.LastName = "" + } + if len(options) != 0 && !options["passwordupdate"] { + u.LastPasswordUpdate = 0 + } +} + +func (u *User) ClearNonProfileFields() { + u.UpdateAt = 0 + u.Password = "" + u.AuthData = "" + u.AuthService = "" + u.EmailVerified = false + u.LastPingAt = 0 + u.AllowMarketing = false + u.Props = StringMap{} + u.NotifyProps = StringMap{} + u.ThemeProps = StringMap{} + u.LastPasswordUpdate = 0 + u.LastPictureUpdate = 0 + u.FailedAttempts = 0 +} + +func (u *User) MakeNonNil() { + if u.Props == nil { + u.Props = make(map[string]string) + } + + if u.NotifyProps == nil { + u.NotifyProps = make(map[string]string) + } +} + +func (u *User) AddProp(key string, value string) { + u.MakeNonNil() + + u.Props[key] = value +} + +func (u *User) AddNotifyProp(key string, value string) { + u.MakeNonNil() + + u.NotifyProps[key] = value +} + +func (u *User) GetFullName() string { + if u.FirstName != "" && u.LastName != "" { + return u.FirstName + " " + u.LastName + } else if u.FirstName != "" { + return u.FirstName + } else if u.LastName != "" { + return u.LastName + } else { + return "" + } +} + +func (u *User) GetDisplayName() string { + if u.Nickname != "" { + return u.Nickname + } else if fullName := u.GetFullName(); fullName != "" { + return fullName + } else { + return u.Username + } +} + +func IsValidRoles(userRoles string) bool { + + roles := strings.Split(userRoles, " ") + + for _, r := range roles { + if !isValidRole(r) { + return false + } + } + + return true +} + +func isValidRole(role string) bool { + if role == "" { + return true + } + + if role == ROLE_TEAM_ADMIN { + return true + } + + if role == ROLE_SYSTEM_ADMIN { + return true + } + + return false +} + +// Make sure you acually want to use this function. In context.go there are functions to check permssions +// This function should not be used to check permissions. +func (u *User) IsInRole(inRole string) bool { + return IsInRole(u.Roles, inRole) +} + +// Make sure you acually want to use this function. In context.go there are functions to check permssions +// This function should not be used to check permissions. +func IsInRole(userRoles string, inRole string) bool { + roles := strings.Split(userRoles, " ") + + for _, r := range roles { + if r == inRole { + return true + } + + } + + return false +} + +func (u *User) IsSSOUser() bool { + if len(u.AuthData) != 0 && len(u.AuthService) != 0 && u.AuthService != USER_AUTH_SERVICE_LDAP { + return true + } + return false +} + +func (u *User) IsLDAPUser() bool { + if u.AuthService == USER_AUTH_SERVICE_LDAP { + return true + } + return false +} + +func (u *User) PreExport() { + u.Password = "" + u.AuthData = "" + u.LastActivityAt = 0 + u.LastPingAt = 0 + u.LastPasswordUpdate = 0 + u.LastPictureUpdate = 0 + u.FailedAttempts = 0 +} + +// 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 + } +} + +func UserMapToJson(u map[string]*User) string { + b, err := json.Marshal(u) + if err != nil { + return "" + } else { + 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 + } +} + +// HashPassword generates a hash using the bcrypt.GenerateFromPassword +func HashPassword(password string) string { + hash, err := bcrypt.GenerateFromPassword([]byte(password), 10) + if err != nil { + panic(err) + } + + return string(hash) +} + +// ComparePassword compares the hash +func ComparePassword(hash string, password string) bool { + + if len(password) == 0 { + return false + } + + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +var validUsernameChars = regexp.MustCompile(`^[a-z0-9\.\-_]+$`) + +var restrictedUsernames = []string{ + "all", + "channel", +} + +func IsValidUsername(s string) bool { + if len(s) == 0 || len(s) > 64 { + return false + } + + if !validUsernameChars.MatchString(s) { + return false + } + + for _, restrictedUsername := range restrictedUsernames { + if s == restrictedUsername { + return false + } + } + + return true +} + +func CleanUsername(s string) string { + s = strings.ToLower(strings.Replace(s, " ", "-", -1)) + + for _, value := range reservedName { + if s == value { + s = strings.Replace(s, value, "", -1) + } + } + + s = strings.TrimSpace(s) + + for _, c := range s { + char := fmt.Sprintf("%c", c) + if !validUsernameChars.MatchString(char) { + s = strings.Replace(s, char, "-", -1) + } + } + + s = strings.Trim(s, "-") + + if !IsValidUsername(s) { + s = "a" + NewId() + } + + return s +} diff --git a/vendor/github.com/mattermost/platform/model/utils.go b/vendor/github.com/mattermost/platform/model/utils.go new file mode 100644 index 00000000..1ce41bb3 --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/utils.go @@ -0,0 +1,381 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "bytes" + "crypto/rand" + "encoding/base32" + "encoding/json" + "fmt" + "io" + "net/mail" + "net/url" + "regexp" + "strings" + "time" + + goi18n "github.com/nicksnyder/go-i18n/i18n" + "github.com/pborman/uuid" +) + +type StringInterface map[string]interface{} +type StringMap map[string]string +type StringArray []string +type EncryptStringMap map[string]string + +type AppError struct { + Id string `json:"id"` + Message string `json:"message"` // Message to be display to the end user without debugging information + DetailedError string `json:"detailed_error"` // Internal error string to help the developer + RequestId string `json:"request_id"` // The RequestId that's also set in the header + StatusCode int `json:"status_code"` // The http status code + Where string `json:"-"` // The function where it happened in the form of Struct.Func + IsOAuth bool `json:"is_oauth"` // Whether the error is OAuth specific + params map[string]interface{} `json:"-"` +} + +func (er *AppError) Error() string { + return er.Where + ": " + er.Message + ", " + er.DetailedError +} + +func (er *AppError) Translate(T goi18n.TranslateFunc) { + if len(er.Message) == 0 { + if er.params == nil { + er.Message = T(er.Id) + } else { + er.Message = T(er.Id, er.params) + } + } +} + +func (er *AppError) ToJson() string { + b, err := json.Marshal(er) + if err != nil { + return "" + } else { + return string(b) + } +} + +// AppErrorFromJson will decode the input and return an AppError +func AppErrorFromJson(data io.Reader) *AppError { + decoder := json.NewDecoder(data) + var er AppError + err := decoder.Decode(&er) + if err == nil { + return &er + } else { + return NewLocAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, err.Error()) + } +} + +func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError { + ap := &AppError{} + ap.Id = id + ap.params = params + ap.Where = where + ap.DetailedError = details + ap.StatusCode = 500 + ap.IsOAuth = false + return ap +} + +var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769") + +// NewId is a globally unique identifier. It is a [A-Z0-9] string 26 +// characters long. It is a UUID version 4 Guid that is zbased32 encoded +// with the padding stripped off. +func NewId() string { + var b bytes.Buffer + encoder := base32.NewEncoder(encoding, &b) + encoder.Write(uuid.NewRandom()) + encoder.Close() + b.Truncate(26) // removes the '==' padding + return b.String() +} + +func NewRandomString(length int) string { + var b bytes.Buffer + str := make([]byte, length+8) + rand.Read(str) + encoder := base32.NewEncoder(encoding, &b) + encoder.Write(str) + encoder.Close() + b.Truncate(length) // removes the '==' padding + return b.String() +} + +// GetMillis is a convience method to get milliseconds since epoch. +func GetMillis() int64 { + return time.Now().UnixNano() / int64(time.Millisecond) +} + +// 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) + } +} + +// MapFromJson will decode the key/value pair map +func MapFromJson(data io.Reader) map[string]string { + decoder := json.NewDecoder(data) + + var objmap map[string]string + if err := decoder.Decode(&objmap); err != nil { + return make(map[string]string) + } else { + return objmap + } +} + +func ArrayToJson(objmap []string) string { + if b, err := json.Marshal(objmap); err != nil { + return "" + } else { + return string(b) + } +} + +func ArrayFromJson(data io.Reader) []string { + decoder := json.NewDecoder(data) + + var objmap []string + if err := decoder.Decode(&objmap); err != nil { + return make([]string, 0) + } else { + return objmap + } +} + +func StringInterfaceToJson(objmap map[string]interface{}) string { + if b, err := json.Marshal(objmap); err != nil { + return "" + } else { + return string(b) + } +} + +func StringInterfaceFromJson(data io.Reader) map[string]interface{} { + decoder := json.NewDecoder(data) + + var objmap map[string]interface{} + if err := decoder.Decode(&objmap); err != nil { + return make(map[string]interface{}) + } else { + return objmap + } +} + +func IsLower(s string) bool { + if strings.ToLower(s) == s { + return true + } + + return false +} + +func IsValidEmail(email string) bool { + + if !IsLower(email) { + return false + } + + if _, err := mail.ParseAddress(email); err == nil { + return true + } + + return false +} + +var reservedName = []string{ + "www", + "web", + "admin", + "support", + "notify", + "test", + "demo", + "mail", + "team", + "channel", + "internal", + "localhost", + "dockerhost", + "stag", + "post", + "cluster", + "api", + "oauth", +} + +var wwwStart = regexp.MustCompile(`^www`) +var betaStart = regexp.MustCompile(`^beta`) +var ciStart = regexp.MustCompile(`^ci`) + +func GetSubDomain(s string) (string, string) { + s = strings.Replace(s, "http://", "", 1) + s = strings.Replace(s, "https://", "", 1) + + match := wwwStart.MatchString(s) + if match { + return "", "" + } + + match = betaStart.MatchString(s) + if match { + return "", "" + } + + match = ciStart.MatchString(s) + if match { + return "", "" + } + + parts := strings.Split(s, ".") + + if len(parts) != 3 { + return "", "" + } + + return parts[0], parts[1] +} + +func IsValidChannelIdentifier(s string) bool { + + if !IsValidAlphaNum(s, true) { + return false + } + + if len(s) < 2 { + return false + } + + return true +} + +var validAlphaNumUnderscore = regexp.MustCompile(`^[a-z0-9]+([a-z\-\_0-9]+|(__)?)[a-z0-9]+$`) +var validAlphaNum = regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`) + +func IsValidAlphaNum(s string, allowUnderscores bool) bool { + var match bool + if allowUnderscores { + match = validAlphaNumUnderscore.MatchString(s) + } else { + match = validAlphaNum.MatchString(s) + } + + if !match { + return false + } + + return true +} + +func Etag(parts ...interface{}) string { + + etag := CurrentVersion + + for _, part := range parts { + etag += fmt.Sprintf(".%v", part) + } + + return etag +} + +var validHashtag = regexp.MustCompile(`^(#[A-Za-zäöüÄÖÜß]+[A-Za-z0-9äöüÄÖÜß_\-]*[A-Za-z0-9äöüÄÖÜß])$`) +var puncStart = regexp.MustCompile(`^[.,()&$!\?\[\]{}':;\\<>\-+=%^*|]+`) +var hashtagStart = regexp.MustCompile(`^#{2,}`) +var puncEnd = regexp.MustCompile(`[.,()&$#!\?\[\]{}':;\\<>\-+=%^*|]+$`) +var puncEndWildcard = regexp.MustCompile(`[.,()&$#!\?\[\]{}':;\\<>\-+=%^|]+$`) + +func ParseHashtags(text string) (string, string) { + words := strings.Fields(text) + + hashtagString := "" + plainString := "" + for _, word := range words { + // trim off surrounding punctuation + word = puncStart.ReplaceAllString(word, "") + word = puncEnd.ReplaceAllString(word, "") + + // and remove extra pound #s + word = hashtagStart.ReplaceAllString(word, "#") + + if validHashtag.MatchString(word) { + hashtagString += " " + word + } else { + plainString += " " + word + } + } + + if len(hashtagString) > 1000 { + hashtagString = hashtagString[:999] + lastSpace := strings.LastIndex(hashtagString, " ") + if lastSpace > -1 { + hashtagString = hashtagString[:lastSpace] + } else { + hashtagString = "" + } + } + + return strings.TrimSpace(hashtagString), strings.TrimSpace(plainString) +} + +func IsFileExtImage(ext string) bool { + ext = strings.ToLower(ext) + for _, imgExt := range IMAGE_EXTENSIONS { + if ext == imgExt { + return true + } + } + return false +} + +func GetImageMimeType(ext string) string { + ext = strings.ToLower(ext) + if len(IMAGE_MIME_TYPES[ext]) == 0 { + return "image" + } else { + return IMAGE_MIME_TYPES[ext] + } +} + +func ClearMentionTags(post string) string { + post = strings.Replace(post, "<mention>", "", -1) + post = strings.Replace(post, "</mention>", "", -1) + 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+_~\-\.%=&]*)?)?(?:#[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,})?)`) + +var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true} + +func IsValidHttpUrl(rawUrl string) bool { + if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 { + return false + } + + if _, err := url.ParseRequestURI(rawUrl); err != nil { + return false + } + + 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 +} diff --git a/vendor/github.com/mattermost/platform/model/version.go b/vendor/github.com/mattermost/platform/model/version.go new file mode 100644 index 00000000..b3950dcc --- /dev/null +++ b/vendor/github.com/mattermost/platform/model/version.go @@ -0,0 +1,127 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "fmt" + "strconv" + "strings" +) + +// This is a list of all the current viersions including any patches. +// It should be maitained in chronological order with most current +// release at the front of the list. +var versions = []string{ + "2.1.0", + "2.0.0", + "1.4.0", + "1.3.0", + "1.2.1", + "1.2.0", + "1.1.0", + "1.0.0", + "0.7.1", + "0.7.0", + "0.6.0", + "0.5.0", +} + +var CurrentVersion string = versions[0] +var BuildNumber string +var BuildDate string +var BuildHash string +var BuildEnterpriseReady string +var versionsWithoutHotFixes []string + +func init() { + versionsWithoutHotFixes = make([]string, 0, len(versions)) + seen := make(map[string]string) + + for _, version := range versions { + maj, min, _ := SplitVersion(version) + verStr := fmt.Sprintf("%v.%v.0", maj, min) + + if seen[verStr] == "" { + versionsWithoutHotFixes = append(versionsWithoutHotFixes, verStr) + seen[verStr] = verStr + } + } +} + +func SplitVersion(version string) (int64, int64, int64) { + parts := strings.Split(version, ".") + + major := int64(0) + minor := int64(0) + patch := int64(0) + + if len(parts) > 0 { + major, _ = strconv.ParseInt(parts[0], 10, 64) + } + + if len(parts) > 1 { + minor, _ = strconv.ParseInt(parts[1], 10, 64) + } + + if len(parts) > 2 { + patch, _ = strconv.ParseInt(parts[2], 10, 64) + } + + return major, minor, patch +} + +func GetPreviousVersion(version string) string { + verMajor, verMinor, _ := SplitVersion(version) + verStr := fmt.Sprintf("%v.%v.0", verMajor, verMinor) + + for index, v := range versionsWithoutHotFixes { + if v == verStr && len(versionsWithoutHotFixes) > index+1 { + return versionsWithoutHotFixes[index+1] + } + } + + return "" +} + +func IsOfficalBuild() bool { + return BuildNumber != "_BUILD_NUMBER_" +} + +func IsCurrentVersion(versionToCheck string) bool { + currentMajor, currentMinor, _ := SplitVersion(CurrentVersion) + toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck) + + if toCheckMajor == currentMajor && toCheckMinor == currentMinor { + return true + } else { + return false + } +} + +func IsPreviousVersionsSupported(versionToCheck string) bool { + toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck) + versionToCheckStr := fmt.Sprintf("%v.%v.0", toCheckMajor, toCheckMinor) + + // Current Supported + if versionsWithoutHotFixes[0] == versionToCheckStr { + return true + } + + // Current - 1 Supported + if versionsWithoutHotFixes[1] == versionToCheckStr { + return true + } + + // Current - 2 Supported + if versionsWithoutHotFixes[2] == versionToCheckStr { + return true + } + + // Current - 3 Supported + if versionsWithoutHotFixes[3] == versionToCheckStr { + return true + } + + return false +} |